slint/internal/core/graphics/boxshadowcache.rs
2023-06-16 10:55:08 +02:00

111 lines
3.8 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.0 OR LicenseRef-Slint-commercial
/*!
This module contains a cache helper for caching box shadow textures.
*/
use std::{cell::RefCell, collections::BTreeMap};
use super::euclid;
use crate::items::ItemRc;
use crate::{
lengths::{PhysicalPx, ScaleFactor},
Color,
};
/// Struct to store options affecting the rendering of a box shadow
#[derive(Clone, PartialEq, Debug, Default)]
pub struct BoxShadowOptions {
/// The width of the box shadow texture in physical pixels.
pub width: euclid::Length<f32, PhysicalPx>,
/// The height of the box shadow texture in physical pixels.
pub height: euclid::Length<f32, PhysicalPx>,
/// The color for the box shadow.
pub color: Color,
/// The blur to apply to the box shadow in pixels.
pub blur: euclid::Length<f32, PhysicalPx>,
/// The radius of the box shadow.
pub radius: euclid::Length<f32, PhysicalPx>,
}
impl Eq for BoxShadowOptions {}
impl Ord for BoxShadowOptions {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if (other.width, other.height, other.color, other.blur, other.radius)
< (self.width, self.height, self.color, self.blur, self.radius)
{
std::cmp::Ordering::Less
} else if (self.width, self.height, self.color, self.blur, self.radius)
< (other.width, other.height, other.color, other.blur, other.radius)
{
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
}
impl PartialOrd for BoxShadowOptions {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl BoxShadowOptions {
/// Extracts the rendering specific properties from the BoxShadow item and scales the logical
/// coordinates to physical pixels used in the BoxShadowOptions. Returns None if for example the
/// alpha on the box shadow would imply that no shadow is to be rendered.
pub fn new(
box_shadow: std::pin::Pin<&crate::items::BoxShadow>,
scale_factor: ScaleFactor,
) -> Option<Self> {
let color = box_shadow.color();
if color.alpha() == 0 {
return None;
}
let width = box_shadow.width() * scale_factor;
let height = box_shadow.height() * scale_factor;
if width.get() < 1. || height.get() < 1. {
return None;
}
Some(Self {
width,
height,
color,
blur: box_shadow.blur() * scale_factor, // This effectively becomes the blur radius, so scale to physical pixels
radius: box_shadow.border_radius() * scale_factor,
})
}
}
/// Cache to hold box textures for given box shadow options.
pub struct BoxShadowCache<ImageType>(RefCell<BTreeMap<BoxShadowOptions, ImageType>>);
impl<ImageType> Default for BoxShadowCache<ImageType> {
fn default() -> Self {
Self(Default::default())
}
}
impl<ImageType: Clone> BoxShadowCache<ImageType> {
/// Look up a box shadow texture for a given box shadow item, or create a new one if needed.
pub fn get_box_shadow(
&self,
item_rc: &ItemRc,
item_cache: &crate::item_rendering::ItemCache<Option<ImageType>>,
box_shadow: std::pin::Pin<&crate::items::BoxShadow>,
scale_factor: ScaleFactor,
shadow_render_fn: impl FnOnce(&BoxShadowOptions) -> ImageType,
) -> Option<ImageType> {
item_cache.get_or_update_cache_entry(item_rc, || {
let shadow_options = BoxShadowOptions::new(box_shadow, scale_factor)?;
self.0
.borrow_mut()
.entry(shadow_options.clone())
.or_insert_with(|| shadow_render_fn(&shadow_options))
.clone()
.into()
})
}
}