i_slint_core/
graphics.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4#![warn(missing_docs)]
5/*!
6    Graphics Abstractions.
7
8    This module contains the abstractions and convenience types used for rendering.
9*/
10extern crate alloc;
11use crate::api::PlatformError;
12use crate::lengths::LogicalLength;
13use crate::Coord;
14use crate::SharedString;
15use alloc::boxed::Box;
16
17pub use euclid;
18/// 2D Rectangle
19pub type Rect = euclid::default::Rect<Coord>;
20/// 2D Rectangle with integer coordinates
21pub type IntRect = euclid::default::Rect<i32>;
22/// 2D Point
23pub type Point = euclid::default::Point2D<Coord>;
24/// 2D Size
25pub type Size = euclid::default::Size2D<Coord>;
26/// 2D Size in integer coordinates
27pub type IntSize = euclid::default::Size2D<u32>;
28/// 2D Transform
29pub type Transform = euclid::default::Transform2D<Coord>;
30
31pub(crate) mod color;
32pub use color::*;
33
34#[cfg(feature = "std")]
35mod path;
36#[cfg(feature = "std")]
37pub use path::*;
38
39mod brush;
40pub use brush::*;
41
42pub(crate) mod image;
43pub use self::image::*;
44
45pub(crate) mod bitmapfont;
46pub use self::bitmapfont::*;
47
48pub mod rendering_metrics_collector;
49
50#[cfg(feature = "box-shadow-cache")]
51pub mod boxshadowcache;
52
53pub mod border_radius;
54pub use border_radius::*;
55
56#[cfg(feature = "unstable-wgpu-26")]
57pub mod wgpu_26;
58#[cfg(feature = "unstable-wgpu-27")]
59pub mod wgpu_27;
60
61/// CachedGraphicsData allows the graphics backend to store an arbitrary piece of data associated with
62/// an item, which is typically computed by accessing properties. The dependency_tracker is used to allow
63/// for a lazy computation. Typically, back ends store either compute intensive data or handles that refer to
64/// data that's stored in GPU memory.
65pub struct CachedGraphicsData<T> {
66    /// The backend specific data.
67    pub data: T,
68    /// The property tracker that should be used to evaluate whether the primitive needs to be re-created
69    /// or not.
70    pub dependency_tracker: Option<core::pin::Pin<Box<crate::properties::PropertyTracker>>>,
71}
72
73impl<T> CachedGraphicsData<T> {
74    /// Creates a new TrackingRenderingPrimitive by evaluating the provided update_fn once, storing the returned
75    /// rendering primitive and initializing the dependency tracker.
76    pub fn new(update_fn: impl FnOnce() -> T) -> Self {
77        let dependency_tracker = Box::pin(crate::properties::PropertyTracker::default());
78        let data = dependency_tracker.as_ref().evaluate(update_fn);
79        Self { data, dependency_tracker: Some(dependency_tracker) }
80    }
81}
82
83/// FontRequest collects all the developer-configurable properties for fonts, such as family, weight, etc.
84/// It is submitted as a request to the platform font system (i.e. CoreText on macOS) and in exchange the
85/// backend returns a `Box<dyn Font>`.
86#[derive(Debug, Clone, PartialEq, Default)]
87pub struct FontRequest {
88    /// The name of the font family to be used, such as "Helvetica". An empty family name means the system
89    /// default font family should be used.
90    pub family: Option<SharedString>,
91    /// If the weight is None, the system default font weight should be used.
92    pub weight: Option<i32>,
93    /// If the pixel size is None, the system default font size should be used.
94    pub pixel_size: Option<LogicalLength>,
95    /// The additional spacing (or shrinking if negative) between glyphs. This is usually not submitted to
96    /// the font-subsystem but collected here for API convenience
97    pub letter_spacing: Option<LogicalLength>,
98    /// Whether to select an italic face of the font family.
99    pub italic: bool,
100}
101
102#[cfg(feature = "shared-fontique")]
103impl FontRequest {
104    /// Attempts to query the fontique font collection for a matching font.
105    pub fn query_fontique(&self) -> Option<i_slint_common::sharedfontique::fontique::QueryFont> {
106        use i_slint_common::sharedfontique::{self, fontique};
107
108        let mut collection = sharedfontique::get_collection();
109
110        let mut query = collection.query();
111        query.set_families(
112            self.family
113                .as_ref()
114                .map(|family| fontique::QueryFamily::from(family.as_str()))
115                .into_iter()
116                .chain(
117                    [
118                        fontique::QueryFamily::Generic(fontique::GenericFamily::SansSerif),
119                        fontique::QueryFamily::Generic(fontique::GenericFamily::SystemUi),
120                    ]
121                    .into_iter(),
122                ),
123        );
124
125        query.set_attributes(fontique::Attributes {
126            weight: self
127                .weight
128                .as_ref()
129                .map(|&weight| fontique::FontWeight::new(weight as f32))
130                .unwrap_or_default(),
131            style: if self.italic {
132                fontique::FontStyle::Italic
133            } else {
134                fontique::FontStyle::Normal
135            },
136            ..Default::default()
137        });
138
139        let mut font = None;
140
141        query.matches_with(|queried_font| {
142            font = Some(queried_font.clone());
143            fontique::QueryStatus::Stop
144        });
145
146        font
147    }
148}
149
150/// Internal enum to specify which version of OpenGL to request
151/// from the windowing system.
152#[derive(Debug, Clone, PartialEq)]
153pub enum RequestedOpenGLVersion {
154    /// OpenGL
155    OpenGL(Option<(u8, u8)>),
156    /// OpenGL ES
157    OpenGLES(Option<(u8, u8)>),
158}
159
160/// Internal enum specify which graphics API should be used, when
161/// the backend selector requests that from a built-in backend.
162#[derive(Debug, Clone)]
163pub enum RequestedGraphicsAPI {
164    /// OpenGL (ES)
165    OpenGL(RequestedOpenGLVersion),
166    /// Metal
167    Metal,
168    /// Vulkan
169    Vulkan,
170    /// Direct 3D
171    Direct3D,
172    #[cfg(feature = "unstable-wgpu-26")]
173    /// WGPU 26.x
174    WGPU26(wgpu_26::api::WGPUConfiguration),
175    #[cfg(feature = "unstable-wgpu-27")]
176    /// WGPU 27.x
177    WGPU27(wgpu_27::api::WGPUConfiguration),
178}
179
180impl TryFrom<&RequestedGraphicsAPI> for RequestedOpenGLVersion {
181    type Error = PlatformError;
182
183    fn try_from(requested_graphics_api: &RequestedGraphicsAPI) -> Result<Self, Self::Error> {
184        match requested_graphics_api {
185            RequestedGraphicsAPI::OpenGL(requested_open_glversion) => {
186                Ok(requested_open_glversion.clone())
187            }
188            RequestedGraphicsAPI::Metal => {
189                Err("Metal rendering is not supported with an OpenGL renderer".into())
190            }
191            RequestedGraphicsAPI::Vulkan => {
192                Err("Vulkan rendering is not supported with an OpenGL renderer".into())
193            }
194            RequestedGraphicsAPI::Direct3D => {
195                Err("Direct3D rendering is not supported with an OpenGL renderer".into())
196            }
197            #[cfg(feature = "unstable-wgpu-26")]
198            RequestedGraphicsAPI::WGPU26(..) => {
199                Err("WGPU 26.x rendering is not supported with an OpenGL renderer".into())
200            }
201            #[cfg(feature = "unstable-wgpu-27")]
202            RequestedGraphicsAPI::WGPU27(..) => {
203                Err("WGPU 27.x rendering is not supported with an OpenGL renderer".into())
204            }
205        }
206    }
207}
208
209impl From<RequestedOpenGLVersion> for RequestedGraphicsAPI {
210    fn from(version: RequestedOpenGLVersion) -> Self {
211        Self::OpenGL(version)
212    }
213}
214
215/// Private API exposed to just the renderers to create GraphicsAPI instance with
216/// non-exhaustive enum variant.
217#[cfg(feature = "unstable-wgpu-26")]
218pub fn create_graphics_api_wgpu_26(
219    instance: wgpu_26::wgpu::Instance,
220    device: wgpu_26::wgpu::Device,
221    queue: wgpu_26::wgpu::Queue,
222) -> crate::api::GraphicsAPI<'static> {
223    crate::api::GraphicsAPI::WGPU26 { instance, device, queue }
224}
225
226/// Private API exposed to just the renderers to create GraphicsAPI instance with
227/// non-exhaustive enum variant.
228#[cfg(feature = "unstable-wgpu-27")]
229pub fn create_graphics_api_wgpu_27(
230    instance: wgpu_27::wgpu::Instance,
231    device: wgpu_27::wgpu::Device,
232    queue: wgpu_27::wgpu::Queue,
233) -> crate::api::GraphicsAPI<'static> {
234    crate::api::GraphicsAPI::WGPU27 { instance, device, queue }
235}
236
237/// Internal module for use by cbindgen and the C++ platform API layer.
238#[cfg(feature = "ffi")]
239pub mod ffi {
240    #![allow(unsafe_code)]
241
242    /// Expand Rect so that cbindgen can see it. ( is in fact euclid::default::Rect<f32>)
243    #[cfg(cbindgen)]
244    #[repr(C)]
245    struct Rect {
246        x: f32,
247        y: f32,
248        width: f32,
249        height: f32,
250    }
251
252    /// Expand IntRect so that cbindgen can see it. ( is in fact euclid::default::Rect<i32>)
253    #[cfg(cbindgen)]
254    #[repr(C)]
255    struct IntRect {
256        x: i32,
257        y: i32,
258        width: i32,
259        height: i32,
260    }
261
262    /// Expand Point so that cbindgen can see it. ( is in fact euclid::default::Point2D<f32>)
263    #[cfg(cbindgen)]
264    #[repr(C)]
265    struct Point {
266        x: f32,
267        y: f32,
268    }
269
270    /// Expand Box2D so that cbindgen can see it.
271    #[cfg(cbindgen)]
272    #[repr(C)]
273    struct Box2D<T, U> {
274        min: euclid::Point2D<T>,
275        max: euclid::Point2D<T>,
276        _unit: std::marker::PhantomData<U>,
277    }
278
279    #[cfg(feature = "std")]
280    pub use super::path::ffi::*;
281
282    /// Conversion function used by C++ platform API layer to
283    /// convert the PhysicalSize used in the Rust WindowAdapter API
284    /// to the ffi.
285    pub fn physical_size_from_api(
286        size: crate::api::PhysicalSize,
287    ) -> crate::graphics::euclid::default::Size2D<u32> {
288        size.to_euclid()
289    }
290
291    /// Conversion function used by C++ platform API layer to
292    /// convert the PhysicalPosition used in the Rust WindowAdapter API
293    /// to the ffi.
294    pub fn physical_position_from_api(
295        position: crate::api::PhysicalPosition,
296    ) -> crate::graphics::euclid::default::Point2D<i32> {
297        position.to_euclid()
298    }
299
300    /// Conversion function used by C++ platform API layer to
301    /// convert from the ffi to PhysicalPosition.
302    pub fn physical_position_to_api(
303        position: crate::graphics::euclid::default::Point2D<i32>,
304    ) -> crate::api::PhysicalPosition {
305        crate::api::PhysicalPosition::from_euclid(position)
306    }
307}