#![cfg_attr(windows, allow(dead_code))]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, DevicePixels, Edges, Hsla,
Pixels, Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
};
use std::{
fmt::Debug,
iter::Peekable,
ops::{Add, Range, Sub},
slice,
sync::Arc,
};
#[allow(non_camel_case_types, unused)]
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub(crate) type DrawOrder = u32;
#[derive(Default)]
pub(crate) struct Scene {
pub(crate) paint_operations: Vec<PaintOperation>,
primitive_bounds: BoundsTree<ScaledPixels>,
layer_stack: Vec<DrawOrder>,
pub(crate) shadows: Vec<Shadow>,
pub(crate) blur_rects: Vec<BlurRect>,
pub(crate) quads: Vec<Quad>,
pub(crate) paths: Vec<Path<ScaledPixels>>,
pub(crate) underlines: Vec<Underline>,
pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
pub(crate) surfaces: Vec<PaintSurface>,
pub(crate) cached_surface_snapshots: Vec<CachedSurfaceSnapshot>,
}
impl Scene {
pub fn clear(&mut self) {
self.paint_operations.clear();
self.primitive_bounds.clear();
self.layer_stack.clear();
self.paths.clear();
self.shadows.clear();
self.blur_rects.clear();
self.quads.clear();
self.underlines.clear();
self.monochrome_sprites.clear();
self.polychrome_sprites.clear();
self.surfaces.clear();
self.cached_surface_snapshots.clear();
}
pub fn len(&self) -> usize {
self.paint_operations.len()
}
pub(crate) fn structural_checksum(&self) -> u64 {
let mut hash = 0xcbf2_9ce4_8422_2325u64;
let mut mix = |value: u64| {
hash ^= value;
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
};
mix(self.shadows.len() as u64);
mix(self.blur_rects.len() as u64);
mix(self.quads.len() as u64);
mix(self.paths.len() as u64);
mix(self.underlines.len() as u64);
mix(self.monochrome_sprites.len() as u64);
mix(self.polychrome_sprites.len() as u64);
mix(self.surfaces.len() as u64);
for quad in &self.quads {
mix(quad.bounds.origin.x.0.to_bits() as u64);
mix(quad.bounds.origin.y.0.to_bits() as u64);
mix(quad.bounds.size.width.0.to_bits() as u64);
mix(quad.bounds.size.height.0.to_bits() as u64);
}
hash
}
pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
let order = self.primitive_bounds.insert(bounds);
self.layer_stack.push(order);
self.paint_operations
.push(PaintOperation::StartLayer(bounds));
}
pub fn pop_layer(&mut self) {
self.layer_stack.pop();
self.paint_operations.push(PaintOperation::EndLayer);
}
pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
let primitive = primitive.into();
let clipped_bounds = primitive
.bounds()
.intersect(&primitive.content_mask().bounds);
if clipped_bounds.is_empty() {
return;
}
let order = self
.layer_stack
.last()
.copied()
.unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
let (kind, index) = match primitive {
Primitive::Shadow(mut shadow) => {
shadow.order = order;
let idx = self.shadows.len();
self.shadows.push(shadow);
(PrimitiveKind::Shadow, idx)
}
Primitive::BlurRect(mut blur_rect) => {
blur_rect.order = order;
let idx = self.blur_rects.len();
self.blur_rects.push(blur_rect);
(PrimitiveKind::BlurRect, idx)
}
Primitive::Quad(mut quad) => {
quad.order = order;
let idx = self.quads.len();
self.quads.push(quad);
(PrimitiveKind::Quad, idx)
}
Primitive::Path(mut path) => {
path.order = order;
path.id = PathId(self.paths.len());
let idx = self.paths.len();
self.paths.push(path);
(PrimitiveKind::Path, idx)
}
Primitive::Underline(mut underline) => {
underline.order = order;
let idx = self.underlines.len();
self.underlines.push(underline);
(PrimitiveKind::Underline, idx)
}
Primitive::MonochromeSprite(mut sprite) => {
sprite.order = order;
let idx = self.monochrome_sprites.len();
self.monochrome_sprites.push(sprite);
(PrimitiveKind::MonochromeSprite, idx)
}
Primitive::PolychromeSprite(mut sprite) => {
sprite.order = order;
let idx = self.polychrome_sprites.len();
self.polychrome_sprites.push(sprite);
(PrimitiveKind::PolychromeSprite, idx)
}
Primitive::Surface(mut surface) => {
surface.order = order;
let idx = self.surfaces.len();
self.surfaces.push(surface);
(PrimitiveKind::Surface, idx)
}
};
self.paint_operations
.push(PaintOperation::Primitive(kind, index));
}
pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
for operation in &prev_scene.paint_operations[range] {
match operation {
PaintOperation::Primitive(kind, index) => {
let primitive = match kind {
PrimitiveKind::Shadow => {
Primitive::Shadow(prev_scene.shadows[*index].clone())
}
PrimitiveKind::BlurRect => {
Primitive::BlurRect(prev_scene.blur_rects[*index].clone())
}
PrimitiveKind::Quad => Primitive::Quad(prev_scene.quads[*index].clone()),
PrimitiveKind::Path => Primitive::Path(prev_scene.paths[*index].clone()),
PrimitiveKind::Underline => {
Primitive::Underline(prev_scene.underlines[*index].clone())
}
PrimitiveKind::MonochromeSprite => Primitive::MonochromeSprite(
prev_scene.monochrome_sprites[*index].clone(),
),
PrimitiveKind::PolychromeSprite => Primitive::PolychromeSprite(
prev_scene.polychrome_sprites[*index].clone(),
),
PrimitiveKind::Surface => {
Primitive::Surface(prev_scene.surfaces[*index].clone())
}
};
self.insert_primitive(primitive);
}
PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
PaintOperation::EndLayer => self.pop_layer(),
}
}
}
pub fn finish(&mut self) {
if !self.shadows.is_sorted_by_key(|s| s.order) {
self.shadows.sort_unstable_by_key(|s| s.order);
}
if !self.blur_rects.is_sorted_by_key(|b| b.order) {
self.blur_rects.sort_unstable_by_key(|b| b.order);
}
if !self.quads.is_sorted_by_key(|q| q.order) {
self.quads.sort_unstable_by_key(|q| q.order);
}
if !self.paths.is_sorted_by_key(|p| p.order) {
self.paths.sort_unstable_by_key(|p| p.order);
}
if !self.underlines.is_sorted_by_key(|u| u.order) {
self.underlines.sort_unstable_by_key(|u| u.order);
}
if !self
.monochrome_sprites
.is_sorted_by_key(|s| (s.order, s.tile.tile_id))
{
self.monochrome_sprites
.sort_unstable_by_key(|s| (s.order, s.tile.tile_id));
}
if !self
.polychrome_sprites
.is_sorted_by_key(|s| (s.order, s.tile.tile_id))
{
self.polychrome_sprites
.sort_unstable_by_key(|s| (s.order, s.tile.tile_id));
}
if !self.surfaces.is_sorted_by_key(|s| s.order) {
self.surfaces.sort_unstable_by_key(|s| s.order);
}
}
pub(crate) fn request_cached_surface_snapshot(&mut self, snapshot: CachedSurfaceSnapshot) {
self.cached_surface_snapshots.push(snapshot);
}
pub(crate) fn snapshot_subscene(&self, paint_operations: Range<usize>) -> Scene {
let mut scene = Scene::default();
scene.replay(paint_operations, self);
scene.finish();
scene
}
#[cfg_attr(
all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code)
)]
pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
BatchIterator {
shadows: &self.shadows,
shadows_start: 0,
shadows_iter: self.shadows.iter().peekable(),
blur_rects: &self.blur_rects,
blur_rects_start: 0,
blur_rects_iter: self.blur_rects.iter().peekable(),
quads: &self.quads,
quads_start: 0,
quads_iter: self.quads.iter().peekable(),
paths: &self.paths,
paths_start: 0,
paths_iter: self.paths.iter().peekable(),
underlines: &self.underlines,
underlines_start: 0,
underlines_iter: self.underlines.iter().peekable(),
monochrome_sprites: &self.monochrome_sprites,
monochrome_sprites_start: 0,
monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
polychrome_sprites: &self.polychrome_sprites,
polychrome_sprites_start: 0,
polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
surfaces: &self.surfaces,
surfaces_start: 0,
surfaces_iter: self.surfaces.iter().peekable(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
#[cfg_attr(
all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code)
)]
pub(crate) enum PrimitiveKind {
Shadow,
BlurRect,
#[default]
Quad,
Path,
Underline,
MonochromeSprite,
PolychromeSprite,
Surface,
}
pub(crate) enum PaintOperation {
Primitive(PrimitiveKind, usize),
StartLayer(Bounds<ScaledPixels>),
EndLayer,
}
#[derive(Clone)]
pub(crate) enum Primitive {
Shadow(Shadow),
BlurRect(BlurRect),
Quad(Quad),
Path(Path<ScaledPixels>),
Underline(Underline),
MonochromeSprite(MonochromeSprite),
PolychromeSprite(PolychromeSprite),
Surface(PaintSurface),
}
impl Primitive {
pub fn bounds(&self) -> &Bounds<ScaledPixels> {
match self {
Primitive::Shadow(shadow) => &shadow.bounds,
Primitive::BlurRect(blur_rect) => &blur_rect.bounds,
Primitive::Quad(quad) => &quad.bounds,
Primitive::Path(path) => &path.bounds,
Primitive::Underline(underline) => &underline.bounds,
Primitive::MonochromeSprite(sprite) => &sprite.bounds,
Primitive::PolychromeSprite(sprite) => &sprite.bounds,
Primitive::Surface(surface) => &surface.bounds,
}
}
pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
match self {
Primitive::Shadow(shadow) => &shadow.content_mask,
Primitive::BlurRect(blur_rect) => &blur_rect.content_mask,
Primitive::Quad(quad) => &quad.content_mask,
Primitive::Path(path) => &path.content_mask,
Primitive::Underline(underline) => &underline.content_mask,
Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
Primitive::Surface(surface) => &surface.content_mask,
}
}
}
#[cfg_attr(
all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code)
)]
struct BatchIterator<'a> {
shadows: &'a [Shadow],
shadows_start: usize,
shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
blur_rects: &'a [BlurRect],
blur_rects_start: usize,
blur_rects_iter: Peekable<slice::Iter<'a, BlurRect>>,
quads: &'a [Quad],
quads_start: usize,
quads_iter: Peekable<slice::Iter<'a, Quad>>,
paths: &'a [Path<ScaledPixels>],
paths_start: usize,
paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
underlines: &'a [Underline],
underlines_start: usize,
underlines_iter: Peekable<slice::Iter<'a, Underline>>,
monochrome_sprites: &'a [MonochromeSprite],
monochrome_sprites_start: usize,
monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
polychrome_sprites: &'a [PolychromeSprite],
polychrome_sprites_start: usize,
polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
surfaces: &'a [PaintSurface],
surfaces_start: usize,
surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
}
impl<'a> Iterator for BatchIterator<'a> {
type Item = PrimitiveBatch<'a>;
fn next(&mut self) -> Option<Self::Item> {
let mut orders_and_kinds = [
(
self.shadows_iter.peek().map(|s| s.order),
PrimitiveKind::Shadow,
),
(
self.blur_rects_iter.peek().map(|b| b.order),
PrimitiveKind::BlurRect,
),
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
(self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
(
self.underlines_iter.peek().map(|u| u.order),
PrimitiveKind::Underline,
),
(
self.monochrome_sprites_iter.peek().map(|s| s.order),
PrimitiveKind::MonochromeSprite,
),
(
self.polychrome_sprites_iter.peek().map(|s| s.order),
PrimitiveKind::PolychromeSprite,
),
(
self.surfaces_iter.peek().map(|s| s.order),
PrimitiveKind::Surface,
),
];
orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
let first = orders_and_kinds[0];
let second = orders_and_kinds[1];
let (batch_kind, max_order_and_kind) = if first.0.is_some() {
(first.1, (second.0.unwrap_or(u32::MAX), second.1))
} else {
return None;
};
match batch_kind {
PrimitiveKind::Shadow => {
let shadows_start = self.shadows_start;
let mut shadows_end = shadows_start + 1;
self.shadows_iter.next();
while self
.shadows_iter
.next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
.is_some()
{
shadows_end += 1;
}
self.shadows_start = shadows_end;
Some(PrimitiveBatch::Shadows(
&self.shadows[shadows_start..shadows_end],
))
}
PrimitiveKind::BlurRect => {
let blur_rects_start = self.blur_rects_start;
let mut blur_rects_end = blur_rects_start + 1;
self.blur_rects_iter.next();
while self
.blur_rects_iter
.next_if(|blur_rect| (blur_rect.order, batch_kind) < max_order_and_kind)
.is_some()
{
blur_rects_end += 1;
}
self.blur_rects_start = blur_rects_end;
Some(PrimitiveBatch::BlurRects(
&self.blur_rects[blur_rects_start..blur_rects_end],
))
}
PrimitiveKind::Quad => {
let quads_start = self.quads_start;
let mut quads_end = quads_start + 1;
self.quads_iter.next();
while self
.quads_iter
.next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
.is_some()
{
quads_end += 1;
}
self.quads_start = quads_end;
Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
}
PrimitiveKind::Path => {
let paths_start = self.paths_start;
let mut paths_end = paths_start + 1;
self.paths_iter.next();
while self
.paths_iter
.next_if(|path| (path.order, batch_kind) < max_order_and_kind)
.is_some()
{
paths_end += 1;
}
self.paths_start = paths_end;
Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
}
PrimitiveKind::Underline => {
let underlines_start = self.underlines_start;
let mut underlines_end = underlines_start + 1;
self.underlines_iter.next();
while self
.underlines_iter
.next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
.is_some()
{
underlines_end += 1;
}
self.underlines_start = underlines_end;
Some(PrimitiveBatch::Underlines(
&self.underlines[underlines_start..underlines_end],
))
}
PrimitiveKind::MonochromeSprite => {
let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
let sprites_start = self.monochrome_sprites_start;
let mut sprites_end = sprites_start + 1;
self.monochrome_sprites_iter.next();
while self
.monochrome_sprites_iter
.next_if(|sprite| {
(sprite.order, batch_kind) < max_order_and_kind
&& sprite.tile.texture_id == texture_id
})
.is_some()
{
sprites_end += 1;
}
self.monochrome_sprites_start = sprites_end;
Some(PrimitiveBatch::MonochromeSprites {
texture_id,
sprites: &self.monochrome_sprites[sprites_start..sprites_end],
})
}
PrimitiveKind::PolychromeSprite => {
let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
let sprites_start = self.polychrome_sprites_start;
let mut sprites_end = self.polychrome_sprites_start + 1;
self.polychrome_sprites_iter.next();
while self
.polychrome_sprites_iter
.next_if(|sprite| {
(sprite.order, batch_kind) < max_order_and_kind
&& sprite.tile.texture_id == texture_id
})
.is_some()
{
sprites_end += 1;
}
self.polychrome_sprites_start = sprites_end;
Some(PrimitiveBatch::PolychromeSprites {
texture_id,
sprites: &self.polychrome_sprites[sprites_start..sprites_end],
})
}
PrimitiveKind::Surface => {
let surfaces_start = self.surfaces_start;
let mut surfaces_end = surfaces_start + 1;
self.surfaces_iter.next();
while self
.surfaces_iter
.next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
.is_some()
{
surfaces_end += 1;
}
self.surfaces_start = surfaces_end;
Some(PrimitiveBatch::Surfaces(
&self.surfaces[surfaces_start..surfaces_end],
))
}
}
}
}
#[derive(Debug)]
#[cfg_attr(
all(
any(target_os = "linux", target_os = "freebsd"),
not(any(feature = "x11", feature = "wayland"))
),
allow(dead_code)
)]
pub(crate) enum PrimitiveBatch<'a> {
Shadows(&'a [Shadow]),
BlurRects(&'a [BlurRect]),
Quads(&'a [Quad]),
Paths(&'a [Path<ScaledPixels>]),
Underlines(&'a [Underline]),
MonochromeSprites {
texture_id: AtlasTextureId,
sprites: &'a [MonochromeSprite],
},
PolychromeSprites {
texture_id: AtlasTextureId,
sprites: &'a [PolychromeSprite],
},
Surfaces(&'a [PaintSurface]),
}
#[derive(Default, Debug, Clone)]
#[repr(C)]
pub(crate) struct Quad {
pub order: DrawOrder,
pub border_style: BorderStyle,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub background: Background,
pub border_color: Hsla,
pub corner_radii: Corners<ScaledPixels>,
pub border_widths: Edges<ScaledPixels>,
pub continuous_corners: u32,
pub transform: TransformationMatrix,
pub blend_mode: u32,
}
impl From<Quad> for Primitive {
fn from(quad: Quad) -> Self {
Primitive::Quad(quad)
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub(crate) struct Underline {
pub order: DrawOrder,
pub pad: u32, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub color: Hsla,
pub thickness: ScaledPixels,
pub wavy: u32,
}
impl From<Underline> for Primitive {
fn from(underline: Underline) -> Self {
Primitive::Underline(underline)
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub(crate) struct Shadow {
pub order: DrawOrder,
pub blur_radius: ScaledPixels,
pub bounds: Bounds<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub color: Hsla,
pub inset: u32,
}
impl From<Shadow> for Primitive {
fn from(shadow: Shadow) -> Self {
Primitive::Shadow(shadow)
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub(crate) struct BlurRect {
pub order: DrawOrder,
pub blur_radius: ScaledPixels,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
pub tint: Hsla,
pub saturation: f32,
}
impl From<BlurRect> for Primitive {
fn from(blur_rect: BlurRect) -> Self {
Primitive::BlurRect(blur_rect)
}
}
impl BlurRect {
pub(crate) fn capture_bounds(&self, viewport_size: Size<DevicePixels>) -> Bounds<ScaledPixels> {
let margin = ScaledPixels((self.blur_radius.0 * 3.0).ceil());
let viewport_bounds = Bounds {
origin: point(ScaledPixels::default(), ScaledPixels::default()),
size: Size {
width: ScaledPixels::from(viewport_size.width),
height: ScaledPixels::from(viewport_size.height),
},
};
self.bounds
.dilate(margin)
.intersect(&viewport_bounds)
.map_origin(|origin| origin.floor())
.map_size(|size| size.ceil())
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[repr(C)]
pub enum BlendMode {
#[default]
Normal = 0,
Multiply = 1,
Screen = 2,
Overlay = 3,
SoftLight = 4,
Difference = 5,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[repr(C)]
pub enum BorderStyle {
#[default]
Solid = 0,
Dashed = 1,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct TransformationMatrix {
pub rotation_scale: [[f32; 2]; 2],
pub translation: [f32; 2],
}
impl Eq for TransformationMatrix {}
impl TransformationMatrix {
pub fn unit() -> Self {
Self {
rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
translation: [0.0, 0.0],
}
}
pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
self.compose(Self {
rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
translation: [point.x.0, point.y.0],
})
}
pub fn rotate(self, angle: Radians) -> Self {
self.compose(Self {
rotation_scale: [
[angle.0.cos(), -angle.0.sin()],
[angle.0.sin(), angle.0.cos()],
],
translation: [0.0, 0.0],
})
}
pub fn scale(self, size: Size<f32>) -> Self {
self.compose(Self {
rotation_scale: [[size.width, 0.0], [0.0, size.height]],
translation: [0.0, 0.0],
})
}
#[inline]
pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
if other == Self::unit() {
return self;
}
TransformationMatrix {
rotation_scale: [
[
self.rotation_scale[0][0] * other.rotation_scale[0][0]
+ self.rotation_scale[0][1] * other.rotation_scale[1][0],
self.rotation_scale[0][0] * other.rotation_scale[0][1]
+ self.rotation_scale[0][1] * other.rotation_scale[1][1],
],
[
self.rotation_scale[1][0] * other.rotation_scale[0][0]
+ self.rotation_scale[1][1] * other.rotation_scale[1][0],
self.rotation_scale[1][0] * other.rotation_scale[0][1]
+ self.rotation_scale[1][1] * other.rotation_scale[1][1],
],
],
translation: [
self.translation[0]
+ self.rotation_scale[0][0] * other.translation[0]
+ self.rotation_scale[0][1] * other.translation[1],
self.translation[1]
+ self.rotation_scale[1][0] * other.translation[0]
+ self.rotation_scale[1][1] * other.translation[1],
],
}
}
pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
let input = [point.x.0, point.y.0];
let mut output = self.translation;
for (i, output_cell) in output.iter_mut().enumerate() {
for (k, input_cell) in input.iter().enumerate() {
*output_cell += self.rotation_scale[i][k] * *input_cell;
}
}
Point::new(output[0].into(), output[1].into())
}
}
impl Default for TransformationMatrix {
fn default() -> Self {
Self::unit()
}
}
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct MonochromeSprite {
pub order: DrawOrder,
pub pad: u32, pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub color: Hsla,
pub tile: AtlasTile,
pub transformation: TransformationMatrix,
}
impl From<MonochromeSprite> for Primitive {
fn from(sprite: MonochromeSprite) -> Self {
Primitive::MonochromeSprite(sprite)
}
}
pub(crate) const POLYCHROME_SPRITE_KIND_COLOR: u32 = 0;
pub(crate) const POLYCHROME_SPRITE_KIND_SUBPIXEL_TEXT: u32 = 1;
pub(crate) const POLYCHROME_SPRITE_KIND_PREMULTIPLIED: u32 = 2;
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct PolychromeSprite {
pub order: DrawOrder,
pub pad: u32, pub grayscale: bool,
pub opacity: f32,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
pub corner_radii: Corners<ScaledPixels>,
pub tile: AtlasTile,
pub sprite_kind: u32,
pub color: Hsla,
}
impl From<PolychromeSprite> for Primitive {
fn from(sprite: PolychromeSprite) -> Self {
Primitive::PolychromeSprite(sprite)
}
}
#[derive(Clone, Debug)]
pub(crate) struct PaintSurface {
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
#[cfg(target_os = "macos")]
pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
}
#[derive(Clone, Debug)]
pub(crate) struct CachedSurfaceSnapshot {
pub paint_operations: Range<usize>,
pub source_bounds: Bounds<DevicePixels>,
pub target: AtlasTile,
}
impl From<PaintSurface> for Primitive {
fn from(surface: PaintSurface) -> Self {
Primitive::Surface(surface)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct PathId(pub(crate) usize);
#[derive(Clone, Debug)]
pub struct Path<P: Clone + Debug + Default + PartialEq> {
pub(crate) id: PathId,
pub(crate) order: DrawOrder,
pub(crate) bounds: Bounds<P>,
pub(crate) content_mask: ContentMask<P>,
pub(crate) vertices: Vec<PathVertex<P>>,
pub(crate) color: Background,
pub(crate) source_path: Option<Arc<lyon::path::Path>>,
start: Point<P>,
current: Point<P>,
contour_count: usize,
}
impl Path<Pixels> {
pub fn new(start: Point<Pixels>) -> Self {
Self {
id: PathId(0),
order: DrawOrder::default(),
vertices: Vec::new(),
start,
current: start,
bounds: Bounds {
origin: start,
size: Default::default(),
},
content_mask: Default::default(),
color: Default::default(),
source_path: None,
contour_count: 0,
}
}
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
Path {
id: self.id,
order: self.order,
bounds: self.bounds.scale_and_snap_conservative(factor),
content_mask: self.content_mask.scale(factor),
vertices: self
.vertices
.iter()
.map(|vertex| vertex.scale(factor))
.collect(),
start: self.start.map(|start| start.scale(factor)),
current: self.current.scale(factor),
contour_count: self.contour_count,
color: self.color,
source_path: self.source_path.clone(),
}
}
pub(crate) fn with_source_path(mut self, source_path: Arc<lyon::path::Path>) -> Self {
self.source_path = Some(source_path);
self
}
pub(crate) fn source_path(&self) -> Option<&lyon::path::Path> {
self.source_path.as_deref()
}
pub(crate) fn transformed(&self, transform: TransformationMatrix) -> Self {
if transform == TransformationMatrix::unit() {
return self.clone();
}
let mut transformed = self.clone();
let mut bounds = Bounds::default();
for vertex in &mut transformed.vertices {
vertex.xy_position = transform.apply(vertex.xy_position);
bounds = bounds.union(&Bounds {
origin: vertex.xy_position,
size: Default::default(),
});
}
transformed.start = transform.apply(self.start);
transformed.current = transform.apply(self.current);
transformed.bounds = bounds;
transformed
}
pub fn move_to(&mut self, to: Point<Pixels>) {
self.contour_count += 1;
self.start = to;
self.current = to;
}
pub fn line_to(&mut self, to: Point<Pixels>) {
self.contour_count += 1;
if self.contour_count > 1 {
self.push_triangle(
(self.start, self.current, to),
(point(0., 1.), point(0., 1.), point(0., 1.)),
);
}
self.current = to;
}
pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
self.contour_count += 1;
if self.contour_count > 1 {
self.push_triangle(
(self.start, self.current, to),
(point(0., 1.), point(0., 1.), point(0., 1.)),
);
}
self.push_triangle(
(self.current, ctrl, to),
(point(0., 0.), point(0.5, 0.), point(1., 1.)),
);
self.current = to;
}
pub fn push_triangle(
&mut self,
xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
st: (Point<f32>, Point<f32>, Point<f32>),
) {
self.bounds = self
.bounds
.union(&Bounds {
origin: xy.0,
size: Default::default(),
})
.union(&Bounds {
origin: xy.1,
size: Default::default(),
})
.union(&Bounds {
origin: xy.2,
size: Default::default(),
});
self.vertices.push(PathVertex {
xy_position: xy.0,
st_position: st.0,
content_mask: Default::default(),
});
self.vertices.push(PathVertex {
xy_position: xy.1,
st_position: st.1,
content_mask: Default::default(),
});
self.vertices.push(PathVertex {
xy_position: xy.2,
st_position: st.2,
content_mask: Default::default(),
});
}
}
impl<T> Path<T>
where
T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
{
#[allow(unused)]
pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
self.bounds.intersect(&self.content_mask.bounds)
}
}
impl From<Path<ScaledPixels>> for Primitive {
fn from(path: Path<ScaledPixels>) -> Self {
Primitive::Path(path)
}
}
#[derive(Clone, Debug)]
#[repr(C)]
pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
pub(crate) xy_position: Point<P>,
pub(crate) st_position: Point<f32>,
pub(crate) content_mask: ContentMask<P>,
}
impl PathVertex<Pixels> {
pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
PathVertex {
xy_position: self.xy_position.scale(factor),
st_position: self.st_position,
content_mask: self.content_mask.scale(factor),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_blur_rect(bounds: Bounds<ScaledPixels>) -> BlurRect {
BlurRect {
order: 0,
blur_radius: ScaledPixels(6.0),
bounds,
content_mask: ContentMask { bounds },
corner_radii: Corners::all(ScaledPixels(4.0)),
tint: Hsla::transparent_black(),
saturation: 1.25,
}
}
#[test]
fn blur_rects_batch_together_within_the_same_layer() {
let mut scene = Scene::default();
let layer_bounds = Bounds {
origin: point(ScaledPixels(0.0), ScaledPixels(0.0)),
size: Size {
width: ScaledPixels(120.0),
height: ScaledPixels(120.0),
},
};
scene.push_layer(layer_bounds);
scene.insert_primitive(test_blur_rect(Bounds {
origin: point(ScaledPixels(4.0), ScaledPixels(6.0)),
size: Size {
width: ScaledPixels(20.0),
height: ScaledPixels(18.0),
},
}));
scene.insert_primitive(test_blur_rect(Bounds {
origin: point(ScaledPixels(40.0), ScaledPixels(12.0)),
size: Size {
width: ScaledPixels(24.0),
height: ScaledPixels(16.0),
},
}));
scene.pop_layer();
scene.finish();
let mut batches = scene.batches();
match batches.next() {
Some(PrimitiveBatch::BlurRects(blur_rects)) => {
assert_eq!(blur_rects.len(), 2);
assert!(
blur_rects
.iter()
.all(|blur_rect| blur_rect.order == scene.blur_rects[0].order)
);
}
other => panic!("expected blur batch, got {other:?}"),
}
assert!(batches.next().is_none());
}
#[test]
fn snapshot_subscene_replays_blur_rects() {
let mut scene = Scene::default();
let layer_bounds = Bounds {
origin: point(ScaledPixels(0.0), ScaledPixels(0.0)),
size: Size {
width: ScaledPixels(80.0),
height: ScaledPixels(80.0),
},
};
let original = test_blur_rect(Bounds {
origin: point(ScaledPixels(8.0), ScaledPixels(10.0)),
size: Size {
width: ScaledPixels(30.0),
height: ScaledPixels(22.0),
},
});
scene.push_layer(layer_bounds);
scene.insert_primitive(original.clone());
scene.pop_layer();
scene.finish();
let snapshot = scene.snapshot_subscene(0..scene.len());
assert_eq!(snapshot.blur_rects.len(), 1);
let replayed = &snapshot.blur_rects[0];
assert_eq!(replayed.blur_radius, original.blur_radius);
assert_eq!(replayed.bounds, original.bounds);
assert_eq!(replayed.content_mask.bounds, original.content_mask.bounds);
assert_eq!(replayed.corner_radii, original.corner_radii);
assert_eq!(replayed.tint, original.tint);
assert_eq!(replayed.saturation, original.saturation);
match snapshot.batches().next() {
Some(PrimitiveBatch::BlurRects(blur_rects)) => assert_eq!(blur_rects.len(), 1),
other => panic!("expected replayed blur batch, got {other:?}"),
}
}
}