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