| 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 | |