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/// 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.
63pub 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
71impl<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`]
86pub struct RenderingCache<T> {
87 slab: slab::Slab<CachedGraphicsData<T>>,
88 generation: usize,
89}
90
91impl<T> Default for RenderingCache<T> {
92 fn default() -> Self {
93 Self { slab: Default::default(), generation: 1 }
94 }
95}
96
97impl<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)]
140pub 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")]
156impl 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)]
171pub 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)]
181pub enum RequestedGraphicsAPI {
182 /// OpenGL (ES)
183 OpenGL(RequestedOpenGLVersion),
184 /// Metal
185 Metal,
186 /// Vulkan
187 Vulkan,
188 /// Direct 3D
189 Direct3D,
190}
191
192impl 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
211impl 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]
223pub 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")]
234pub 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