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