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