// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // cSpell: ignore bitmapfont glversion #![allow(unsafe_code)] #![warn(missing_docs)] /*! Graphics Abstractions. This module contains the abstractions and convenience types used for rendering. */ extern crate alloc; use crate::Coord; use crate::SharedString; use crate::api::PlatformError; use crate::lengths::LogicalLength; use alloc::boxed::Box; pub use euclid; /// 2D Rectangle pub type Rect = euclid::default::Rect; /// 2D Rectangle with integer coordinates pub type IntRect = euclid::default::Rect; /// 2D Point pub type Point = euclid::default::Point2D; /// 2D Size pub type Size = euclid::default::Size2D; /// 2D Size in integer coordinates pub type IntSize = euclid::default::Size2D; /// 2D Transform pub type Transform = euclid::default::Transform2D; pub(crate) mod color; pub use color::*; #[cfg(feature = "shared-fontique")] use i_slint_common::sharedfontique::{self, fontique}; #[cfg(feature = "path")] mod path; #[cfg(feature = "path")] pub use path::*; mod brush; pub use brush::*; pub(crate) mod image; pub use self::image::*; pub(crate) mod bitmapfont; pub use self::bitmapfont::*; pub mod rendering_metrics_collector; #[cfg(feature = "box-shadow-cache")] pub mod boxshadowcache; pub mod border_radius; pub use border_radius::*; #[cfg(feature = "wgpu-29")] pub mod wgpu_29; #[cfg(feature = "wgpu-30")] pub mod wgpu_30; /// Adjusts a rectangle and a border width for drawing the border entirely inside the /// rectangle's geometry: renderers stroke a border centered on the path, so the rectangle /// is inset by half the border width. If the border width exceeds half of the rectangle's /// width, it is clamped so that the border just fills the rectangle. pub fn adjust_rect_and_border_for_inner_drawing( rect: &mut euclid::Rect, border_width: &mut euclid::Length, ) { use crate::lengths::RectLengths; // If the border width exceeds the width, just fill the rectangle. *border_width = border_width.min(rect.width_length() / 2.); // adjust the size so that the border is drawn within the geometry rect.origin += euclid::Size2D::from_lengths(*border_width / 2., *border_width / 2.); rect.size -= euclid::Size2D::from_lengths(*border_width, *border_width); } /// CachedGraphicsData allows the graphics backend to store an arbitrary piece of data associated with /// an item, which is typically computed by accessing properties. The dependency_tracker is used to allow /// for a lazy computation. Typically, back ends store either compute intensive data or handles that refer to /// data that's stored in GPU memory. pub struct CachedGraphicsData { /// The backend specific data. pub data: T, /// The property tracker that should be used to evaluate whether the primitive needs to be re-created /// or not. pub dependency_tracker: Option>>, } impl CachedGraphicsData { /// Creates a new TrackingRenderingPrimitive by evaluating the provided update_fn once, storing the returned /// rendering primitive and initializing the dependency tracker. pub fn new(update_fn: impl FnOnce() -> T) -> Self { let dependency_tracker = Box::pin(crate::properties::PropertyTracker::default()); let data = dependency_tracker.as_ref().evaluate(update_fn); Self { data, dependency_tracker: Some(dependency_tracker) } } } /// FontRequest collects all the developer-configurable properties for fonts, such as family, weight, etc. /// It is submitted as a request to the platform font system (i.e. CoreText on macOS) and in exchange the /// backend returns a `Box`. #[derive(Debug, Clone, PartialEq, Default)] pub struct FontRequest { /// The name of the font family to be used, such as "Helvetica". An empty family name means the system /// default font family should be used. pub family: Option, /// If the weight is None, the system default font weight should be used. pub weight: Option, /// If the pixel size is None, the system default font size should be used. pub pixel_size: Option, /// The additional spacing (or shrinking if negative) between glyphs. This is usually not submitted to /// the font-subsystem but collected here for API convenience pub letter_spacing: Option, /// The line height as a factor applied to the font's natural line height. /// `None` uses the natural line height unchanged (a factor of 1). pub line_height_factor: Option, /// Whether to select an italic face of the font family. pub italic: bool, } impl FontRequest { /// Returns the configured line height given the font's natural line height /// (in any unit), or `None` when the natural line height applies unchanged. pub fn line_height_for_natural_height(&self, natural_line_height: f32) -> Option { self.line_height_factor.map(|factor| natural_line_height * factor) } } #[cfg(feature = "shared-fontique")] impl FontRequest { /// Attempts to query the fontique font collection for a matching font. pub fn query_fontique( &self, collection: &mut fontique::Collection, source_cache: &mut fontique::SourceCache, ) -> Option { let mut query = collection.query(source_cache); query.set_families( self.family .as_ref() .map(|family| fontique::QueryFamily::from(family.as_str())) .into_iter() .chain( sharedfontique::FALLBACK_FAMILIES .into_iter() .map(fontique::QueryFamily::Generic), ), ); query.set_attributes(fontique::Attributes { weight: self .weight .as_ref() .map(|&weight| fontique::FontWeight::new(weight as f32)) .unwrap_or_default(), style: if self.italic { fontique::FontStyle::Italic } else { fontique::FontStyle::Normal }, ..Default::default() }); let mut font = None; query.matches_with(|queried_font| { font = Some(queried_font.clone()); fontique::QueryStatus::Stop }); font } } /// Internal enum to specify which version of OpenGL to request /// from the windowing system. #[derive(Debug, Clone, PartialEq)] pub enum RequestedOpenGLVersion { /// OpenGL OpenGL(Option<(u8, u8)>), /// OpenGL ES OpenGLES(Option<(u8, u8)>), } /// Internal enum specify which graphics API should be used, when /// the backend selector requests that from a built-in backend. #[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] pub enum RequestedGraphicsAPI { /// OpenGL (ES) OpenGL(RequestedOpenGLVersion), /// Metal Metal, /// Vulkan Vulkan, /// Direct 3D Direct3D, #[cfg(feature = "unstable-wgpu-29")] /// WGPU 29.x WGPU29(wgpu_29::api::WGPUConfiguration), #[cfg(feature = "unstable-wgpu-30")] /// WGPU 30.x WGPU30(wgpu_30::api::WGPUConfiguration), } impl TryFrom<&RequestedGraphicsAPI> for RequestedOpenGLVersion { type Error = PlatformError; fn try_from(requested_graphics_api: &RequestedGraphicsAPI) -> Result { match requested_graphics_api { RequestedGraphicsAPI::OpenGL(requested_open_glversion) => { Ok(requested_open_glversion.clone()) } RequestedGraphicsAPI::Metal => { Err("Metal rendering is not supported with an OpenGL renderer".into()) } RequestedGraphicsAPI::Vulkan => { Err("Vulkan rendering is not supported with an OpenGL renderer".into()) } RequestedGraphicsAPI::Direct3D => { Err("Direct3D rendering is not supported with an OpenGL renderer".into()) } #[cfg(feature = "unstable-wgpu-29")] RequestedGraphicsAPI::WGPU29(..) => { Err("WGPU 29.x rendering is not supported with an OpenGL renderer".into()) } #[cfg(feature = "unstable-wgpu-30")] RequestedGraphicsAPI::WGPU30(..) => { Err("WGPU 30.x rendering is not supported with an OpenGL renderer".into()) } } } } impl From for RequestedGraphicsAPI { fn from(version: RequestedOpenGLVersion) -> Self { Self::OpenGL(version) } } /// Private API exposed to just the renderers to create GraphicsAPI instance with /// non-exhaustive enum variant. #[cfg(feature = "unstable-wgpu-29")] pub fn create_graphics_api_wgpu_29( instance: wgpu_29::wgpu::Instance, device: wgpu_29::wgpu::Device, queue: wgpu_29::wgpu::Queue, ) -> crate::api::GraphicsAPI<'static> { crate::api::GraphicsAPI::WGPU29 { instance, device, queue } } /// Private API exposed to just the renderers to create GraphicsAPI instance with /// non-exhaustive enum variant. #[cfg(feature = "unstable-wgpu-30")] pub fn create_graphics_api_wgpu_30( instance: wgpu_30::wgpu::Instance, device: wgpu_30::wgpu::Device, queue: wgpu_30::wgpu::Queue, ) -> crate::api::GraphicsAPI<'static> { crate::api::GraphicsAPI::WGPU30 { instance, device, queue } } /// Internal module for use by cbindgen and the C++ platform API layer. #[cfg(feature = "ffi")] pub mod ffi { #![allow(unsafe_code)] /// Expand Rect so that cbindgen can see it. ( is in fact euclid::default::Rect) #[cfg(cbindgen)] #[repr(C)] struct Rect { x: f32, y: f32, width: f32, height: f32, } /// Expand IntRect so that cbindgen can see it. ( is in fact euclid::default::Rect) #[cfg(cbindgen)] #[repr(C)] struct IntRect { x: i32, y: i32, width: i32, height: i32, } /// Expand Point so that cbindgen can see it. ( is in fact euclid::default::Point2D) #[cfg(cbindgen)] #[repr(C)] struct Point { x: f32, y: f32, } /// Expand Box2D so that cbindgen can see it. #[cfg(cbindgen)] #[repr(C)] struct Box2D { min: euclid::Point2D, max: euclid::Point2D, _unit: std::marker::PhantomData, } #[cfg(feature = "std")] pub use super::path::ffi::*; /// Conversion function used by C++ platform API layer to /// convert the PhysicalSize used in the Rust WindowAdapter API /// to the ffi. pub fn physical_size_from_api( size: crate::api::PhysicalSize, ) -> crate::graphics::euclid::default::Size2D { size.to_euclid() } /// Conversion function used by C++ platform API layer to /// convert the PhysicalPosition used in the Rust WindowAdapter API /// to the ffi. pub fn physical_position_from_api( position: crate::api::PhysicalPosition, ) -> crate::graphics::euclid::default::Point2D { position.to_euclid() } /// Conversion function used by C++ platform API layer to /// convert from the ffi to PhysicalPosition. pub fn physical_position_to_api( position: crate::graphics::euclid::default::Point2D, ) -> crate::api::PhysicalPosition { crate::api::PhysicalPosition::from_euclid(position) } }