scene.rs

  1// todo("windows"): remove
  2#![cfg_attr(windows, allow(dead_code))]
  3
  4use schemars::JsonSchema;
  5use serde::{Deserialize, Serialize};
  6
  7use crate::{
  8    AtlasTextureId, AtlasTile, Background, Bounds, ContentMask, Corners, Edges, Hsla, Pixels,
  9    Point, Radians, ScaledPixels, Size, bounds_tree::BoundsTree, point,
 10};
 11use std::{
 12    fmt::Debug,
 13    iter::Peekable,
 14    ops::{Add, Range, Sub},
 15    slice,
 16};
 17
 18#[allow(non_camel_case_types, unused)]
 19pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 20
 21pub(crate) type DrawOrder = u32;
 22
 23/// Test-only scene snapshot for inspecting rendered content.
 24#[cfg(any(test, feature = "test-support"))]
 25pub mod test_scene {
 26    use crate::{Bounds, Hsla, Point, ScaledPixels};
 27
 28    /// A rendered quad (background, border, cursor, selection, etc.)
 29    #[derive(Debug, Clone)]
 30    pub struct RenderedQuad {
 31        /// Bounds in scaled pixels.
 32        pub bounds: Bounds<ScaledPixels>,
 33        /// Background color (if solid).
 34        pub background_color: Option<Hsla>,
 35        /// Border color.
 36        pub border_color: Hsla,
 37    }
 38
 39    /// A rendered text glyph.
 40    #[derive(Debug, Clone)]
 41    pub struct RenderedGlyph {
 42        /// Origin position in scaled pixels.
 43        pub origin: Point<ScaledPixels>,
 44        /// Size in scaled pixels.
 45        pub size: crate::Size<ScaledPixels>,
 46        /// Color of the glyph.
 47        pub color: Hsla,
 48    }
 49
 50    /// Snapshot of scene contents for testing.
 51    #[derive(Debug, Default)]
 52    pub struct SceneSnapshot {
 53        /// All rendered quads.
 54        pub quads: Vec<RenderedQuad>,
 55        /// All rendered text glyphs.
 56        pub glyphs: Vec<RenderedGlyph>,
 57        /// Number of shadow primitives.
 58        pub shadow_count: usize,
 59        /// Number of path primitives.
 60        pub path_count: usize,
 61        /// Number of underline primitives.
 62        pub underline_count: usize,
 63        /// Number of polychrome sprites (images, emoji).
 64        pub polychrome_sprite_count: usize,
 65        /// Number of surface primitives.
 66        pub surface_count: usize,
 67    }
 68
 69    impl SceneSnapshot {
 70        /// Get unique Y positions of quads, sorted.
 71        pub fn quad_y_positions(&self) -> Vec<f32> {
 72            let mut positions: Vec<f32> = self.quads.iter().map(|q| q.bounds.origin.y.0).collect();
 73            positions.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
 74            positions.dedup();
 75            positions
 76        }
 77
 78        /// Get unique Y positions of glyphs, sorted.
 79        pub fn glyph_y_positions(&self) -> Vec<f32> {
 80            let mut positions: Vec<f32> = self.glyphs.iter().map(|g| g.origin.y.0).collect();
 81            positions.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
 82            positions.dedup();
 83            positions
 84        }
 85
 86        /// Find quads within a Y range.
 87        pub fn quads_in_y_range(&self, min_y: f32, max_y: f32) -> Vec<&RenderedQuad> {
 88            self.quads
 89                .iter()
 90                .filter(|q| {
 91                    let y = q.bounds.origin.y.0;
 92                    y >= min_y && y < max_y
 93                })
 94                .collect()
 95        }
 96
 97        /// Find glyphs within a Y range.
 98        pub fn glyphs_in_y_range(&self, min_y: f32, max_y: f32) -> Vec<&RenderedGlyph> {
 99            self.glyphs
100                .iter()
101                .filter(|g| {
102                    let y = g.origin.y.0;
103                    y >= min_y && y < max_y
104                })
105                .collect()
106        }
107
108        /// Debug summary string.
109        pub fn summary(&self) -> String {
110            format!(
111                "quads: {}, glyphs: {}, shadows: {}, paths: {}, underlines: {}, polychrome: {}, surfaces: {}",
112                self.quads.len(),
113                self.glyphs.len(),
114                self.shadow_count,
115                self.path_count,
116                self.underline_count,
117                self.polychrome_sprite_count,
118                self.surface_count,
119            )
120        }
121    }
122}
123
124#[cfg(any(test, feature = "test-support"))]
125pub use test_scene::*;
126
127#[derive(Default)]
128pub(crate) struct Scene {
129    pub(crate) paint_operations: Vec<PaintOperation>,
130    primitive_bounds: BoundsTree<ScaledPixels>,
131    layer_stack: Vec<DrawOrder>,
132    pub(crate) shadows: Vec<Shadow>,
133    pub(crate) quads: Vec<Quad>,
134    pub(crate) paths: Vec<Path<ScaledPixels>>,
135    pub(crate) underlines: Vec<Underline>,
136    pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
137    pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
138    pub(crate) surfaces: Vec<PaintSurface>,
139}
140
141impl Scene {
142    pub fn clear(&mut self) {
143        self.paint_operations.clear();
144        self.primitive_bounds.clear();
145        self.layer_stack.clear();
146        self.paths.clear();
147        self.shadows.clear();
148        self.quads.clear();
149        self.underlines.clear();
150        self.monochrome_sprites.clear();
151        self.polychrome_sprites.clear();
152        self.surfaces.clear();
153    }
154
155    pub fn len(&self) -> usize {
156        self.paint_operations.len()
157    }
158
159    pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
160        let order = self.primitive_bounds.insert(bounds);
161        self.layer_stack.push(order);
162        self.paint_operations
163            .push(PaintOperation::StartLayer(bounds));
164    }
165
166    pub fn pop_layer(&mut self) {
167        self.layer_stack.pop();
168        self.paint_operations.push(PaintOperation::EndLayer);
169    }
170
171    pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
172        let mut primitive = primitive.into();
173        let clipped_bounds = primitive
174            .bounds()
175            .intersect(&primitive.content_mask().bounds);
176
177        if clipped_bounds.is_empty() {
178            return;
179        }
180
181        let order = self
182            .layer_stack
183            .last()
184            .copied()
185            .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
186        match &mut primitive {
187            Primitive::Shadow(shadow) => {
188                shadow.order = order;
189                self.shadows.push(shadow.clone());
190            }
191            Primitive::Quad(quad) => {
192                quad.order = order;
193                self.quads.push(quad.clone());
194            }
195            Primitive::Path(path) => {
196                path.order = order;
197                path.id = PathId(self.paths.len());
198                self.paths.push(path.clone());
199            }
200            Primitive::Underline(underline) => {
201                underline.order = order;
202                self.underlines.push(underline.clone());
203            }
204            Primitive::MonochromeSprite(sprite) => {
205                sprite.order = order;
206                self.monochrome_sprites.push(sprite.clone());
207            }
208            Primitive::PolychromeSprite(sprite) => {
209                sprite.order = order;
210                self.polychrome_sprites.push(sprite.clone());
211            }
212            Primitive::Surface(surface) => {
213                surface.order = order;
214                self.surfaces.push(surface.clone());
215            }
216        }
217        self.paint_operations
218            .push(PaintOperation::Primitive(primitive));
219    }
220
221    pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
222        for operation in &prev_scene.paint_operations[range] {
223            match operation {
224                PaintOperation::Primitive(primitive) => self.insert_primitive(primitive.clone()),
225                PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
226                PaintOperation::EndLayer => self.pop_layer(),
227            }
228        }
229    }
230
231    /// Create a snapshot of the scene for testing.
232    #[cfg(any(test, feature = "test-support"))]
233    pub fn snapshot(&self) -> SceneSnapshot {
234        let quads = self.quads.iter().map(|q| {
235            RenderedQuad {
236                bounds: q.bounds,
237                background_color: q.background.as_solid(),
238                border_color: q.border_color,
239            }
240        }).collect();
241
242        let glyphs = self.monochrome_sprites.iter().map(|s| {
243            RenderedGlyph {
244                origin: s.bounds.origin,
245                size: s.bounds.size,
246                color: s.color,
247            }
248        }).collect();
249
250        SceneSnapshot {
251            quads,
252            glyphs,
253            shadow_count: self.shadows.len(),
254            path_count: self.paths.len(),
255            underline_count: self.underlines.len(),
256            polychrome_sprite_count: self.polychrome_sprites.len(),
257            surface_count: self.surfaces.len(),
258        }
259    }
260
261    pub fn finish(&mut self) {
262        self.shadows.sort_by_key(|shadow| shadow.order);
263        self.quads.sort_by_key(|quad| quad.order);
264        self.paths.sort_by_key(|path| path.order);
265        self.underlines.sort_by_key(|underline| underline.order);
266        self.monochrome_sprites
267            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
268        self.polychrome_sprites
269            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
270        self.surfaces.sort_by_key(|surface| surface.order);
271    }
272
273    #[cfg_attr(
274        all(
275            any(target_os = "linux", target_os = "freebsd"),
276            not(any(feature = "x11", feature = "wayland"))
277        ),
278        allow(dead_code)
279    )]
280    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
281        BatchIterator {
282            shadows: &self.shadows,
283            shadows_start: 0,
284            shadows_iter: self.shadows.iter().peekable(),
285            quads: &self.quads,
286            quads_start: 0,
287            quads_iter: self.quads.iter().peekable(),
288            paths: &self.paths,
289            paths_start: 0,
290            paths_iter: self.paths.iter().peekable(),
291            underlines: &self.underlines,
292            underlines_start: 0,
293            underlines_iter: self.underlines.iter().peekable(),
294            monochrome_sprites: &self.monochrome_sprites,
295            monochrome_sprites_start: 0,
296            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
297            polychrome_sprites: &self.polychrome_sprites,
298            polychrome_sprites_start: 0,
299            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
300            surfaces: &self.surfaces,
301            surfaces_start: 0,
302            surfaces_iter: self.surfaces.iter().peekable(),
303        }
304    }
305}
306
307#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
308#[cfg_attr(
309    all(
310        any(target_os = "linux", target_os = "freebsd"),
311        not(any(feature = "x11", feature = "wayland"))
312    ),
313    allow(dead_code)
314)]
315pub(crate) enum PrimitiveKind {
316    Shadow,
317    #[default]
318    Quad,
319    Path,
320    Underline,
321    MonochromeSprite,
322    PolychromeSprite,
323    Surface,
324}
325
326pub(crate) enum PaintOperation {
327    Primitive(Primitive),
328    StartLayer(Bounds<ScaledPixels>),
329    EndLayer,
330}
331
332#[derive(Clone)]
333pub(crate) enum Primitive {
334    Shadow(Shadow),
335    Quad(Quad),
336    Path(Path<ScaledPixels>),
337    Underline(Underline),
338    MonochromeSprite(MonochromeSprite),
339    PolychromeSprite(PolychromeSprite),
340    Surface(PaintSurface),
341}
342
343impl Primitive {
344    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
345        match self {
346            Primitive::Shadow(shadow) => &shadow.bounds,
347            Primitive::Quad(quad) => &quad.bounds,
348            Primitive::Path(path) => &path.bounds,
349            Primitive::Underline(underline) => &underline.bounds,
350            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
351            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
352            Primitive::Surface(surface) => &surface.bounds,
353        }
354    }
355
356    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
357        match self {
358            Primitive::Shadow(shadow) => &shadow.content_mask,
359            Primitive::Quad(quad) => &quad.content_mask,
360            Primitive::Path(path) => &path.content_mask,
361            Primitive::Underline(underline) => &underline.content_mask,
362            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
363            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
364            Primitive::Surface(surface) => &surface.content_mask,
365        }
366    }
367}
368
369#[cfg_attr(
370    all(
371        any(target_os = "linux", target_os = "freebsd"),
372        not(any(feature = "x11", feature = "wayland"))
373    ),
374    allow(dead_code)
375)]
376struct BatchIterator<'a> {
377    shadows: &'a [Shadow],
378    shadows_start: usize,
379    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
380    quads: &'a [Quad],
381    quads_start: usize,
382    quads_iter: Peekable<slice::Iter<'a, Quad>>,
383    paths: &'a [Path<ScaledPixels>],
384    paths_start: usize,
385    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
386    underlines: &'a [Underline],
387    underlines_start: usize,
388    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
389    monochrome_sprites: &'a [MonochromeSprite],
390    monochrome_sprites_start: usize,
391    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
392    polychrome_sprites: &'a [PolychromeSprite],
393    polychrome_sprites_start: usize,
394    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
395    surfaces: &'a [PaintSurface],
396    surfaces_start: usize,
397    surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
398}
399
400impl<'a> Iterator for BatchIterator<'a> {
401    type Item = PrimitiveBatch<'a>;
402
403    fn next(&mut self) -> Option<Self::Item> {
404        let mut orders_and_kinds = [
405            (
406                self.shadows_iter.peek().map(|s| s.order),
407                PrimitiveKind::Shadow,
408            ),
409            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
410            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
411            (
412                self.underlines_iter.peek().map(|u| u.order),
413                PrimitiveKind::Underline,
414            ),
415            (
416                self.monochrome_sprites_iter.peek().map(|s| s.order),
417                PrimitiveKind::MonochromeSprite,
418            ),
419            (
420                self.polychrome_sprites_iter.peek().map(|s| s.order),
421                PrimitiveKind::PolychromeSprite,
422            ),
423            (
424                self.surfaces_iter.peek().map(|s| s.order),
425                PrimitiveKind::Surface,
426            ),
427        ];
428        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
429
430        let first = orders_and_kinds[0];
431        let second = orders_and_kinds[1];
432        let (batch_kind, max_order_and_kind) = if first.0.is_some() {
433            (first.1, (second.0.unwrap_or(u32::MAX), second.1))
434        } else {
435            return None;
436        };
437
438        match batch_kind {
439            PrimitiveKind::Shadow => {
440                let shadows_start = self.shadows_start;
441                let mut shadows_end = shadows_start + 1;
442                self.shadows_iter.next();
443                while self
444                    .shadows_iter
445                    .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
446                    .is_some()
447                {
448                    shadows_end += 1;
449                }
450                self.shadows_start = shadows_end;
451                Some(PrimitiveBatch::Shadows(
452                    &self.shadows[shadows_start..shadows_end],
453                ))
454            }
455            PrimitiveKind::Quad => {
456                let quads_start = self.quads_start;
457                let mut quads_end = quads_start + 1;
458                self.quads_iter.next();
459                while self
460                    .quads_iter
461                    .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
462                    .is_some()
463                {
464                    quads_end += 1;
465                }
466                self.quads_start = quads_end;
467                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
468            }
469            PrimitiveKind::Path => {
470                let paths_start = self.paths_start;
471                let mut paths_end = paths_start + 1;
472                self.paths_iter.next();
473                while self
474                    .paths_iter
475                    .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
476                    .is_some()
477                {
478                    paths_end += 1;
479                }
480                self.paths_start = paths_end;
481                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
482            }
483            PrimitiveKind::Underline => {
484                let underlines_start = self.underlines_start;
485                let mut underlines_end = underlines_start + 1;
486                self.underlines_iter.next();
487                while self
488                    .underlines_iter
489                    .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
490                    .is_some()
491                {
492                    underlines_end += 1;
493                }
494                self.underlines_start = underlines_end;
495                Some(PrimitiveBatch::Underlines(
496                    &self.underlines[underlines_start..underlines_end],
497                ))
498            }
499            PrimitiveKind::MonochromeSprite => {
500                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
501                let sprites_start = self.monochrome_sprites_start;
502                let mut sprites_end = sprites_start + 1;
503                self.monochrome_sprites_iter.next();
504                while self
505                    .monochrome_sprites_iter
506                    .next_if(|sprite| {
507                        (sprite.order, batch_kind) < max_order_and_kind
508                            && sprite.tile.texture_id == texture_id
509                    })
510                    .is_some()
511                {
512                    sprites_end += 1;
513                }
514                self.monochrome_sprites_start = sprites_end;
515                Some(PrimitiveBatch::MonochromeSprites {
516                    texture_id,
517                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
518                })
519            }
520            PrimitiveKind::PolychromeSprite => {
521                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
522                let sprites_start = self.polychrome_sprites_start;
523                let mut sprites_end = self.polychrome_sprites_start + 1;
524                self.polychrome_sprites_iter.next();
525                while self
526                    .polychrome_sprites_iter
527                    .next_if(|sprite| {
528                        (sprite.order, batch_kind) < max_order_and_kind
529                            && sprite.tile.texture_id == texture_id
530                    })
531                    .is_some()
532                {
533                    sprites_end += 1;
534                }
535                self.polychrome_sprites_start = sprites_end;
536                Some(PrimitiveBatch::PolychromeSprites {
537                    texture_id,
538                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
539                })
540            }
541            PrimitiveKind::Surface => {
542                let surfaces_start = self.surfaces_start;
543                let mut surfaces_end = surfaces_start + 1;
544                self.surfaces_iter.next();
545                while self
546                    .surfaces_iter
547                    .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
548                    .is_some()
549                {
550                    surfaces_end += 1;
551                }
552                self.surfaces_start = surfaces_end;
553                Some(PrimitiveBatch::Surfaces(
554                    &self.surfaces[surfaces_start..surfaces_end],
555                ))
556            }
557        }
558    }
559}
560
561#[derive(Debug)]
562#[cfg_attr(
563    all(
564        any(target_os = "linux", target_os = "freebsd"),
565        not(any(feature = "x11", feature = "wayland"))
566    ),
567    allow(dead_code)
568)]
569pub(crate) enum PrimitiveBatch<'a> {
570    Shadows(&'a [Shadow]),
571    Quads(&'a [Quad]),
572    Paths(&'a [Path<ScaledPixels>]),
573    Underlines(&'a [Underline]),
574    MonochromeSprites {
575        texture_id: AtlasTextureId,
576        sprites: &'a [MonochromeSprite],
577    },
578    PolychromeSprites {
579        texture_id: AtlasTextureId,
580        sprites: &'a [PolychromeSprite],
581    },
582    Surfaces(&'a [PaintSurface]),
583}
584
585#[derive(Default, Debug, Clone)]
586#[repr(C)]
587pub(crate) struct Quad {
588    pub order: DrawOrder,
589    pub border_style: BorderStyle,
590    pub bounds: Bounds<ScaledPixels>,
591    pub content_mask: ContentMask<ScaledPixels>,
592    pub background: Background,
593    pub border_color: Hsla,
594    pub corner_radii: Corners<ScaledPixels>,
595    pub border_widths: Edges<ScaledPixels>,
596}
597
598impl From<Quad> for Primitive {
599    fn from(quad: Quad) -> Self {
600        Primitive::Quad(quad)
601    }
602}
603
604#[derive(Debug, Clone)]
605#[repr(C)]
606pub(crate) struct Underline {
607    pub order: DrawOrder,
608    pub pad: u32, // align to 8 bytes
609    pub bounds: Bounds<ScaledPixels>,
610    pub content_mask: ContentMask<ScaledPixels>,
611    pub color: Hsla,
612    pub thickness: ScaledPixels,
613    pub wavy: u32,
614}
615
616impl From<Underline> for Primitive {
617    fn from(underline: Underline) -> Self {
618        Primitive::Underline(underline)
619    }
620}
621
622#[derive(Debug, Clone)]
623#[repr(C)]
624pub(crate) struct Shadow {
625    pub order: DrawOrder,
626    pub blur_radius: ScaledPixels,
627    pub bounds: Bounds<ScaledPixels>,
628    pub corner_radii: Corners<ScaledPixels>,
629    pub content_mask: ContentMask<ScaledPixels>,
630    pub color: Hsla,
631}
632
633impl From<Shadow> for Primitive {
634    fn from(shadow: Shadow) -> Self {
635        Primitive::Shadow(shadow)
636    }
637}
638
639/// The style of a border.
640#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
641#[repr(C)]
642pub enum BorderStyle {
643    /// A solid border.
644    #[default]
645    Solid = 0,
646    /// A dashed border.
647    Dashed = 1,
648}
649
650/// A data type representing a 2 dimensional transformation that can be applied to an element.
651#[derive(Debug, Clone, Copy, PartialEq)]
652#[repr(C)]
653pub struct TransformationMatrix {
654    /// 2x2 matrix containing rotation and scale,
655    /// stored row-major
656    pub rotation_scale: [[f32; 2]; 2],
657    /// translation vector
658    pub translation: [f32; 2],
659}
660
661impl Eq for TransformationMatrix {}
662
663impl TransformationMatrix {
664    /// The unit matrix, has no effect.
665    pub fn unit() -> Self {
666        Self {
667            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
668            translation: [0.0, 0.0],
669        }
670    }
671
672    /// Move the origin by a given point
673    pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
674        self.compose(Self {
675            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
676            translation: [point.x.0, point.y.0],
677        })
678    }
679
680    /// Clockwise rotation in radians around the origin
681    pub fn rotate(self, angle: Radians) -> Self {
682        self.compose(Self {
683            rotation_scale: [
684                [angle.0.cos(), -angle.0.sin()],
685                [angle.0.sin(), angle.0.cos()],
686            ],
687            translation: [0.0, 0.0],
688        })
689    }
690
691    /// Scale around the origin
692    pub fn scale(self, size: Size<f32>) -> Self {
693        self.compose(Self {
694            rotation_scale: [[size.width, 0.0], [0.0, size.height]],
695            translation: [0.0, 0.0],
696        })
697    }
698
699    /// Perform matrix multiplication with another transformation
700    /// to produce a new transformation that is the result of
701    /// applying both transformations: first, `other`, then `self`.
702    #[inline]
703    pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
704        if other == Self::unit() {
705            return self;
706        }
707        // Perform matrix multiplication
708        TransformationMatrix {
709            rotation_scale: [
710                [
711                    self.rotation_scale[0][0] * other.rotation_scale[0][0]
712                        + self.rotation_scale[0][1] * other.rotation_scale[1][0],
713                    self.rotation_scale[0][0] * other.rotation_scale[0][1]
714                        + self.rotation_scale[0][1] * other.rotation_scale[1][1],
715                ],
716                [
717                    self.rotation_scale[1][0] * other.rotation_scale[0][0]
718                        + self.rotation_scale[1][1] * other.rotation_scale[1][0],
719                    self.rotation_scale[1][0] * other.rotation_scale[0][1]
720                        + self.rotation_scale[1][1] * other.rotation_scale[1][1],
721                ],
722            ],
723            translation: [
724                self.translation[0]
725                    + self.rotation_scale[0][0] * other.translation[0]
726                    + self.rotation_scale[0][1] * other.translation[1],
727                self.translation[1]
728                    + self.rotation_scale[1][0] * other.translation[0]
729                    + self.rotation_scale[1][1] * other.translation[1],
730            ],
731        }
732    }
733
734    /// Apply transformation to a point, mainly useful for debugging
735    pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
736        let input = [point.x.0, point.y.0];
737        let mut output = self.translation;
738        for (i, output_cell) in output.iter_mut().enumerate() {
739            for (k, input_cell) in input.iter().enumerate() {
740                *output_cell += self.rotation_scale[i][k] * *input_cell;
741            }
742        }
743        Point::new(output[0].into(), output[1].into())
744    }
745}
746
747impl Default for TransformationMatrix {
748    fn default() -> Self {
749        Self::unit()
750    }
751}
752
753#[derive(Clone, Debug)]
754#[repr(C)]
755pub(crate) struct MonochromeSprite {
756    pub order: DrawOrder,
757    pub pad: u32,
758    pub bounds: Bounds<ScaledPixels>,
759    pub content_mask: ContentMask<ScaledPixels>,
760    pub color: Hsla,
761    pub tile: AtlasTile,
762    pub transformation: TransformationMatrix,
763}
764
765impl From<MonochromeSprite> for Primitive {
766    fn from(sprite: MonochromeSprite) -> Self {
767        Primitive::MonochromeSprite(sprite)
768    }
769}
770
771#[derive(Clone, Debug)]
772#[repr(C)]
773pub(crate) struct PolychromeSprite {
774    pub order: DrawOrder,
775    pub pad: u32,
776    pub grayscale: bool,
777    pub opacity: f32,
778    pub bounds: Bounds<ScaledPixels>,
779    pub content_mask: ContentMask<ScaledPixels>,
780    pub corner_radii: Corners<ScaledPixels>,
781    pub tile: AtlasTile,
782}
783
784impl From<PolychromeSprite> for Primitive {
785    fn from(sprite: PolychromeSprite) -> Self {
786        Primitive::PolychromeSprite(sprite)
787    }
788}
789
790#[derive(Clone, Debug)]
791pub(crate) struct PaintSurface {
792    pub order: DrawOrder,
793    pub bounds: Bounds<ScaledPixels>,
794    pub content_mask: ContentMask<ScaledPixels>,
795    #[cfg(target_os = "macos")]
796    pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
797}
798
799impl From<PaintSurface> for Primitive {
800    fn from(surface: PaintSurface) -> Self {
801        Primitive::Surface(surface)
802    }
803}
804
805#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
806pub(crate) struct PathId(pub(crate) usize);
807
808/// A line made up of a series of vertices and control points.
809#[derive(Clone, Debug)]
810pub struct Path<P: Clone + Debug + Default + PartialEq> {
811    pub(crate) id: PathId,
812    pub(crate) order: DrawOrder,
813    pub(crate) bounds: Bounds<P>,
814    pub(crate) content_mask: ContentMask<P>,
815    pub(crate) vertices: Vec<PathVertex<P>>,
816    pub(crate) color: Background,
817    start: Point<P>,
818    current: Point<P>,
819    contour_count: usize,
820}
821
822impl Path<Pixels> {
823    /// Create a new path with the given starting point.
824    pub fn new(start: Point<Pixels>) -> Self {
825        Self {
826            id: PathId(0),
827            order: DrawOrder::default(),
828            vertices: Vec::new(),
829            start,
830            current: start,
831            bounds: Bounds {
832                origin: start,
833                size: Default::default(),
834            },
835            content_mask: Default::default(),
836            color: Default::default(),
837            contour_count: 0,
838        }
839    }
840
841    /// Scale this path by the given factor.
842    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
843        Path {
844            id: self.id,
845            order: self.order,
846            bounds: self.bounds.scale(factor),
847            content_mask: self.content_mask.scale(factor),
848            vertices: self
849                .vertices
850                .iter()
851                .map(|vertex| vertex.scale(factor))
852                .collect(),
853            start: self.start.map(|start| start.scale(factor)),
854            current: self.current.scale(factor),
855            contour_count: self.contour_count,
856            color: self.color,
857        }
858    }
859
860    /// Move the start, current point to the given point.
861    pub fn move_to(&mut self, to: Point<Pixels>) {
862        self.contour_count += 1;
863        self.start = to;
864        self.current = to;
865    }
866
867    /// Draw a straight line from the current point to the given point.
868    pub fn line_to(&mut self, to: Point<Pixels>) {
869        self.contour_count += 1;
870        if self.contour_count > 1 {
871            self.push_triangle(
872                (self.start, self.current, to),
873                (point(0., 1.), point(0., 1.), point(0., 1.)),
874            );
875        }
876        self.current = to;
877    }
878
879    /// Draw a curve from the current point to the given point, using the given control point.
880    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
881        self.contour_count += 1;
882        if self.contour_count > 1 {
883            self.push_triangle(
884                (self.start, self.current, to),
885                (point(0., 1.), point(0., 1.), point(0., 1.)),
886            );
887        }
888
889        self.push_triangle(
890            (self.current, ctrl, to),
891            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
892        );
893        self.current = to;
894    }
895
896    /// Push a triangle to the Path.
897    pub fn push_triangle(
898        &mut self,
899        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
900        st: (Point<f32>, Point<f32>, Point<f32>),
901    ) {
902        self.bounds = self
903            .bounds
904            .union(&Bounds {
905                origin: xy.0,
906                size: Default::default(),
907            })
908            .union(&Bounds {
909                origin: xy.1,
910                size: Default::default(),
911            })
912            .union(&Bounds {
913                origin: xy.2,
914                size: Default::default(),
915            });
916
917        self.vertices.push(PathVertex {
918            xy_position: xy.0,
919            st_position: st.0,
920            content_mask: Default::default(),
921        });
922        self.vertices.push(PathVertex {
923            xy_position: xy.1,
924            st_position: st.1,
925            content_mask: Default::default(),
926        });
927        self.vertices.push(PathVertex {
928            xy_position: xy.2,
929            st_position: st.2,
930            content_mask: Default::default(),
931        });
932    }
933}
934
935impl<T> Path<T>
936where
937    T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
938{
939    #[allow(unused)]
940    pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
941        self.bounds.intersect(&self.content_mask.bounds)
942    }
943}
944
945impl From<Path<ScaledPixels>> for Primitive {
946    fn from(path: Path<ScaledPixels>) -> Self {
947        Primitive::Path(path)
948    }
949}
950
951#[derive(Clone, Debug)]
952#[repr(C)]
953pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
954    pub(crate) xy_position: Point<P>,
955    pub(crate) st_position: Point<f32>,
956    pub(crate) content_mask: ContentMask<P>,
957}
958
959impl PathVertex<Pixels> {
960    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
961        PathVertex {
962            xy_position: self.xy_position.scale(factor),
963            st_position: self.st_position,
964            content_mask: self.content_mask.scale(factor),
965        }
966    }
967}