diff --git a/Cargo.toml b/Cargo.toml index d12dd87975..a3f17d6f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ 'internal/backends/selector', 'internal/backends/testing', 'internal/backends/linuxkms', + 'internal/renderers/software', 'internal/renderers/skia', 'internal/renderers/femtovg', 'internal/common', @@ -132,6 +133,7 @@ i-slint-core = { version = "=1.15.0", path = "internal/core", default-features = i-slint-core-macros = { version = "=1.15.0", path = "internal/core-macros", default-features = false } i-slint-renderer-femtovg = { version = "=1.15.0", path = "internal/renderers/femtovg", default-features = false } i-slint-renderer-skia = { version = "=1.15.0", path = "internal/renderers/skia", default-features = false } +i-slint-renderer-software = { version = "=1.15.0", path = "internal/renderers/software", default-features = false } slint = { version = "=1.15.0", path = "api/rs/slint", default-features = false } slint-build = { version = "=1.15.0", path = "api/rs/build", default-features = false } slint-cpp = { version = "=1.15.0", path = "api/cpp", default-features = false } diff --git a/api/cpp/Cargo.toml b/api/cpp/Cargo.toml index 8f50f52a5a..7a9767102c 100644 --- a/api/cpp/Cargo.toml +++ b/api/cpp/Cargo.toml @@ -41,14 +41,19 @@ renderer-femtovg-wgpu = ["i-slint-backend-selector/renderer-femtovg-wgpu"] renderer-skia = ["i-slint-backend-selector/renderer-skia", "i-slint-renderer-skia", "raw-window-handle"] renderer-skia-opengl = ["i-slint-backend-selector/renderer-skia-opengl", "renderer-skia"] renderer-skia-vulkan = ["i-slint-backend-selector/renderer-skia-vulkan", "renderer-skia"] -renderer-software = ["i-slint-backend-selector/renderer-software"] +renderer-software = ["i-slint-backend-selector/renderer-software", "dep:i-slint-renderer-software"] gettext = ["i-slint-core/gettext-rs"] accessibility = ["i-slint-backend-selector/accessibility"] system-testing = ["i-slint-backend-selector/system-testing"] -std = ["i-slint-core/default", "i-slint-core/image-default-formats", "i-slint-backend-selector"] +std = [ + "i-slint-core/default", + "i-slint-core/image-default-formats", + "i-slint-backend-selector", + "i-slint-renderer-software?/std", +] freestanding = ["i-slint-core/libm", "i-slint-core/unsafe-single-threaded"] -experimental = ["i-slint-core/experimental"] +experimental = ["i-slint-renderer-software?/experimental"] default = ["std", "backend-winit", "renderer-femtovg", "backend-qt"] @@ -56,6 +61,7 @@ default = ["std", "backend-winit", "renderer-femtovg", "backend-qt"] i-slint-backend-selector = { workspace = true, optional = true } i-slint-backend-testing = { workspace = true, optional = true, features = ["ffi"] } i-slint-renderer-skia = { workspace = true, features = ["default", "x11", "wayland"], optional = true } +i-slint-renderer-software = { workspace = true, optional = true } i-slint-core = { workspace = true, features = ["ffi"] } slint-interpreter = { workspace = true, features = ["ffi", "compat-1-2"], optional = true } raw-window-handle = { version = "0.6", optional = true } diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index d1d4c44263..77c739f268 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -341,7 +341,6 @@ fn gen_corelib( "Rect", "SortOrder", "BitmapFont", - "PhysicalRegion", ] .iter() .chain(items.iter()) @@ -496,10 +495,12 @@ fn gen_corelib( "SharedPixelBuffer", "SharedImageBuffer", "StaticTextures", - "BorrowedOpenGLTextureOrigin" + "BorrowedOpenGLTextureOrigin", + "PhysicalRegion", + "PHYSICAL_REGION_MAX_SIZE", ], "slint_image_internal.h", - "#include \"slint_color.h\"\nnamespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }", + "#include \"slint_color.h\"\nnamespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; struct PhysicalPx; using namespace vtable; namespace types{ struct NineSliceImage{}; } }", ), ( vec!["Color", "slint_color_brighter", "slint_color_darker", @@ -582,6 +583,7 @@ fn gen_corelib( "ConicGradientBrush", "slint_conic_gradient_normalize_stops", "slint_conic_gradient_apply_rotation", + "PHYSICAL_REGION_MAX_SIZE", ] .into_iter() .chain(config.export.exclude.iter().map(|s| s.as_str())) @@ -622,6 +624,7 @@ fn gen_corelib( .with_src(crate_dir.join("input.rs")) .with_src(crate_dir.join("item_rendering.rs")) .with_src(crate_dir.join("window.rs")) + .with_src(crate_dir.join("../renderers/software/lib.rs")) .with_include("slint_enums_internal.h") .generate() .with_context(|| format!("Unable to generate bindings for {internal_header}"))? @@ -881,6 +884,7 @@ namespace slint::cbindgen_private { using slint::cbindgen_private::types::TexturePixelFormat; struct DrawTextureArgs; struct DrawRectangleArgs; + using types::PhysicalRegion; } ", ) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 8f1f0e015c..430dd5bb0f 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -602,7 +602,7 @@ public: auto rectangles() const { SharedVector rectangles; - slint_software_renderer_region_to_rects(&inner, &rectangles); + cbindgen_private::slint_software_renderer_region_to_rects(&inner, &rectangles); # if __cpp_lib_ranges >= 202110L // DR20 P2415R2 using std::ranges::owning_view; # else diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 298aaedcf4..f07f156f0d 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -359,12 +359,12 @@ mod software_renderer { type SoftwareRendererOpaque = *const c_void; use i_slint_core::SharedVector; use i_slint_core::graphics::{IntRect, Rgb8Pixel}; - use i_slint_core::software_renderer::{ + use i_slint_renderer_software::{ PhysicalRegion, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, }; #[cfg(feature = "experimental")] - use i_slint_core::software_renderer::{TargetPixelBuffer, TexturePixelFormat}; + use i_slint_renderer_software::{TargetPixelBuffer, TexturePixelFormat}; #[cfg(feature = "experimental")] type CppTargetPixelBufferUserData = *mut c_void; @@ -398,8 +398,8 @@ mod software_renderer { pub tiling_gap_y: u32, } #[cfg(feature = "experimental")] - impl From<&i_slint_core::software_renderer::DrawTextureArgs> for DrawTextureArgs { - fn from(from: &i_slint_core::software_renderer::DrawTextureArgs) -> Self { + impl From<&i_slint_renderer_software::DrawTextureArgs> for DrawTextureArgs { + fn from(from: &i_slint_renderer_software::DrawTextureArgs) -> Self { let source = from.source(); Self { image_data: source.data.as_ptr(), @@ -448,8 +448,8 @@ mod software_renderer { pub rotation: i32, } #[cfg(feature = "experimental")] - impl From<&i_slint_core::software_renderer::DrawRectangleArgs> for DrawRectangleArgs { - fn from(from: &i_slint_core::software_renderer::DrawRectangleArgs) -> Self { + impl From<&i_slint_renderer_software::DrawRectangleArgs> for DrawRectangleArgs { + fn from(from: &i_slint_renderer_software::DrawRectangleArgs) -> Self { Self { x: from.x, y: from.y, @@ -494,7 +494,7 @@ mod software_renderer { } #[cfg(feature = "experimental")] - impl TargetPixelBuffer + impl TargetPixelBuffer for CppTargetPixelBuffer { type TargetPixel = TargetPixel; @@ -524,7 +524,7 @@ mod software_renderer { /// Draw a rectangle specified by the DrawRectangleArgs. That rectangle must be clipped to the given region fn draw_rectangle( &mut self, - args: &i_slint_core::software_renderer::DrawRectangleArgs, + args: &i_slint_renderer_software::DrawRectangleArgs, clip: &PhysicalRegion, ) -> bool { let args = args.into(); @@ -533,7 +533,7 @@ mod software_renderer { fn draw_texture( &mut self, - texture: &i_slint_core::software_renderer::DrawTextureArgs, + texture: &i_slint_renderer_software::DrawTextureArgs, clip: &PhysicalRegion, ) -> bool { let texture = texture.into(); @@ -620,8 +620,8 @@ mod software_renderer { user_data: *mut core::ffi::c_void, } - impl - i_slint_core::software_renderer::LineBufferProvider for LineByLineProcessor + impl + i_slint_renderer_software::LineBufferProvider for LineByLineProcessor { type TargetPixel = TargetPixel; fn process_line( @@ -710,7 +710,7 @@ mod software_renderer { r: SoftwareRendererOpaque, rotation: i32, ) { - use i_slint_core::software_renderer::RenderingRotation; + use i_slint_renderer_software::RenderingRotation; let renderer = unsafe { &*(r as *const SoftwareRenderer) }; renderer.set_rendering_rotation(match rotation { 90 => RenderingRotation::Rotate90, diff --git a/api/rs/slint/Cargo.toml b/api/rs/slint/Cargo.toml index 6f838c4f16..f60c3b4809 100644 --- a/api/rs/slint/Cargo.toml +++ b/api/rs/slint/Cargo.toml @@ -31,7 +31,7 @@ default = ["std", "backend-default", "renderer-femtovg", "renderer-software", "a "compat-1-0" = ["compat-1-2", "renderer-software"] ## Enable use of the Rust standard library. -std = ["i-slint-core/std"] +std = ["i-slint-core/std", "i-slint-renderer-software?/std"] ## Enable the translations using [gettext](https://www.gnu.org/software/gettext/gettext) ## @@ -43,7 +43,7 @@ gettext = ["i-slint-core/gettext-rs"] ## This feature enables floating point arithmetic emulation using the [libm](https://crates.io/crates/libm) crate. Use this ## in MCU environments where the processor does not support floating point arithmetic. -libm = ["i-slint-core/libm"] +libm = ["i-slint-core/libm", "i-slint-renderer-software?/libm"] ## If enabled, calls of `debug()` in `.slint` files use to the [`log::debug!()`] macro ## of the [log](https://crates.io/crates/log) crate instead of just `println!()`. @@ -54,7 +54,7 @@ log = ["dep:log"] serde = ["i-slint-core/serde"] ## This feature enables the software renderer to pick up fonts from the operating system for text rendering. -software-renderer-systemfonts = ["renderer-software", "i-slint-core/software-renderer-systemfonts"] +software-renderer-systemfonts = ["renderer-software", "i-slint-renderer-software/systemfonts"] ## Slint uses internally some `thread_local` state. ## @@ -167,7 +167,7 @@ renderer-skia-opengl = ["i-slint-backend-selector/renderer-skia-opengl", "std"] renderer-skia-vulkan = ["i-slint-backend-selector/renderer-skia-vulkan", "std"] ## Render using the software renderer. -renderer-software = ["i-slint-backend-selector/renderer-software", "i-slint-core/software-renderer"] +renderer-software = ["i-slint-backend-selector/renderer-software", "dep:i-slint-renderer-software"] ## KMS with Vulkan or EGL and libinput on Linux are used to render the application in full screen mode, without any ## windowing system. Requires libseat. If you don't have libseat, select `backend-linuxkms-noseat` instead. (Experimental) @@ -260,6 +260,7 @@ slint-macros = { workspace = true } i-slint-backend-selector = { workspace = true } i-slint-core-macros = { workspace = true } i-slint-common = { workspace = true } +i-slint-renderer-software = { workspace = true, optional = true } slint-interpreter = { workspace = true, optional = true, default-features = false, features = ["display-diagnostics", "compat-1-2", "internal-live-preview"] } const-field-offset = { version = "0.1.2", path = "../../../helper_crates/const-field-offset" } diff --git a/api/rs/slint/lib.rs b/api/rs/slint/lib.rs index 235cf4e388..d9c2a1375b 100644 --- a/api/rs/slint/lib.rs +++ b/api/rs/slint/lib.rs @@ -425,6 +425,14 @@ pub mod platform { pub use i_slint_renderer_femtovg::FemtoVGOpenGLRenderer as FemtoVGRenderer; pub use i_slint_renderer_femtovg::opengl::OpenGLInterface; } + + #[cfg(feature = "renderer-software")] + /// This module contains the [`software_renderer::SoftwareRenderer`] and related types. + /// + /// It is only enabled when the `renderer-software` Slint feature is enabled. + pub mod software_renderer { + pub use i_slint_renderer_software::*; + } } #[i_slint_core_macros::slint_doc] diff --git a/examples/mcu-board-support/Cargo.toml b/examples/mcu-board-support/Cargo.toml index 516c6c37f8..08e11a28df 100644 --- a/examples/mcu-board-support/Cargo.toml +++ b/examples/mcu-board-support/Cargo.toml @@ -192,13 +192,14 @@ stm32u5g9j-dk2 = [ "cortex-m/critical-section-single-core", "gt911/defmt", "embassy-futures", - "i-slint-core/experimental", + "i-slint-renderer-software/experimental", ] [dependencies] slint = { version = "=1.15.0", path = "../../api/rs/slint", default-features = false, features = ["compat-1-2", "renderer-software"] } i-slint-core = { workspace = true } i-slint-core-macros = { version = "=1.15.0", path = "../../internal/core-macros" } +i-slint-renderer-software = { workspace = true, optional = true } derive_more = { workspace = true } embedded-graphics = { version = "0.8", optional = true } diff --git a/internal/backends/linuxkms/Cargo.toml b/internal/backends/linuxkms/Cargo.toml index 0bc9cb009b..ea06d59c86 100644 --- a/internal/backends/linuxkms/Cargo.toml +++ b/internal/backends/linuxkms/Cargo.toml @@ -20,10 +20,9 @@ renderer-skia = ["renderer-skia-opengl"] renderer-skia-vulkan = ["i-slint-renderer-skia/vulkan", "vulkano", "drm", "dep:memmap2"] renderer-skia-opengl = ["i-slint-renderer-skia/opengl", "drm", "gbm", "glutin", "raw-window-handle", "dep:memmap2"] renderer-femtovg = ["i-slint-renderer-femtovg/opengl", "drm", "gbm", "glutin", "raw-window-handle"] -renderer-software = ["i-slint-core/software-renderer-systemfonts", "drm", "dep:bytemuck", "dep:memmap2"] +renderer-software = ["i-slint-renderer-software/systemfonts", "drm", "dep:bytemuck", "dep:memmap2"] libseat = ["dep:libseat"] -#default = ["renderer-skia", "renderer-femtovg"] default = [] [dependencies] @@ -31,6 +30,7 @@ i-slint-core = { workspace = true, features = ["default", "image-decoders", "svg i-slint-common = { workspace = true, features = ["default"] } i-slint-renderer-skia = { workspace = true, features = ["default", "kms"], optional = true } i-slint-renderer-femtovg = { workspace = true, features = ["default"], optional = true } +i-slint-renderer-software = { workspace = true, optional = true } [target.'cfg(target_os = "linux")'.dependencies] input = { workspace = true, default-features = true } diff --git a/internal/backends/linuxkms/renderer/sw.rs b/internal/backends/linuxkms/renderer/sw.rs index 927e8f87fc..8b680e31e1 100644 --- a/internal/backends/linuxkms/renderer/sw.rs +++ b/internal/backends/linuxkms/renderer/sw.rs @@ -1,12 +1,12 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -//! Delegate the rendering to the [`i_slint_core::software_renderer::SoftwareRenderer`] +//! Delegate the rendering to the [`i_slint_renderer_software::SoftwareRenderer`] use i_slint_core::api::PhysicalSize as PhysicalWindowSize; use i_slint_core::platform::PlatformError; -pub use i_slint_core::software_renderer::SoftwareRenderer; -use i_slint_core::software_renderer::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel}; +pub use i_slint_renderer_software::SoftwareRenderer; +use i_slint_renderer_software::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel}; use std::sync::Arc; use crate::display::RenderingRotation; @@ -189,16 +189,16 @@ impl crate::fullscreenwindowadapter::FullscreenRenderer for SoftwareRendererAdap self.renderer.set_rendering_rotation(match rotation { RenderingRotation::NoRotation => { - i_slint_core::software_renderer::RenderingRotation::NoRotation + i_slint_renderer_software::RenderingRotation::NoRotation } RenderingRotation::Rotate90 => { - i_slint_core::software_renderer::RenderingRotation::Rotate90 + i_slint_renderer_software::RenderingRotation::Rotate90 } RenderingRotation::Rotate180 => { - i_slint_core::software_renderer::RenderingRotation::Rotate180 + i_slint_renderer_software::RenderingRotation::Rotate180 } RenderingRotation::Rotate270 => { - i_slint_core::software_renderer::RenderingRotation::Rotate270 + i_slint_renderer_software::RenderingRotation::Rotate270 } }); @@ -215,7 +215,7 @@ impl crate::fullscreenwindowadapter::FullscreenRenderer for SoftwareRendererAdap self.renderer.render(buffer, self.size.width as usize); } drm::buffer::DrmFourcc::Rgb565 => { - let buffer: &mut [i_slint_core::software_renderer::Rgb565Pixel] = + let buffer: &mut [i_slint_renderer_software::Rgb565Pixel] = bytemuck::cast_slice_mut(pixels.as_mut()); self.renderer.render(buffer, self.size.width as usize); } diff --git a/internal/backends/selector/Cargo.toml b/internal/backends/selector/Cargo.toml index 36f0a2aa7e..bc9efdc3b1 100644 --- a/internal/backends/selector/Cargo.toml +++ b/internal/backends/selector/Cargo.toml @@ -38,11 +38,7 @@ renderer-skia-vulkan = [ "i-slint-backend-linuxkms?/renderer-skia-vulkan", "i-slint-renderer-skia/vulkan", ] -renderer-software = [ - "i-slint-backend-winit?/renderer-software", - "i-slint-backend-linuxkms?/renderer-software", - "i-slint-core/software-renderer", -] +renderer-software = ["i-slint-backend-winit?/renderer-software", "i-slint-backend-linuxkms?/renderer-software"] rtti = ["i-slint-core/rtti", "i-slint-backend-qt?/rtti"] accessibility = ["i-slint-backend-winit?/accessibility"] diff --git a/internal/backends/winit/Cargo.toml b/internal/backends/winit/Cargo.toml index 5e16a89682..66804f60a4 100644 --- a/internal/backends/winit/Cargo.toml +++ b/internal/backends/winit/Cargo.toml @@ -45,13 +45,7 @@ renderer-femtovg-wgpu = ["i-slint-renderer-femtovg/wgpu", "dep:i-slint-renderer- renderer-skia = ["i-slint-renderer-skia"] renderer-skia-opengl = ["renderer-skia", "i-slint-renderer-skia/opengl"] renderer-skia-vulkan = ["renderer-skia", "i-slint-renderer-skia/vulkan"] -renderer-software = [ - "dep:softbuffer", - "dep:imgref", - "dep:rgb", - "i-slint-core/software-renderer-systemfonts", - "dep:bytemuck", -] +renderer-software = ["dep:softbuffer", "dep:imgref", "dep:rgb", "i-slint-renderer-software/systemfonts", "dep:bytemuck"] accessibility = ["dep:accesskit", "dep:accesskit_winit"] raw-window-handle-06 = ["i-slint-core/raw-window-handle-06"] unstable-wgpu-26 = [ @@ -90,6 +84,7 @@ i-slint-renderer-femtovg = { workspace = true, features = ["default"], optional i-slint-renderer-skia = { workspace = true, features = ["default"], optional = true } # For the software renderer +i-slint-renderer-software = { workspace = true, optional = true } softbuffer = { workspace = true, optional = true, default-features = false } imgref = { version = "1.6.1", optional = true } rgb = { version = "0.8.27", optional = true } diff --git a/internal/backends/winit/renderer/sw.rs b/internal/backends/winit/renderer/sw.rs index 3a1bf470f6..fa792b4e01 100644 --- a/internal/backends/winit/renderer/sw.rs +++ b/internal/backends/winit/renderer/sw.rs @@ -1,14 +1,14 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -//! Delegate the rendering to the [`i_slint_core::software_renderer::SoftwareRenderer`] +//! Delegate the rendering to the [`i_slint_renderer_software::SoftwareRenderer`] use core::num::NonZeroU32; use core::ops::DerefMut; use i_slint_core::graphics::Rgb8Pixel; use i_slint_core::platform::PlatformError; -pub use i_slint_core::software_renderer::SoftwareRenderer; -use i_slint_core::software_renderer::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel}; +pub use i_slint_renderer_software::SoftwareRenderer; +use i_slint_renderer_software::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel}; use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; @@ -122,10 +122,10 @@ impl super::WinitCompatibleRenderer for WinitSoftwareRenderer { // SLINT_LINE_BY_LINE is set and this is a debug mode where we also render in a Rgb565Pixel struct FrameBuffer<'a> { buffer: &'a mut [u32], - line: Vec, + line: Vec, } - impl i_slint_core::software_renderer::LineBufferProvider for FrameBuffer<'_> { - type TargetPixel = i_slint_core::software_renderer::Rgb565Pixel; + impl i_slint_renderer_software::LineBufferProvider for FrameBuffer<'_> { + type TargetPixel = i_slint_renderer_software::Rgb565Pixel; fn process_line( &mut self, line: usize, diff --git a/internal/core/Cargo.toml b/internal/core/Cargo.toml index eba8f0b734..c11d4c3f73 100644 --- a/internal/core/Cargo.toml +++ b/internal/core/Cargo.toml @@ -19,8 +19,8 @@ categories = ["gui", "development-tools", "no-std"] path = "lib.rs" [features] -ffi = ["dep:static_assertions"] # Expose C ABI -libm = ["num-traits/libm", "euclid/libm", "zeno?/libm"] +ffi = ["dep:static_assertions"] # Expose C ABI +libm = ["num-traits/libm", "euclid/libm"] # Allow the viewer to query at runtime information about item types rtti = [] # Use the standard library @@ -37,7 +37,6 @@ std = [ "image-decoders", "svg", "raw-window-handle-06?/std", - "zeno?/std", "chrono/std", "chrono/wasmbind", "chrono/clock", @@ -56,10 +55,6 @@ unsafe-single-threaded = [] unicode = ["unicode-script", "unicode-linebreak"] -software-renderer-systemfonts = ["shared-fontique", "dep:skrifa", "fontdue", "software-renderer", "shared-parley"] -software-renderer-path = ["dep:zeno"] -software-renderer = ["bytemuck"] - image-decoders = ["dep:image", "dep:clru"] image-default-formats = ["image?/default-formats"] svg = ["dep:resvg"] @@ -70,8 +65,6 @@ shared-fontique = ["i-slint-common/shared-fontique"] raw-window-handle-06 = ["dep:raw-window-handle-06"] -experimental = [] - experimental-rich-text = [] unstable-wgpu-26 = ["dep:wgpu-26"] @@ -114,9 +107,6 @@ strum = { workspace = true } unicode-segmentation = { workspace = true } unicode-linebreak = { version = "0.1.5", optional = true } unicode-script = { version = "0.5.7", optional = true } -integer-sqrt = { version = "0.1.5" } -bytemuck = { workspace = true, optional = true, features = ["derive"] } -zeno = { version = "0.3.3", optional = true, default-features = false, features = ["eval"] } sys-locale = { version = "0.3.2", optional = true, features = ["js"] } parley = { version = "0.7.0", optional = true } pulldown-cmark = { version = "0.13.0", optional = true } @@ -132,7 +122,6 @@ bitflags = { version = "2.4.2" } chrono = { version = "0.4", default-features = false, features = ["alloc"] } skrifa = { workspace = true, optional = true } -fontdue = { workspace = true, optional = true } wgpu-26 = { workspace = true, optional = true } wgpu-27 = { workspace = true, optional = true } @@ -153,7 +142,7 @@ web-sys = { workspace = true, features = ["HtmlImageElement", "Navigator"] } [dev-dependencies] -slint = { path = "../../api/rs/slint", default-features = false, features = ["std", "compat-1-2"] } +slint = { path = "../../api/rs/slint", default-features = false, features = ["std", "compat-1-2", "renderer-software"] } i-slint-backend-testing = { path = "../backends/testing" } rustybuzz = { workspace = true } ttf-parser = { workspace = true } @@ -173,5 +162,4 @@ unexpected_cfgs = { level = "warn", check-cfg = ["cfg(slint_debug_property)", "c unnecessary_cast = { level = "allow" } [package.metadata.docs.rs] -features = ["software-renderer", "experimental"] rustdoc-args = ["--generate-link-to-definition"] diff --git a/internal/core/lib.rs b/internal/core/lib.rs index 94ba4214a3..5af65b67be 100644 --- a/internal/core/lib.rs +++ b/internal/core/lib.rs @@ -16,16 +16,16 @@ extern crate alloc; extern crate std; #[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))] -pub(crate) mod unsafe_single_threaded; +pub mod unsafe_single_threaded; #[cfg(all(not(feature = "std"), not(feature = "unsafe-single-threaded")))] compile_error!( "At least one of the following feature need to be enabled: `std` or `unsafe-single-threaded`" ); use crate::items::OperatingSystemType; #[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))] -use crate::unsafe_single_threaded::thread_local; +pub use crate::unsafe_single_threaded::thread_local; #[cfg(feature = "std")] -use std::thread_local; +pub use std::thread_local; pub mod accessibility; pub mod animations; @@ -53,8 +53,6 @@ pub mod renderer; pub mod rtti; pub mod sharedvector; pub mod slice; -#[cfg(feature = "software-renderer")] -pub mod software_renderer; pub mod string; pub mod tests; pub mod textlayout; diff --git a/internal/core/partial_renderer.rs b/internal/core/partial_renderer.rs index 17b8594f21..102b500735 100644 --- a/internal/core/partial_renderer.rs +++ b/internal/core/partial_renderer.rs @@ -225,7 +225,7 @@ impl core::fmt::Debug for DirtyRegion { impl DirtyRegion { /// The maximum number of rectangles that can be stored in a DirtyRegion - pub(crate) const MAX_COUNT: usize = 3; + pub const MAX_COUNT: usize = 3; /// An iterator over the part of the region (they can overlap) pub fn iter(&self) -> impl Iterator> + '_ { @@ -331,7 +331,7 @@ impl From for DirtyRegion { } } -/// This enum describes which parts of the buffer passed to the [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer) may be re-used to speed up painting. +/// This enum describes which parts of the buffer passed to the `SoftwareRenderer` may be re-used to speed up painting. // FIXME: #[non_exhaustive] #3023 #[derive(PartialEq, Eq, Debug, Clone, Default, Copy)] pub enum RepaintBufferType { diff --git a/internal/core/platform.rs b/internal/core/platform.rs index 30f09e4c99..0ceb14df18 100644 --- a/internal/core/platform.rs +++ b/internal/core/platform.rs @@ -11,8 +11,6 @@ use crate::SharedString; pub use crate::api::PlatformError; use crate::api::{LogicalPosition, LogicalSize}; pub use crate::renderer::Renderer; -#[cfg(feature = "software-renderer")] -pub use crate::software_renderer; #[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))] use crate::unsafe_single_threaded::OnceCell; pub use crate::window::{LayoutConstraints, WindowAdapter, WindowProperties}; diff --git a/internal/core/renderer.rs b/internal/core/renderer.rs index 656c5d14ba..543b9cc401 100644 --- a/internal/core/renderer.rs +++ b/internal/core/renderer.rs @@ -16,8 +16,7 @@ use crate::window::WindowAdapter; /// /// This trait is [sealed](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed), /// meaning that you are not expected to implement this trait -/// yourself, but you should use the provided one from Slint such as -/// [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer) +/// yourself, but you should use the provided ones from Slint. pub trait Renderer: RendererSealed {} impl Renderer for T {} diff --git a/internal/core/unsafe_single_threaded.rs b/internal/core/unsafe_single_threaded.rs index e6d8cd430a..08dc2bb010 100644 --- a/internal/core/unsafe_single_threaded.rs +++ b/internal/core/unsafe_single_threaded.rs @@ -9,9 +9,9 @@ macro_rules! SLINT__thread_local_inner { ($(#[$($meta:tt)*])* $vis:vis $ident:ident $ty:ty $block:block) => { $(#[$($meta)*])* - $vis static $ident: crate::unsafe_single_threaded::FakeThreadStorage<$ty> = { + $vis static $ident: $crate::unsafe_single_threaded::FakeThreadStorage<$ty> = { fn init() -> $ty $block - crate::unsafe_single_threaded::FakeThreadStorage::new(init) + $crate::unsafe_single_threaded::FakeThreadStorage::new(init) }; }; } diff --git a/internal/core/window.rs b/internal/core/window.rs index cfb46d15df..b3102b25cd 100644 --- a/internal/core/window.rs +++ b/internal/core/window.rs @@ -61,13 +61,13 @@ fn previous_focus_item(item: ItemRc) -> ItemRc { /// experience unexpected behavior, or the intention of the developer calling functions on the [`Window`] /// API may not be fulfilled. /// -/// Your implementation must hold a renderer, such as [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer). +/// Your implementation must hold a renderer, such as `SoftwareRenderer` or `FemtoVGRenderer`. /// In the [`Self::renderer()`] function, you must return a reference to it. /// /// It is also required to hold a [`Window`] and return a reference to it in your /// implementation of [`Self::window()`]. /// -/// See also [`MinimalSoftwareWindow`](crate::software_renderer::MinimalSoftwareWindow) +/// See also `slint::platform::software_renderer::MinimalSoftwareWindow` /// for a minimal implementation of this trait using the software renderer pub trait WindowAdapter { /// Returns the window API. @@ -127,8 +127,6 @@ pub trait WindowAdapter { /// /// The `Renderer` trait is an internal trait that you are not expected to implement. /// In your implementation you should return a reference to an instance of one of the renderers provided by Slint. - /// - /// Currently, the only public struct that implement renderer is [`SoftwareRenderer`](crate::software_renderer::SoftwareRenderer). fn renderer(&self) -> &dyn Renderer; /// Re-implement this function to update the properties such as window title or layout constraints. @@ -2223,29 +2221,3 @@ pub mod ffi { } } } - -#[cfg(feature = "software-renderer")] -#[test] -fn test_empty_window() { - // Test that when creating an empty window without a component, we don't panic when render() is called. - // This isn't typically done intentionally, but for example if we receive a paint event in Qt before a component - // is set, this may happen. Concretely as per #2799 this could happen with popups where the call to - // QWidget::show() with egl delivers an immediate paint event, before we've had a chance to call set_component. - // Let's emulate this scenario here using public platform API. - - let msw = crate::software_renderer::MinimalSoftwareWindow::new( - crate::software_renderer::RepaintBufferType::NewBuffer, - ); - msw.window().request_redraw(); - let mut region = None; - let render_called = msw.draw_if_needed(|renderer| { - let mut buffer = - crate::graphics::SharedPixelBuffer::::new(100, 100); - let stride = buffer.width() as usize; - region = Some(renderer.render(buffer.make_mut_slice(), stride)); - }); - assert!(render_called); - let region = region.unwrap(); - assert_eq!(region.bounding_box_size(), PhysicalSize::default()); - assert_eq!(region.bounding_box_origin(), PhysicalPosition::default()); -} diff --git a/internal/renderers/software/Cargo.toml b/internal/renderers/software/Cargo.toml new file mode 100644 index 0000000000..cb02497609 --- /dev/null +++ b/internal/renderers/software/Cargo.toml @@ -0,0 +1,54 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 + +[package] +name = "i-slint-renderer-software" +description = "Slint's Software renderer implementation" +authors.workspace = true +edition = "2024" +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +path = "lib.rs" + +[features] +systemfonts = [ + "i-slint-core/shared-fontique", + "i-slint-common/shared-fontique", + "dep:skrifa", + "dep:fontdue", + "i-slint-core/shared-parley", + "std", + "dep:clru", +] +path = ["dep:zeno", "dep:lyon_path"] +std = ["zeno?/std", "num-traits/std", "euclid/std", "i-slint-core/std"] +libm = ["zeno?/libm", "num-traits/libm", "euclid/libm", "i-slint-core/libm"] + +experimental = [] +default = ["std"] + +[dependencies] +i-slint-core = { workspace = true } +i-slint-common = { workspace = true } +bytemuck = { workspace = true, features = ["derive"] } +clru = { workspace = true, optional = true } +derive_more = { workspace = true, features = ["error"] } +euclid = { workspace = true } +integer-sqrt = { version = "0.1.5" } +num-traits = { version = "0.2", default-features = false } +skrifa = { workspace = true, optional = true } +fontdue = { workspace = true, optional = true } +zeno = { version = "0.3.3", optional = true, default-features = false, features = ["eval"] } +lyon_path = { version = "1.0", optional = true } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(cbindgen)", "cfg(slint_nightly_test)"] } + +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] +features = ["experimental"] diff --git a/internal/renderers/software/LICENSES/GPL-3.0-only.txt b/internal/renderers/software/LICENSES/GPL-3.0-only.txt new file mode 120000 index 0000000000..6ac1cb9955 --- /dev/null +++ b/internal/renderers/software/LICENSES/GPL-3.0-only.txt @@ -0,0 +1 @@ +../../../../LICENSES/GPL-3.0-only.txt \ No newline at end of file diff --git a/internal/renderers/software/LICENSES/LicenseRef-Slint-Royalty-free-2.0.md b/internal/renderers/software/LICENSES/LicenseRef-Slint-Royalty-free-2.0.md new file mode 120000 index 0000000000..4f1673e140 --- /dev/null +++ b/internal/renderers/software/LICENSES/LicenseRef-Slint-Royalty-free-2.0.md @@ -0,0 +1 @@ +../../../../LICENSES/LicenseRef-Slint-Royalty-free-2.0.md \ No newline at end of file diff --git a/internal/renderers/software/LICENSES/LicenseRef-Slint-Software-3.0.md b/internal/renderers/software/LICENSES/LicenseRef-Slint-Software-3.0.md new file mode 120000 index 0000000000..e48a511e98 --- /dev/null +++ b/internal/renderers/software/LICENSES/LicenseRef-Slint-Software-3.0.md @@ -0,0 +1 @@ +../../../../LICENSES/LicenseRef-Slint-Software-3.0.md \ No newline at end of file diff --git a/internal/renderers/software/README.md b/internal/renderers/software/README.md new file mode 100644 index 0000000000..fb83b36751 --- /dev/null +++ b/internal/renderers/software/README.md @@ -0,0 +1,7 @@ + +**NOTE**: This library is an **internal** crate of the [Slint project](https://slint.dev). +This crate should **not be used directly** by applications using Slint. +You should use the `slint` crate instead. + +**WARNING**: This crate does not follow the semver convention for versioning and can +only be used with `version = "=x.y.z"` in Cargo.toml. diff --git a/internal/core/software_renderer/draw_functions.rs b/internal/renderers/software/draw_functions.rs similarity index 99% rename from internal/core/software_renderer/draw_functions.rs rename to internal/renderers/software/draw_functions.rs index 8963f6d8ed..e978bfcb7e 100644 --- a/internal/core/software_renderer/draw_functions.rs +++ b/internal/renderers/software/draw_functions.rs @@ -7,10 +7,10 @@ //! on the line buffer use super::{Fixed, PhysicalLength, PhysicalRect}; -use crate::Color; -use crate::graphics::{Rgb8Pixel, TexturePixelFormat}; -use crate::lengths::{PointLengths, SizeLengths}; use derive_more::{Add, Mul, Sub}; +use i_slint_core::Color; +use i_slint_core::graphics::{Rgb8Pixel, TexturePixelFormat}; +use i_slint_core::lengths::{PointLengths, SizeLengths}; use integer_sqrt::IntegerSquareRoot; #[allow(unused_imports)] use num_traits::Float; @@ -864,7 +864,7 @@ pub trait TargetPixel: Sized + Copy { } } -impl TargetPixel for crate::graphics::image::Rgb8Pixel { +impl TargetPixel for Rgb8Pixel { fn blend(&mut self, color: PremultipliedRgbaColor) { let a = (u8::MAX - color.alpha) as u16; self.r = (self.r as u16 * a / 255) as u8 + color.red; diff --git a/internal/core/software_renderer/fixed.rs b/internal/renderers/software/fixed.rs similarity index 100% rename from internal/core/software_renderer/fixed.rs rename to internal/renderers/software/fixed.rs diff --git a/internal/core/software_renderer/fonts.rs b/internal/renderers/software/fonts.rs similarity index 82% rename from internal/core/software_renderer/fonts.rs rename to internal/renderers/software/fonts.rs index ba16ab7dd1..26f8690fb0 100644 --- a/internal/core/software_renderer/fonts.rs +++ b/internal/renderers/software/fonts.rs @@ -6,12 +6,12 @@ use alloc::vec::Vec; use core::cell::RefCell; use super::{Fixed, PhysicalLength, PhysicalSize}; -use crate::Coord; -use crate::graphics::{BitmapFont, FontRequest}; -use crate::lengths::{LogicalLength, ScaleFactor}; -use crate::textlayout::TextLayout; +use i_slint_core::Coord; +use i_slint_core::graphics::{BitmapFont, FontRequest}; +use i_slint_core::lengths::{LogicalLength, ScaleFactor}; +use i_slint_core::textlayout::TextLayout; -crate::thread_local! { +i_slint_core::thread_local! { static BITMAP_FONTS: RefCell> = RefCell::default() } @@ -39,7 +39,7 @@ impl RenderableGlyph { } // Subset of `RenderableGlyph`, specfically for VectorFonts. -#[cfg(feature = "software-renderer-systemfonts")] +#[cfg(feature = "systemfonts")] #[derive(Clone)] pub struct RenderableVectorGlyph { pub x: Fixed, @@ -51,7 +51,7 @@ pub struct RenderableVectorGlyph { pub bounds: fontdue::OutlineBounds, } -#[cfg(feature = "software-renderer-systemfonts")] +#[cfg(feature = "systemfonts")] impl RenderableVectorGlyph { pub fn size(&self) -> PhysicalSize { PhysicalSize::from_lengths(self.width, self.height) @@ -67,24 +67,29 @@ pub trait GlyphRenderer { pub(super) const DEFAULT_FONT_SIZE: LogicalLength = LogicalLength::new(12 as Coord); mod pixelfont; -#[cfg(feature = "software-renderer-systemfonts")] +#[cfg(feature = "systemfonts")] pub mod vectorfont; -#[cfg(feature = "software-renderer-systemfonts")] +#[cfg(feature = "systemfonts")] pub mod systemfonts; #[derive(derive_more::From)] pub enum Font { PixelFont(pixelfont::PixelFont), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] VectorFont(vectorfont::VectorFont), } -impl crate::textlayout::FontMetrics for Font { +/// Returns the size of the pre-rendered font in pixels. +pub fn pixel_size(glyphs: &i_slint_core::graphics::BitmapGlyphs) -> PhysicalLength { + PhysicalLength::new(glyphs.pixel_size) +} + +impl i_slint_core::textlayout::FontMetrics for Font { fn ascent(&self) -> PhysicalLength { match self { Font::PixelFont(pixel_font) => pixel_font.ascent(), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] Font::VectorFont(vector_font) => vector_font.ascent(), } } @@ -92,7 +97,7 @@ impl crate::textlayout::FontMetrics for Font { fn height(&self) -> PhysicalLength { match self { Font::PixelFont(pixel_font) => pixel_font.height(), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] Font::VectorFont(vector_font) => vector_font.height(), } } @@ -100,7 +105,7 @@ impl crate::textlayout::FontMetrics for Font { fn descent(&self) -> PhysicalLength { match self { Font::PixelFont(pixel_font) => pixel_font.descent(), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] Font::VectorFont(vector_font) => vector_font.descent(), } } @@ -108,7 +113,7 @@ impl crate::textlayout::FontMetrics for Font { fn x_height(&self) -> PhysicalLength { match self { Font::PixelFont(pixel_font) => pixel_font.x_height(), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] Font::VectorFont(vector_font) => vector_font.x_height(), } } @@ -116,7 +121,7 @@ impl crate::textlayout::FontMetrics for Font { fn cap_height(&self) -> PhysicalLength { match self { Font::PixelFont(pixel_font) => pixel_font.cap_height(), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] Font::VectorFont(vector_font) => vector_font.cap_height(), } } @@ -147,7 +152,7 @@ pub fn match_font(request: &FontRequest, scale_factor: ScaleFactor) -> Font { let font = match bitmap_font { Some(bitmap_font) => bitmap_font, None => { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] if let Some(vectorfont) = systemfonts::match_font(request, scale_factor) { return vectorfont.into(); } @@ -162,9 +167,9 @@ pub fn match_font(request: &FontRequest, scale_factor: ScaleFactor) -> Font { }) { fallback_bitmap_font } else { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] return systemfonts::fallbackfont(request, scale_factor).into(); - #[cfg(not(feature = "software-renderer-systemfonts"))] + #[cfg(not(feature = "systemfonts"))] panic!( "No font fallback found. The software renderer requires enabling the `EmbedForSoftwareRenderer` option when compiling slint files." ) @@ -177,11 +182,11 @@ pub fn match_font(request: &FontRequest, scale_factor: ScaleFactor) -> Font { let nearest_pixel_size = font .glyphs - .partition_point(|glyphs| glyphs.pixel_size() <= requested_pixel_size) + .partition_point(|glyphs| pixel_size(glyphs) <= requested_pixel_size) .saturating_sub(1); let matching_glyphs = &font.glyphs[nearest_pixel_size]; - let pixel_size = if font.sdf { requested_pixel_size } else { matching_glyphs.pixel_size() }; + let pixel_size = if font.sdf { requested_pixel_size } else { pixel_size(matching_glyphs) }; pixelfont::PixelFont { bitmap_font: font, glyphs: matching_glyphs, pixel_size }.into() } @@ -192,7 +197,8 @@ pub fn text_layout_for_font<'a, Font>( scale_factor: ScaleFactor, ) -> TextLayout<'a, Font> where - Font: crate::textlayout::AbstractFont + crate::textlayout::TextShaper, + Font: i_slint_core::textlayout::AbstractFont + + i_slint_core::textlayout::TextShaper, { let letter_spacing = font_request.letter_spacing.map(|spacing| (spacing.cast() * scale_factor).cast()); diff --git a/internal/core/software_renderer/fonts/pixelfont.rs b/internal/renderers/software/fonts/pixelfont.rs similarity index 94% rename from internal/core/software_renderer/fonts/pixelfont.rs rename to internal/renderers/software/fonts/pixelfont.rs index 1cb46de726..82c197f93b 100644 --- a/internal/core/software_renderer/fonts/pixelfont.rs +++ b/internal/renderers/software/fonts/pixelfont.rs @@ -1,20 +1,13 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -use crate::graphics::{BitmapFont, BitmapGlyphs}; -use crate::software_renderer::PhysicalLength; -use crate::software_renderer::fixed::Fixed; -use crate::textlayout::{FontMetrics, Glyph, TextShaper}; +use crate::PhysicalLength; +use crate::fixed::Fixed; +use i_slint_core::graphics::{BitmapFont, BitmapGlyphs}; +use i_slint_core::textlayout::{FontMetrics, Glyph, TextShaper}; use super::{GlyphRenderer, RenderableGlyph}; -impl BitmapGlyphs { - /// Returns the size of the pre-rendered font in pixels. - pub fn pixel_size(&self) -> PhysicalLength { - PhysicalLength::new(self.pixel_size) - } -} - // A font that is resolved to a specific pixel size. pub struct PixelFont { pub bitmap_font: &'static BitmapFont, diff --git a/internal/core/software_renderer/fonts/systemfonts.rs b/internal/renderers/software/fonts/systemfonts.rs similarity index 97% rename from internal/core/software_renderer/fonts/systemfonts.rs rename to internal/renderers/software/fonts/systemfonts.rs index ef08417e45..9e3fd289c2 100644 --- a/internal/core/software_renderer/fonts/systemfonts.rs +++ b/internal/renderers/software/fonts/systemfonts.rs @@ -7,8 +7,8 @@ use alloc::boxed::Box; use alloc::rc::Rc; use std::collections::HashMap; -use crate::lengths::ScaleFactor; use i_slint_common::sharedfontique::{self, HashedBlob, fontique}; +use i_slint_core::lengths::ScaleFactor; use super::super::PhysicalLength; use super::vectorfont::VectorFont; @@ -17,7 +17,7 @@ struct CachedFont { fontdue_font: Rc, } -crate::thread_local! { +i_slint_core::thread_local! { // fontdue fonts cached and indexed by fontique blob id (unique incremental) and true type collection index static FONTDUE_FONTS: RefCell> = Default::default(); } diff --git a/internal/core/software_renderer/fonts/vectorfont.rs b/internal/renderers/software/fonts/vectorfont.rs similarity index 95% rename from internal/core/software_renderer/fonts/vectorfont.rs rename to internal/renderers/software/fonts/vectorfont.rs index dc4d9c29b2..f62402adae 100644 --- a/internal/core/software_renderer/fonts/vectorfont.rs +++ b/internal/renderers/software/fonts/vectorfont.rs @@ -6,11 +6,11 @@ use core::num::NonZeroU16; use alloc::rc::Rc; use skrifa::MetadataProvider; -use crate::lengths::PhysicalPx; -use crate::software_renderer::PhysicalLength; -use crate::software_renderer::fixed::Fixed; -use crate::textlayout::{Glyph, TextShaper}; +use crate::PhysicalLength; +use crate::fixed::Fixed; use i_slint_common::sharedfontique::fontique; +use i_slint_core::lengths::PhysicalPx; +use i_slint_core::textlayout::{Glyph, TextShaper}; use super::RenderableVectorGlyph; @@ -36,7 +36,7 @@ type GlyphCache = clru::CLruCache< RenderableGlyphWeightScale, >; -crate::thread_local!(static GLYPH_CACHE: core::cell::RefCell = +i_slint_core::thread_local!(static GLYPH_CACHE: core::cell::RefCell = core::cell::RefCell::new( clru::CLruCache::with_config( clru::CLruCacheConfig::new(core::num::NonZeroUsize::new(1024 * 1024).unwrap()) @@ -177,7 +177,7 @@ impl TextShaper for VectorFont { } } -impl crate::textlayout::FontMetrics for VectorFont { +impl i_slint_core::textlayout::FontMetrics for VectorFont { fn ascent(&self) -> PhysicalLength { self.ascender } diff --git a/internal/core/software_renderer.rs b/internal/renderers/software/lib.rs similarity index 95% rename from internal/core/software_renderer.rs rename to internal/renderers/software/lib.rs index 26164f1dab..17e4000668 100644 --- a/internal/core/software_renderer.rs +++ b/internal/renderers/software/lib.rs @@ -1,47 +1,54 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -//! This module contains the [`SoftwareRenderer`] and related types. -//! -//! It is only enabled when the `renderer-software` Slint feature is enabled. - +#![doc = include_str!("README.md")] +#![doc(html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg")] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(unsafe_code)] +#![cfg_attr(slint_nightly_test, feature(non_exhaustive_omitted_patterns_lint))] +#![cfg_attr(slint_nightly_test, warn(non_exhaustive_omitted_patterns))] +#![no_std] #![warn(missing_docs)] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + mod draw_functions; mod fixed; mod fonts; mod minimal_software_window; -#[cfg(feature = "software-renderer-path")] +#[cfg(feature = "path")] mod path; mod scene; use self::fonts::GlyphRenderer; pub use self::minimal_software_window::MinimalSoftwareWindow; use self::scene::*; -use crate::api::PlatformError; -use crate::graphics::rendering_metrics_collector::{RefreshMode, RenderingMetricsCollector}; -use crate::graphics::{BorderRadius, Rgba8Pixel, SharedImageBuffer, SharedPixelBuffer}; -use crate::item_rendering::{ - CachedRenderingData, ItemRenderer, PlainOrStyledText, RenderBorderRectangle, RenderImage, - RenderRectangle, -}; -use crate::item_tree::ItemTreeWeak; -use crate::items::{ItemRc, TextOverflow, TextWrap}; -use crate::lengths::{ - LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector, - PhysicalPx, PointLengths, RectLengths, ScaleFactor, SizeLengths, -}; -use crate::partial_renderer::{DirtyRegion, PartialRenderingState}; -use crate::renderer::RendererSealed; -use crate::textlayout::{AbstractFont, FontMetrics, TextParagraphLayout}; -use crate::window::{WindowAdapter, WindowInner}; -use crate::{Brush, Color, ImageInner, StaticTextures}; use alloc::rc::{Rc, Weak}; use alloc::vec::Vec; use core::cell::{Cell, RefCell}; use core::pin::Pin; use euclid::Length; use fixed::Fixed; +use i_slint_core::api::PlatformError; +use i_slint_core::graphics::rendering_metrics_collector::{RefreshMode, RenderingMetricsCollector}; +use i_slint_core::graphics::{BorderRadius, Rgba8Pixel, SharedImageBuffer, SharedPixelBuffer}; +use i_slint_core::item_rendering::{ + CachedRenderingData, ItemRenderer, PlainOrStyledText, RenderBorderRectangle, RenderImage, + RenderRectangle, +}; +use i_slint_core::item_tree::ItemTreeWeak; +use i_slint_core::items::{ItemRc, TextOverflow, TextWrap}; +use i_slint_core::lengths::{ + LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector, + PhysicalPx, PointLengths, RectLengths, ScaleFactor, SizeLengths, +}; +use i_slint_core::partial_renderer::{DirtyRegion, PartialRenderingState}; +use i_slint_core::renderer::RendererSealed; +use i_slint_core::textlayout::{AbstractFont, FontMetrics, TextParagraphLayout}; +use i_slint_core::window::{WindowAdapter, WindowInner}; +use i_slint_core::{Brush, Color, ImageInner, StaticTextures}; #[allow(unused)] use num_traits::Float; use num_traits::NumCast; @@ -54,7 +61,7 @@ type PhysicalSize = euclid::Size2D; type PhysicalPoint = euclid::Point2D; type PhysicalBorderRadius = BorderRadius; -pub use crate::partial_renderer::RepaintBufferType; +pub use i_slint_core::partial_renderer::RepaintBufferType; /// This enum describes the rotation that should be applied to the contents rendered by the software renderer. /// @@ -227,14 +234,14 @@ impl PhysicalRegion { } /// Returns the size of the bounding box of this region. - pub fn bounding_box_size(&self) -> crate::api::PhysicalSize { + pub fn bounding_box_size(&self) -> i_slint_core::api::PhysicalSize { let bb = self.bounding_rect(); - crate::api::PhysicalSize { width: bb.width() as _, height: bb.height() as _ } + i_slint_core::api::PhysicalSize { width: bb.width() as _, height: bb.height() as _ } } /// Returns the origin of the bounding box of this region. - pub fn bounding_box_origin(&self) -> crate::api::PhysicalPosition { + pub fn bounding_box_origin(&self) -> i_slint_core::api::PhysicalPosition { let bb = self.bounding_rect(); - crate::api::PhysicalPosition { x: bb.origin.x as _, y: bb.origin.y as _ } + i_slint_core::api::PhysicalPosition { x: bb.origin.x as _, y: bb.origin.y as _ } } /// Returns an iterator over the rectangles in this region. @@ -242,7 +249,8 @@ impl PhysicalRegion { /// They do not overlap. pub fn iter( &self, - ) -> impl Iterator + '_ { + ) -> impl Iterator + '_ + { let mut line_ranges = Vec::>::new(); let mut begin_line = 0; let mut end_line = 0; @@ -251,8 +259,11 @@ impl PhysicalRegion { match line_ranges.pop() { Some(r) => { return Some(( - crate::api::PhysicalPosition { x: r.start as _, y: begin_line as _ }, - crate::api::PhysicalSize { + i_slint_core::api::PhysicalPosition { + x: r.start as _, + y: begin_line as _, + }, + i_slint_core::api::PhysicalSize { width: r.len() as _, height: (end_line - begin_line) as _, }, @@ -299,7 +310,10 @@ fn region_iter() { assert_eq!(region.iter().next(), None); region.count = 1; let r = |x, y, width, height| { - (crate::api::PhysicalPosition { x, y }, crate::api::PhysicalSize { width, height }) + ( + i_slint_core::api::PhysicalPosition { x, y }, + i_slint_core::api::PhysicalSize { width, height }, + ) }; let mut iter = region.iter(); @@ -426,7 +440,7 @@ pub struct SoftwareRenderer { /// Only used if repaint_buffer_type == RepaintBufferType::SwappedBuffers prev_frame_dirty: Cell, partial_rendering_state: PartialRenderingState, - maybe_window_adapter: RefCell>>, + maybe_window_adapter: RefCell>>, rotation: Cell, rendering_metrics_collector: Option>, } @@ -641,7 +655,7 @@ impl SoftwareRenderer { for (component, origin) in components { if let Some(component) = ItemTreeWeak::upgrade(component) { - crate::item_rendering::render_component_items( + i_slint_core::item_rendering::render_component_items( &component, &mut renderer, *origin, @@ -660,7 +674,7 @@ impl SoftwareRenderer { fn measure_frame_rendered(&self, renderer: &mut dyn ItemRenderer) { if let Some(metrics) = &self.rendering_metrics_collector { let prev_frame_dirty = self.prev_frame_dirty.take(); - let m = crate::graphics::rendering_metrics_collector::RenderingMetrics { + let m = i_slint_core::graphics::rendering_metrics_collector::RenderingMetrics { dirty_region: Some(prev_frame_dirty.clone()), ..Default::default() }; @@ -686,7 +700,7 @@ impl SoftwareRenderer { /// then be more efficient) /// /// ```rust - /// # use i_slint_core::software_renderer::{LineBufferProvider, SoftwareRenderer, Rgb565Pixel}; + /// # use i_slint_renderer_software::{LineBufferProvider, SoftwareRenderer, Rgb565Pixel}; /// # fn xxx<'a>(the_frame_buffer: &'a mut [Rgb565Pixel], display_width: usize, renderer: &SoftwareRenderer) { /// struct FrameBuffer<'a>{ frame_buffer: &'a mut [Rgb565Pixel], stride: usize } /// impl<'a> LineBufferProvider for FrameBuffer<'a> { @@ -713,10 +727,11 @@ impl SoftwareRenderer { }; let window_inner = WindowInner::from_pub(window.window()); let component_rc = window_inner.component(); - let component = crate::item_tree::ItemTreeRc::borrow_pin(&component_rc); - if let Some(window_item) = crate::items::ItemRef::downcast_pin::( - component.as_ref().get_item_ref(0), - ) { + let component = i_slint_core::item_tree::ItemTreeRc::borrow_pin(&component_rc); + if let Some(window_item) = i_slint_core::items::ItemRef::downcast_pin::< + i_slint_core::items::WindowItem, + >(component.as_ref().get_item_ref(0)) + { let factor = ScaleFactor::new(window_inner.scale_factor()); let size = LogicalSize::from_lengths(window_item.width(), window_item.height()).cast() * factor; @@ -737,8 +752,8 @@ impl SoftwareRenderer { impl RendererSealed for SoftwareRenderer { fn text_size( &self, - text_item: Pin<&dyn crate::item_rendering::RenderString>, - item_rc: &crate::item_tree::ItemRc, + text_item: Pin<&dyn i_slint_core::item_rendering::RenderString>, + item_rc: &i_slint_core::item_tree::ItemRc, max_width: Option, text_wrap: TextWrap, ) -> LogicalSize { @@ -749,11 +764,11 @@ impl RendererSealed for SoftwareRenderer { let font = fonts::match_font(&font_request, scale_factor); match (font, parley_disabled(), text_item.text()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false, _) => { sharedparley::text_size(self, text_item, item_rc, max_width, text_wrap) } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true, PlainOrStyledText::Plain(text)) => { let layout = fonts::text_layout_for_font(&vf, &font_request, scale_factor); let (longest_line_width, height) = layout.text_size( @@ -783,8 +798,8 @@ impl RendererSealed for SoftwareRenderer { fn char_size( &self, - text_item: Pin<&dyn crate::item_rendering::HasFont>, - item_rc: &crate::item_tree::ItemRc, + text_item: Pin<&dyn i_slint_core::item_rendering::HasFont>, + item_rc: &i_slint_core::item_tree::ItemRc, ch: char, ) -> LogicalSize { let Some(scale_factor) = self.scale_factor() else { @@ -794,11 +809,11 @@ impl RendererSealed for SoftwareRenderer { let font = fonts::match_font(&font_request, scale_factor); match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => { sharedparley::char_size(text_item, item_rc, ch).unwrap_or_default() } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true) => { let mut buf = [0u8, 0u8, 0u8, 0u8]; let layout = fonts::text_layout_for_font(&vf, &font_request, scale_factor); @@ -820,24 +835,24 @@ impl RendererSealed for SoftwareRenderer { fn font_metrics( &self, - font_request: crate::graphics::FontRequest, - ) -> crate::items::FontMetrics { + font_request: i_slint_core::graphics::FontRequest, + ) -> i_slint_core::items::FontMetrics { let Some(scale_factor) = self.scale_factor() else { - return crate::items::FontMetrics::default(); + return i_slint_core::items::FontMetrics::default(); }; let font = fonts::match_font(&font_request, scale_factor); match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => sharedparley::font_metrics(font_request), - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(font), true) => { let ascent: LogicalLength = (font.ascent().cast() / scale_factor).cast(); let descent: LogicalLength = (font.descent().cast() / scale_factor).cast(); let x_height: LogicalLength = (font.x_height().cast() / scale_factor).cast(); let cap_height: LogicalLength = (font.cap_height().cast() / scale_factor).cast(); - crate::items::FontMetrics { + i_slint_core::items::FontMetrics { ascent: ascent.get() as _, descent: descent.get() as _, x_height: x_height.get() as _, @@ -850,7 +865,7 @@ impl RendererSealed for SoftwareRenderer { let x_height: LogicalLength = (font.x_height().cast() / scale_factor).cast(); let cap_height: LogicalLength = (font.cap_height().cast() / scale_factor).cast(); - crate::items::FontMetrics { + i_slint_core::items::FontMetrics { ascent: ascent.get() as _, descent: descent.get() as _, x_height: x_height.get() as _, @@ -862,7 +877,7 @@ impl RendererSealed for SoftwareRenderer { fn text_input_byte_offset_for_position( &self, - text_input: Pin<&crate::items::TextInput>, + text_input: Pin<&i_slint_core::items::TextInput>, item_rc: &ItemRc, pos: LogicalPoint, ) -> usize { @@ -873,11 +888,11 @@ impl RendererSealed for SoftwareRenderer { let font = fonts::match_font(&font_request, scale_factor); match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => { sharedparley::text_input_byte_offset_for_position(self, text_input, item_rc, pos) } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true) => { let visual_representation = text_input.visual_representation(None); @@ -939,7 +954,7 @@ impl RendererSealed for SoftwareRenderer { fn text_input_cursor_rect_for_byte_offset( &self, - text_input: Pin<&crate::items::TextInput>, + text_input: Pin<&i_slint_core::items::TextInput>, item_rc: &ItemRc, byte_offset: usize, ) -> LogicalRect { @@ -950,7 +965,7 @@ impl RendererSealed for SoftwareRenderer { let font = fonts::match_font(&font_request, scale_factor); match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => { sharedparley::text_input_cursor_rect_for_byte_offset( self, @@ -959,7 +974,7 @@ impl RendererSealed for SoftwareRenderer { byte_offset, ) } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true) => { let visual_representation = text_input.visual_representation(None); @@ -1033,9 +1048,9 @@ impl RendererSealed for SoftwareRenderer { fn free_graphics_resources( &self, - _component: crate::item_tree::ItemTreeRef, - items: &mut dyn Iterator>>, - ) -> Result<(), crate::platform::PlatformError> { + _component: i_slint_core::item_tree::ItemTreeRef, + items: &mut dyn Iterator>>, + ) -> Result<(), i_slint_core::platform::PlatformError> { self.partial_rendering_state.free_graphics_resources(items); Ok(()) } @@ -1044,11 +1059,11 @@ impl RendererSealed for SoftwareRenderer { self.partial_rendering_state.mark_dirty_region(region); } - fn register_bitmap_font(&self, font_data: &'static crate::graphics::BitmapFont) { + fn register_bitmap_font(&self, font_data: &'static i_slint_core::graphics::BitmapFont) { fonts::register_bitmap_font(font_data); } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] fn register_font_from_memory( &self, data: &'static [u8], @@ -1056,7 +1071,7 @@ impl RendererSealed for SoftwareRenderer { self::fonts::systemfonts::register_font_from_memory(data) } - #[cfg(all(feature = "software-renderer-systemfonts", not(target_arch = "wasm32")))] + #[cfg(all(feature = "systemfonts", not(target_arch = "wasm32")))] fn register_font_from_path( &self, path: &std::path::Path, @@ -1098,7 +1113,8 @@ impl RendererSealed for SoftwareRenderer { return Err("take_snapshot() called on window with invalid size".into()); }; - let mut target_buffer = SharedPixelBuffer::::new(width, height); + let mut target_buffer = + SharedPixelBuffer::::new(width, height); self.set_repaint_buffer_type(RepaintBufferType::NewBuffer); self.render(target_buffer.make_mut_slice(), width as usize); @@ -1123,11 +1139,11 @@ impl RendererSealed for SoftwareRenderer { } fn parley_disabled() -> bool { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] { std::env::var("SLINT_SOFTWARE_RENDERER_PARLEY_DISABLED").is_ok() } - #[cfg(not(feature = "software-renderer-systemfonts"))] + #[cfg(not(feature = "systemfonts"))] false } @@ -1322,7 +1338,7 @@ fn prepare_scene( for (component, origin) in components { if let Some(component) = ItemTreeWeak::upgrade(component) { - crate::item_rendering::render_component_items( + i_slint_core::item_rendering::render_component_items( &component, &mut renderer, *origin, @@ -1371,7 +1387,7 @@ trait ProcessScene { fn process_linear_gradient(&mut self, geometry: PhysicalRect, gradient: LinearGradientCommand); fn process_radial_gradient(&mut self, geometry: PhysicalRect, gradient: RadialGradientCommand); fn process_conic_gradient(&mut self, geometry: PhysicalRect, gradient: ConicGradientCommand); - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_filled_path( &mut self, path_geometry: PhysicalRect, @@ -1379,7 +1395,7 @@ trait ProcessScene { commands: alloc::vec::Vec, color: PremultipliedRgbaColor, ); - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_stroked_path( &mut self, path_geometry: PhysicalRect, @@ -1769,7 +1785,7 @@ impl ProcessScene for RenderToBuffer< }); } - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_filled_path( &mut self, path_geometry: PhysicalRect, @@ -1780,7 +1796,7 @@ impl ProcessScene for RenderToBuffer< path::render_filled_path(&commands, &path_geometry, &clip_geometry, color, self.buffer); } - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_stroked_path( &mut self, path_geometry: PhysicalRect, @@ -1932,7 +1948,7 @@ impl ProcessScene for PrepareScene { } } - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_filled_path( &mut self, _path_geometry: PhysicalRect, @@ -1944,7 +1960,7 @@ impl ProcessScene for PrepareScene { // Only works with buffer-based rendering (RenderToBuffer) } - #[cfg(feature = "software-renderer-path")] + #[cfg(feature = "path")] fn process_stroked_path( &mut self, _path_geometry: PhysicalRect, @@ -2001,14 +2017,14 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { fn draw_image_impl( &mut self, image_inner: &ImageInner, - crate::graphics::FitResult { + i_slint_core::graphics::FitResult { clip_rect: source_rect, source_to_target_x, source_to_target_y, size: fit_size, offset: image_fit_offset, tiled, - }: crate::graphics::FitResult, + }: i_slint_core::graphics::FitResult, colorize: Color, ) { let global_alpha_u16 = (self.current_state.alpha * 255.) as u16; @@ -2190,7 +2206,9 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { color: Color, selection: Option, ) where - Font: AbstractFont + crate::textlayout::TextShaper + GlyphRenderer, + Font: AbstractFont + + i_slint_core::textlayout::TextShaper + + GlyphRenderer, { paragraph .layout_lines::<()>( @@ -2388,7 +2406,7 @@ struct RenderState { clip: LogicalRect, } -impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T> { +impl i_slint_core::item_rendering::ItemRenderer for SceneBuilder<'_, T> { fn draw_rectangle( &mut self, rect: Pin<&dyn RenderRectangle>, @@ -2492,7 +2510,7 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T if let ImageInner::NineSlice(nine) = image_inner { let colorize = image.colorize().color(); let source_size = source.size(); - for fit in crate::graphics::fit9slice( + for fit in i_slint_core::graphics::fit9slice( source_size, nine.1, size.cast() * self.scale_factor, @@ -2514,7 +2532,7 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T ); let phys_size = geom.size_length().cast() * self.scale_factor; - let fit = crate::graphics::fit( + let fit = i_slint_core::graphics::fit( image.image_fit(), phys_size, source_clip, @@ -2528,7 +2546,7 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T fn draw_text( &mut self, - text: Pin<&dyn crate::item_rendering::RenderText>, + text: Pin<&dyn i_slint_core::item_rendering::RenderText>, self_rc: &ItemRc, size: LogicalSize, _cache: &CachedRenderingData, @@ -2538,11 +2556,11 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T let font = fonts::match_font(&font_request, self.scale_factor); match (font, parley_disabled(), text.text()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false, _) => { sharedparley::draw_text(self, text, Some(self_rc), size); } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true, PlainOrStyledText::Plain(string)) => { if string.trim().is_empty() { return; @@ -2632,7 +2650,7 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T fn draw_text_input( &mut self, - text_input: Pin<&crate::items::TextInput>, + text_input: Pin<&i_slint_core::items::TextInput>, self_rc: &ItemRc, size: LogicalSize, ) { @@ -2640,11 +2658,11 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T let font = fonts::match_font(&font_request, self.scale_factor); match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => { sharedparley::draw_text_input(self, text_input, self_rc, size, None); } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true) => { let geom = LogicalRect::from(size); if !self.should_draw(&geom) { @@ -2816,18 +2834,23 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T } } - #[cfg(all(feature = "std", not(feature = "software-renderer-path")))] + #[cfg(all(feature = "std", not(feature = "path")))] fn draw_path( &mut self, - _path: Pin<&crate::items::Path>, + _path: Pin<&i_slint_core::items::Path>, _self_rc: &ItemRc, _size: LogicalSize, ) { - // Path rendering is disabled without the software-renderer-path feature + // Path rendering is disabled without the path feature } - #[cfg(all(feature = "std", feature = "software-renderer-path"))] - fn draw_path(&mut self, path: Pin<&crate::items::Path>, self_rc: &ItemRc, size: LogicalSize) { + #[cfg(all(feature = "std", feature = "path"))] + fn draw_path( + &mut self, + path: Pin<&i_slint_core::items::Path>, + self_rc: &ItemRc, + size: LogicalSize, + ) { let geom = LogicalRect::from(size); if !self.should_draw(&geom) { return; @@ -2898,7 +2921,7 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T fn draw_box_shadow( &mut self, - _box_shadow: Pin<&crate::items::BoxShadow>, + _box_shadow: Pin<&i_slint_core::items::BoxShadow>, _: &ItemRc, _size: LogicalSize, ) { @@ -3005,16 +3028,16 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T let clip = self.current_state.clip.cast() * self.scale_factor; match (font, parley_disabled()) { - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(_), false) => { sharedparley::draw_text( self, - std::pin::pin!((crate::SharedString::from(string), Brush::from(color))), + std::pin::pin!((i_slint_core::SharedString::from(string), Brush::from(color))), None, self.current_state.clip.size.cast(), ); } - #[cfg(feature = "software-renderer-systemfonts")] + #[cfg(feature = "systemfonts")] (fonts::Font::VectorFont(vf), true) => { let layout = fonts::text_layout_for_font(&vf, &font_request, self.scale_factor); @@ -3052,11 +3075,11 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T } } - fn draw_image_direct(&mut self, _image: crate::graphics::Image) { + fn draw_image_direct(&mut self, _image: i_slint_core::graphics::Image) { todo!() } - fn window(&self) -> &crate::window::WindowInner { + fn window(&self) -> &i_slint_core::window::WindowInner { self.window } @@ -3065,14 +3088,14 @@ impl crate::item_rendering::ItemRenderer for SceneBuilder<'_, T } } -impl crate::item_rendering::ItemRendererFeatures for SceneBuilder<'_, T> { +impl i_slint_core::item_rendering::ItemRendererFeatures for SceneBuilder<'_, T> { const SUPPORTS_TRANSFORMATIONS: bool = false; } -#[cfg(feature = "software-renderer-systemfonts")] -use crate::textlayout::sharedparley; +#[cfg(feature = "systemfonts")] +use i_slint_core::textlayout::sharedparley; -#[cfg(feature = "software-renderer-systemfonts")] +#[cfg(feature = "systemfonts")] impl sharedparley::GlyphRenderer for SceneBuilder<'_, T> { type PlatformBrush = Color; @@ -3085,10 +3108,7 @@ impl sharedparley::GlyphRenderer for SceneBuilder<'_, T> { brush: Brush, _size: LogicalSize, ) -> Option { - match brush { - Brush::SolidColor(color) => Some(color), - _ => None, - } + Some(brush.color()) } fn platform_text_stroke_brush( @@ -3097,10 +3117,7 @@ impl sharedparley::GlyphRenderer for SceneBuilder<'_, T> { _physical_stroke_width: f32, _size: LogicalSize, ) -> Option { - match brush { - Brush::SolidColor(color) => Some(color), - _ => None, - } + Some(brush.color()) } fn fill_rectangle(&mut self, mut physical_rect: sharedparley::PhysicalRect, color: Color) { diff --git a/internal/core/software_renderer/minimal_software_window.rs b/internal/renderers/software/minimal_software_window.rs similarity index 66% rename from internal/core/software_renderer/minimal_software_window.rs rename to internal/renderers/software/minimal_software_window.rs index 824bfa4eb1..d90a03fad3 100644 --- a/internal/core/software_renderer/minimal_software_window.rs +++ b/internal/renderers/software/minimal_software_window.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 use super::{RepaintBufferType, SoftwareRenderer}; -use crate::api::Window; -use crate::platform::Renderer; -use crate::window::WindowAdapter; use alloc::rc::{Rc, Weak}; use core::cell::Cell; +use i_slint_core::api::Window; +use i_slint_core::platform::Renderer; +use i_slint_core::window::WindowAdapter; /// This is a minimal adapter for a Window that doesn't have any other feature than rendering /// using the software renderer. @@ -14,7 +14,7 @@ pub struct MinimalSoftwareWindow { window: Window, renderer: SoftwareRenderer, needs_redraw: Cell, - size: Cell, + size: Cell, } impl MinimalSoftwareWindow { @@ -69,7 +69,7 @@ impl MinimalSoftwareWindow { /// Forward to the window through Deref /// (Before 1.1, WindowAdapter didn't have set_size, so the one from Deref was used. /// But in Slint 1.1, if one had imported the WindowAdapter trait, the other one would be found) - pub fn set_size(&self, size: impl Into) { + pub fn set_size(&self, size: impl Into) { self.window.set_size(size); } } @@ -83,14 +83,15 @@ impl WindowAdapter for MinimalSoftwareWindow { &self.renderer } - fn size(&self) -> crate::api::PhysicalSize { + fn size(&self) -> i_slint_core::api::PhysicalSize { self.size.get() } - fn set_size(&self, size: crate::api::WindowSize) { + fn set_size(&self, size: i_slint_core::api::WindowSize) { let sf = self.window.scale_factor(); self.size.set(size.to_physical(sf)); let logical_size = size.to_logical(sf); - self.window.dispatch_event(crate::platform::WindowEvent::Resized { size: logical_size }); + self.window + .dispatch_event(i_slint_core::platform::WindowEvent::Resized { size: logical_size }); } fn request_redraw(&self) { @@ -104,3 +105,27 @@ impl core::ops::Deref for MinimalSoftwareWindow { &self.window } } + +#[test] +fn test_empty_window() { + // Test that when creating an empty window without a component, we don't panic when render() is called. + // This isn't typically done intentionally, but for example if we receive a paint event in Qt before a component + // is set, this may happen. Concretely as per #2799 this could happen with popups where the call to + // QWidget::show() with egl delivers an immediate paint event, before we've had a chance to call set_component. + // Let's emulate this scenario here using public platform API. + + let msw = MinimalSoftwareWindow::new(RepaintBufferType::NewBuffer); + msw.window().request_redraw(); + let mut region = None; + let render_called = msw.draw_if_needed(|renderer| { + let mut buffer = i_slint_core::graphics::SharedPixelBuffer::< + i_slint_core::graphics::Rgb8Pixel, + >::new(100, 100); + let stride = buffer.width() as usize; + region = Some(renderer.render(buffer.make_mut_slice(), stride)); + }); + assert!(render_called); + let region = region.unwrap(); + assert_eq!(region.bounding_box_size(), i_slint_core::api::PhysicalSize::default()); + assert_eq!(region.bounding_box_origin(), i_slint_core::api::PhysicalPosition::default()); +} diff --git a/internal/core/software_renderer/path.rs b/internal/renderers/software/path.rs similarity index 94% rename from internal/core/software_renderer/path.rs rename to internal/renderers/software/path.rs index 0222985fd9..740db218e1 100644 --- a/internal/core/software_renderer/path.rs +++ b/internal/renderers/software/path.rs @@ -5,9 +5,9 @@ use super::PhysicalRect; use super::draw_functions::{PremultipliedRgbaColor, TargetPixel}; -#[cfg(feature = "std")] -use crate::graphics::PathDataIterator; use alloc::vec::Vec; +#[cfg(feature = "std")] +use i_slint_core::graphics::PathDataIterator; use zeno::{Fill, Mask, Stroke}; pub use zeno::Command; @@ -55,7 +55,7 @@ fn render_path_with_style( clip_geometry: &PhysicalRect, color: PremultipliedRgbaColor, style: zeno::Style, - buffer: &mut impl crate::software_renderer::target_pixel_buffer::TargetPixelBuffer, + buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer, ) { // The mask needs to be rendered at the full path size let path_width = path_geometry.size.width as usize; @@ -132,7 +132,7 @@ pub fn render_filled_path( path_geometry: &PhysicalRect, clip_geometry: &PhysicalRect, color: PremultipliedRgbaColor, - buffer: &mut impl crate::software_renderer::target_pixel_buffer::TargetPixelBuffer, + buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer, ) { render_path_with_style( commands, @@ -158,7 +158,7 @@ pub fn render_stroked_path( clip_geometry: &PhysicalRect, color: PremultipliedRgbaColor, stroke_width: f32, - buffer: &mut impl crate::software_renderer::target_pixel_buffer::TargetPixelBuffer, + buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer, ) { render_path_with_style( commands, diff --git a/internal/core/software_renderer/scene.rs b/internal/renderers/software/scene.rs similarity index 98% rename from internal/core/software_renderer/scene.rs rename to internal/renderers/software/scene.rs index 6ca6ba38c9..33079ea853 100644 --- a/internal/core/software_renderer/scene.rs +++ b/internal/renderers/software/scene.rs @@ -7,12 +7,12 @@ use super::{ Fixed, PhysicalBorderRadius, PhysicalLength, PhysicalPoint, PhysicalRect, PhysicalRegion, PhysicalSize, PremultipliedRgbaColor, RenderingRotation, }; -use crate::Color; -use crate::graphics::{SharedImageBuffer, TexturePixelFormat}; -use crate::lengths::{PointLengths as _, SizeLengths as _}; use alloc::rc::Rc; use alloc::vec::Vec; use euclid::Length; +use i_slint_core::Color; +use i_slint_core::graphics::{SharedImageBuffer, TexturePixelFormat}; +use i_slint_core::lengths::{PointLengths as _, SizeLengths as _}; #[derive(Default)] pub struct SceneVectors { @@ -535,7 +535,7 @@ pub struct LinearGradientCommand { #[derive(Debug)] pub struct RadialGradientCommand { /// The gradient stops (colors and positions) - pub stops: crate::SharedVector, + pub stops: i_slint_core::SharedVector, /// Center of the gradient relative to the item position pub center_x: PhysicalLength, pub center_y: PhysicalLength, @@ -551,5 +551,5 @@ pub struct RadialGradientCommand { pub struct ConicGradientCommand { /// The gradient stops (colors and normalized angle positions) /// Position 0 = 0 degrees (north), 1 = 360 degrees - pub stops: crate::SharedVector, + pub stops: i_slint_core::SharedVector, } diff --git a/internal/core/software_renderer/target_pixel_buffer.rs b/internal/renderers/software/target_pixel_buffer.rs similarity index 99% rename from internal/core/software_renderer/target_pixel_buffer.rs rename to internal/renderers/software/target_pixel_buffer.rs index e400af4d08..15b25561ed 100644 --- a/internal/core/software_renderer/target_pixel_buffer.rs +++ b/internal/renderers/software/target_pixel_buffer.rs @@ -3,8 +3,8 @@ use super::*; -use crate::graphics::IntSize; -pub use crate::graphics::TexturePixelFormat; +use i_slint_core::graphics::IntSize; +pub use i_slint_core::graphics::TexturePixelFormat; /// The pixel data of a for the source of a [`Texture`]. #[derive(Clone)] diff --git a/scripts/publish.sh b/scripts/publish.sh index cfa5bb278c..86b31da8ed 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -9,6 +9,7 @@ cargo publish --manifest-path internal/core/Cargo.toml cargo publish --manifest-path api/rs/macros/Cargo.toml cargo publish --manifest-path internal/renderers/skia/Cargo.toml --features x11 cargo publish --manifest-path internal/renderers/femtovg/Cargo.toml +cargo publish --manifest-path internal/renderers/software/Cargo.toml cargo publish --manifest-path internal/backends/winit/Cargo.toml --features x11,renderer-femtovg cargo publish --manifest-path api/rs/build/Cargo.toml cargo publish --manifest-path internal/backends/qt/Cargo.toml diff --git a/tests/screenshots/software.rs b/tests/screenshots/software.rs index 6b2af829d2..544a6f419d 100644 --- a/tests/screenshots/software.rs +++ b/tests/screenshots/software.rs @@ -8,7 +8,7 @@ use i_slint_core::graphics::{IntRect, Rgb8Pixel, SharedPixelBuffer, euclid}; use i_slint_core::lengths::LogicalRect; use i_slint_core::platform::PlatformError; use i_slint_core::renderer::RendererSealed; -use i_slint_core::software_renderer::{ +use slint::platform::software_renderer::{ LineBufferProvider, MinimalSoftwareWindow, RenderingRotation, }; use std::rc::Rc; @@ -31,7 +31,7 @@ impl i_slint_core::platform::Platform for SwrTestingBackend { pub fn init_swr() -> Rc { let window = MinimalSoftwareWindow::new( - i_slint_core::software_renderer::RepaintBufferType::ReusedBuffer, + slint::platform::software_renderer::RepaintBufferType::ReusedBuffer, ); i_slint_core::platform::set_platform(Box::new(SwrTestingBackend { window: window.clone() }))