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
235            .quads
236            .iter()
237            .map(|q| RenderedQuad {
238                bounds: q.bounds,
239                background_color: q.background.as_solid(),
240                border_color: q.border_color,
241            })
242            .collect();
243
244        let glyphs = self
245            .monochrome_sprites
246            .iter()
247            .map(|s| RenderedGlyph {
248                origin: s.bounds.origin,
249                size: s.bounds.size,
250                color: s.color,
251            })
252            .collect();
253
254        SceneSnapshot {
255            quads,
256            glyphs,
257            shadow_count: self.shadows.len(),
258            path_count: self.paths.len(),
259            underline_count: self.underlines.len(),
260            polychrome_sprite_count: self.polychrome_sprites.len(),
261            surface_count: self.surfaces.len(),
262        }
263    }
264
265    pub fn finish(&mut self) {
266        self.shadows.sort_by_key(|shadow| shadow.order);
267        self.quads.sort_by_key(|quad| quad.order);
268        self.paths.sort_by_key(|path| path.order);
269        self.underlines.sort_by_key(|underline| underline.order);
270        self.monochrome_sprites
271            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
272        self.polychrome_sprites
273            .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
274        self.surfaces.sort_by_key(|surface| surface.order);
275    }
276
277    #[cfg_attr(
278        all(
279            any(target_os = "linux", target_os = "freebsd"),
280            not(any(feature = "x11", feature = "wayland"))
281        ),
282        allow(dead_code)
283    )]
284    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
285        BatchIterator {
286            shadows: &self.shadows,
287            shadows_start: 0,
288            shadows_iter: self.shadows.iter().peekable(),
289            quads: &self.quads,
290            quads_start: 0,
291            quads_iter: self.quads.iter().peekable(),
292            paths: &self.paths,
293            paths_start: 0,
294            paths_iter: self.paths.iter().peekable(),
295            underlines: &self.underlines,
296            underlines_start: 0,
297            underlines_iter: self.underlines.iter().peekable(),
298            monochrome_sprites: &self.monochrome_sprites,
299            monochrome_sprites_start: 0,
300            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
301            polychrome_sprites: &self.polychrome_sprites,
302            polychrome_sprites_start: 0,
303            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
304            surfaces: &self.surfaces,
305            surfaces_start: 0,
306            surfaces_iter: self.surfaces.iter().peekable(),
307        }
308    }
309}
310
311#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
312#[cfg_attr(
313    all(
314        any(target_os = "linux", target_os = "freebsd"),
315        not(any(feature = "x11", feature = "wayland"))
316    ),
317    allow(dead_code)
318)]
319pub(crate) enum PrimitiveKind {
320    Shadow,
321    #[default]
322    Quad,
323    Path,
324    Underline,
325    MonochromeSprite,
326    PolychromeSprite,
327    Surface,
328}
329
330pub(crate) enum PaintOperation {
331    Primitive(Primitive),
332    StartLayer(Bounds<ScaledPixels>),
333    EndLayer,
334}
335
336#[derive(Clone)]
337pub(crate) enum Primitive {
338    Shadow(Shadow),
339    Quad(Quad),
340    Path(Path<ScaledPixels>),
341    Underline(Underline),
342    MonochromeSprite(MonochromeSprite),
343    PolychromeSprite(PolychromeSprite),
344    Surface(PaintSurface),
345}
346
347impl Primitive {
348    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
349        match self {
350            Primitive::Shadow(shadow) => &shadow.bounds,
351            Primitive::Quad(quad) => &quad.bounds,
352            Primitive::Path(path) => &path.bounds,
353            Primitive::Underline(underline) => &underline.bounds,
354            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
355            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
356            Primitive::Surface(surface) => &surface.bounds,
357        }
358    }
359
360    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
361        match self {
362            Primitive::Shadow(shadow) => &shadow.content_mask,
363            Primitive::Quad(quad) => &quad.content_mask,
364            Primitive::Path(path) => &path.content_mask,
365            Primitive::Underline(underline) => &underline.content_mask,
366            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
367            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
368            Primitive::Surface(surface) => &surface.content_mask,
369        }
370    }
371}
372
373#[cfg_attr(
374    all(
375        any(target_os = "linux", target_os = "freebsd"),
376        not(any(feature = "x11", feature = "wayland"))
377    ),
378    allow(dead_code)
379)]
380struct BatchIterator<'a> {
381    shadows: &'a [Shadow],
382    shadows_start: usize,
383    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
384    quads: &'a [Quad],
385    quads_start: usize,
386    quads_iter: Peekable<slice::Iter<'a, Quad>>,
387    paths: &'a [Path<ScaledPixels>],
388    paths_start: usize,
389    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
390    underlines: &'a [Underline],
391    underlines_start: usize,
392    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
393    monochrome_sprites: &'a [MonochromeSprite],
394    monochrome_sprites_start: usize,
395    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
396    polychrome_sprites: &'a [PolychromeSprite],
397    polychrome_sprites_start: usize,
398    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
399    surfaces: &'a [PaintSurface],
400    surfaces_start: usize,
401    surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
402}
403
404impl<'a> Iterator for BatchIterator<'a> {
405    type Item = PrimitiveBatch<'a>;
406
407    fn next(&mut self) -> Option<Self::Item> {
408        let mut orders_and_kinds = [
409            (
410                self.shadows_iter.peek().map(|s| s.order),
411                PrimitiveKind::Shadow,
412            ),
413            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
414            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
415            (
416                self.underlines_iter.peek().map(|u| u.order),
417                PrimitiveKind::Underline,
418            ),
419            (
420                self.monochrome_sprites_iter.peek().map(|s| s.order),
421                PrimitiveKind::MonochromeSprite,
422            ),
423            (
424                self.polychrome_sprites_iter.peek().map(|s| s.order),
425                PrimitiveKind::PolychromeSprite,
426            ),
427            (
428                self.surfaces_iter.peek().map(|s| s.order),
429                PrimitiveKind::Surface,
430            ),
431        ];
432        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
433
434        let first = orders_and_kinds[0];
435        let second = orders_and_kinds[1];
436        let (batch_kind, max_order_and_kind) = if first.0.is_some() {
437            (first.1, (second.0.unwrap_or(u32::MAX), second.1))
438        } else {
439            return None;
440        };
441
442        match batch_kind {
443            PrimitiveKind::Shadow => {
444                let shadows_start = self.shadows_start;
445                let mut shadows_end = shadows_start + 1;
446                self.shadows_iter.next();
447                while self
448                    .shadows_iter
449                    .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
450                    .is_some()
451                {
452                    shadows_end += 1;
453                }
454                self.shadows_start = shadows_end;
455                Some(PrimitiveBatch::Shadows(
456                    &self.shadows[shadows_start..shadows_end],
457                ))
458            }
459            PrimitiveKind::Quad => {
460                let quads_start = self.quads_start;
461                let mut quads_end = quads_start + 1;
462                self.quads_iter.next();
463                while self
464                    .quads_iter
465                    .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
466                    .is_some()
467                {
468                    quads_end += 1;
469                }
470                self.quads_start = quads_end;
471                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
472            }
473            PrimitiveKind::Path => {
474                let paths_start = self.paths_start;
475                let mut paths_end = paths_start + 1;
476                self.paths_iter.next();
477                while self
478                    .paths_iter
479                    .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
480                    .is_some()
481                {
482                    paths_end += 1;
483                }
484                self.paths_start = paths_end;
485                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
486            }
487            PrimitiveKind::Underline => {
488                let underlines_start = self.underlines_start;
489                let mut underlines_end = underlines_start + 1;
490                self.underlines_iter.next();
491                while self
492                    .underlines_iter
493                    .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
494                    .is_some()
495                {
496                    underlines_end += 1;
497                }
498                self.underlines_start = underlines_end;
499                Some(PrimitiveBatch::Underlines(
500                    &self.underlines[underlines_start..underlines_end],
501                ))
502            }
503            PrimitiveKind::MonochromeSprite => {
504                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
505                let sprites_start = self.monochrome_sprites_start;
506                let mut sprites_end = sprites_start + 1;
507                self.monochrome_sprites_iter.next();
508                while self
509                    .monochrome_sprites_iter
510                    .next_if(|sprite| {
511                        (sprite.order, batch_kind) < max_order_and_kind
512                            && sprite.tile.texture_id == texture_id
513                    })
514                    .is_some()
515                {
516                    sprites_end += 1;
517                }
518                self.monochrome_sprites_start = sprites_end;
519                Some(PrimitiveBatch::MonochromeSprites {
520                    texture_id,
521                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
522                })
523            }
524            PrimitiveKind::PolychromeSprite => {
525                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
526                let sprites_start = self.polychrome_sprites_start;
527                let mut sprites_end = self.polychrome_sprites_start + 1;
528                self.polychrome_sprites_iter.next();
529                while self
530                    .polychrome_sprites_iter
531                    .next_if(|sprite| {
532                        (sprite.order, batch_kind) < max_order_and_kind
533                            && sprite.tile.texture_id == texture_id
534                    })
535                    .is_some()
536                {
537                    sprites_end += 1;
538                }
539                self.polychrome_sprites_start = sprites_end;
540                Some(PrimitiveBatch::PolychromeSprites {
541                    texture_id,
542                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
543                })
544            }
545            PrimitiveKind::Surface => {
546                let surfaces_start = self.surfaces_start;
547                let mut surfaces_end = surfaces_start + 1;
548                self.surfaces_iter.next();
549                while self
550                    .surfaces_iter
551                    .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
552                    .is_some()
553                {
554                    surfaces_end += 1;
555                }
556                self.surfaces_start = surfaces_end;
557                Some(PrimitiveBatch::Surfaces(
558                    &self.surfaces[surfaces_start..surfaces_end],
559                ))
560            }
561        }
562    }
563}
564
565#[derive(Debug)]
566#[cfg_attr(
567    all(
568        any(target_os = "linux", target_os = "freebsd"),
569        not(any(feature = "x11", feature = "wayland"))
570    ),
571    allow(dead_code)
572)]
573pub(crate) enum PrimitiveBatch<'a> {
574    Shadows(&'a [Shadow]),
575    Quads(&'a [Quad]),
576    Paths(&'a [Path<ScaledPixels>]),
577    Underlines(&'a [Underline]),
578    MonochromeSprites {
579        texture_id: AtlasTextureId,
580        sprites: &'a [MonochromeSprite],
581    },
582    PolychromeSprites {
583        texture_id: AtlasTextureId,
584        sprites: &'a [PolychromeSprite],
585    },
586    Surfaces(&'a [PaintSurface]),
587}
588
589#[derive(Default, Debug, Clone)]
590#[repr(C)]
591pub(crate) struct Quad {
592    pub order: DrawOrder,
593    pub border_style: BorderStyle,
594    pub bounds: Bounds<ScaledPixels>,
595    pub content_mask: ContentMask<ScaledPixels>,
596    pub background: Background,
597    pub border_color: Hsla,
598    pub corner_radii: Corners<ScaledPixels>,
599    pub border_widths: Edges<ScaledPixels>,
600}
601
602impl From<Quad> for Primitive {
603    fn from(quad: Quad) -> Self {
604        Primitive::Quad(quad)
605    }
606}
607
608#[derive(Debug, Clone)]
609#[repr(C)]
610pub(crate) struct Underline {
611    pub order: DrawOrder,
612    pub pad: u32, // align to 8 bytes
613    pub bounds: Bounds<ScaledPixels>,
614    pub content_mask: ContentMask<ScaledPixels>,
615    pub color: Hsla,
616    pub thickness: ScaledPixels,
617    pub wavy: u32,
618}
619
620impl From<Underline> for Primitive {
621    fn from(underline: Underline) -> Self {
622        Primitive::Underline(underline)
623    }
624}
625
626#[derive(Debug, Clone)]
627#[repr(C)]
628pub(crate) struct Shadow {
629    pub order: DrawOrder,
630    pub blur_radius: ScaledPixels,
631    pub bounds: Bounds<ScaledPixels>,
632    pub corner_radii: Corners<ScaledPixels>,
633    pub content_mask: ContentMask<ScaledPixels>,
634    pub color: Hsla,
635}
636
637impl From<Shadow> for Primitive {
638    fn from(shadow: Shadow) -> Self {
639        Primitive::Shadow(shadow)
640    }
641}
642
643/// The style of a border.
644#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
645#[repr(C)]
646pub enum BorderStyle {
647    /// A solid border.
648    #[default]
649    Solid = 0,
650    /// A dashed border.
651    Dashed = 1,
652}
653
654/// A data type representing a 2 dimensional transformation that can be applied to an element.
655#[derive(Debug, Clone, Copy, PartialEq)]
656#[repr(C)]
657pub struct TransformationMatrix {
658    /// 2x2 matrix containing rotation and scale,
659    /// stored row-major
660    pub rotation_scale: [[f32; 2]; 2],
661    /// translation vector
662    pub translation: [f32; 2],
663}
664
665impl Eq for TransformationMatrix {}
666
667impl TransformationMatrix {
668    /// The unit matrix, has no effect.
669    pub fn unit() -> Self {
670        Self {
671            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
672            translation: [0.0, 0.0],
673        }
674    }
675
676    /// Move the origin by a given point
677    pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
678        self.compose(Self {
679            rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
680            translation: [point.x.0, point.y.0],
681        })
682    }
683
684    /// Clockwise rotation in radians around the origin
685    pub fn rotate(self, angle: Radians) -> Self {
686        self.compose(Self {
687            rotation_scale: [
688                [angle.0.cos(), -angle.0.sin()],
689                [angle.0.sin(), angle.0.cos()],
690            ],
691            translation: [0.0, 0.0],
692        })
693    }
694
695    /// Scale around the origin
696    pub fn scale(self, size: Size<f32>) -> Self {
697        self.compose(Self {
698            rotation_scale: [[size.width, 0.0], [0.0, size.height]],
699            translation: [0.0, 0.0],
700        })
701    }
702
703    /// Perform matrix multiplication with another transformation
704    /// to produce a new transformation that is the result of
705    /// applying both transformations: first, `other`, then `self`.
706    #[inline]
707    pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
708        if other == Self::unit() {
709            return self;
710        }
711        // Perform matrix multiplication
712        TransformationMatrix {
713            rotation_scale: [
714                [
715                    self.rotation_scale[0][0] * other.rotation_scale[0][0]
716                        + self.rotation_scale[0][1] * other.rotation_scale[1][0],
717                    self.rotation_scale[0][0] * other.rotation_scale[0][1]
718                        + self.rotation_scale[0][1] * other.rotation_scale[1][1],
719                ],
720                [
721                    self.rotation_scale[1][0] * other.rotation_scale[0][0]
722                        + self.rotation_scale[1][1] * other.rotation_scale[1][0],
723                    self.rotation_scale[1][0] * other.rotation_scale[0][1]
724                        + self.rotation_scale[1][1] * other.rotation_scale[1][1],
725                ],
726            ],
727            translation: [
728                self.translation[0]
729                    + self.rotation_scale[0][0] * other.translation[0]
730                    + self.rotation_scale[0][1] * other.translation[1],
731                self.translation[1]
732                    + self.rotation_scale[1][0] * other.translation[0]
733                    + self.rotation_scale[1][1] * other.translation[1],
734            ],
735        }
736    }
737
738    /// Apply transformation to a point, mainly useful for debugging
739    pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
740        let input = [point.x.0, point.y.0];
741        let mut output = self.translation;
742        for (i, output_cell) in output.iter_mut().enumerate() {
743            for (k, input_cell) in input.iter().enumerate() {
744                *output_cell += self.rotation_scale[i][k] * *input_cell;
745            }
746        }
747        Point::new(output[0].into(), output[1].into())
748    }
749}
750
751impl Default for TransformationMatrix {
752    fn default() -> Self {
753        Self::unit()
754    }
755}
756
757#[derive(Clone, Debug)]
758#[repr(C)]
759pub(crate) struct MonochromeSprite {
760    pub order: DrawOrder,
761    pub pad: u32,
762    pub bounds: Bounds<ScaledPixels>,
763    pub content_mask: ContentMask<ScaledPixels>,
764    pub color: Hsla,
765    pub tile: AtlasTile,
766    pub transformation: TransformationMatrix,
767}
768
769impl From<MonochromeSprite> for Primitive {
770    fn from(sprite: MonochromeSprite) -> Self {
771        Primitive::MonochromeSprite(sprite)
772    }
773}
774
775#[derive(Clone, Debug)]
776#[repr(C)]
777pub(crate) struct PolychromeSprite {
778    pub order: DrawOrder,
779    pub pad: u32,
780    pub grayscale: bool,
781    pub opacity: f32,
782    pub bounds: Bounds<ScaledPixels>,
783    pub content_mask: ContentMask<ScaledPixels>,
784    pub corner_radii: Corners<ScaledPixels>,
785    pub tile: AtlasTile,
786}
787
788impl From<PolychromeSprite> for Primitive {
789    fn from(sprite: PolychromeSprite) -> Self {
790        Primitive::PolychromeSprite(sprite)
791    }
792}
793
794#[derive(Clone, Debug)]
795pub(crate) struct PaintSurface {
796    pub order: DrawOrder,
797    pub bounds: Bounds<ScaledPixels>,
798    pub content_mask: ContentMask<ScaledPixels>,
799    #[cfg(target_os = "macos")]
800    pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
801}
802
803impl From<PaintSurface> for Primitive {
804    fn from(surface: PaintSurface) -> Self {
805        Primitive::Surface(surface)
806    }
807}
808
809#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
810pub(crate) struct PathId(pub(crate) usize);
811
812/// A line made up of a series of vertices and control points.
813#[derive(Clone, Debug)]
814pub struct Path<P: Clone + Debug + Default + PartialEq> {
815    pub(crate) id: PathId,
816    pub(crate) order: DrawOrder,
817    pub(crate) bounds: Bounds<P>,
818    pub(crate) content_mask: ContentMask<P>,
819    pub(crate) vertices: Vec<PathVertex<P>>,
820    pub(crate) color: Background,
821    start: Point<P>,
822    current: Point<P>,
823    contour_count: usize,
824}
825
826impl Path<Pixels> {
827    /// Create a new path with the given starting point.
828    pub fn new(start: Point<Pixels>) -> Self {
829        Self {
830            id: PathId(0),
831            order: DrawOrder::default(),
832            vertices: Vec::new(),
833            start,
834            current: start,
835            bounds: Bounds {
836                origin: start,
837                size: Default::default(),
838            },
839            content_mask: Default::default(),
840            color: Default::default(),
841            contour_count: 0,
842        }
843    }
844
845    /// Scale this path by the given factor.
846    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
847        Path {
848            id: self.id,
849            order: self.order,
850            bounds: self.bounds.scale(factor),
851            content_mask: self.content_mask.scale(factor),
852            vertices: self
853                .vertices
854                .iter()
855                .map(|vertex| vertex.scale(factor))
856                .collect(),
857            start: self.start.map(|start| start.scale(factor)),
858            current: self.current.scale(factor),
859            contour_count: self.contour_count,
860            color: self.color,
861        }
862    }
863
864    /// Move the start, current point to the given point.
865    pub fn move_to(&mut self, to: Point<Pixels>) {
866        self.contour_count += 1;
867        self.start = to;
868        self.current = to;
869    }
870
871    /// Draw a straight line from the current point to the given point.
872    pub fn line_to(&mut self, to: Point<Pixels>) {
873        self.contour_count += 1;
874        if self.contour_count > 1 {
875            self.push_triangle(
876                (self.start, self.current, to),
877                (point(0., 1.), point(0., 1.), point(0., 1.)),
878            );
879        }
880        self.current = to;
881    }
882
883    /// Draw a curve from the current point to the given point, using the given control point.
884    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
885        self.contour_count += 1;
886        if self.contour_count > 1 {
887            self.push_triangle(
888                (self.start, self.current, to),
889                (point(0., 1.), point(0., 1.), point(0., 1.)),
890            );
891        }
892
893        self.push_triangle(
894            (self.current, ctrl, to),
895            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
896        );
897        self.current = to;
898    }
899
900    /// Push a triangle to the Path.
901    pub fn push_triangle(
902        &mut self,
903        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
904        st: (Point<f32>, Point<f32>, Point<f32>),
905    ) {
906        self.bounds = self
907            .bounds
908            .union(&Bounds {
909                origin: xy.0,
910                size: Default::default(),
911            })
912            .union(&Bounds {
913                origin: xy.1,
914                size: Default::default(),
915            })
916            .union(&Bounds {
917                origin: xy.2,
918                size: Default::default(),
919            });
920
921        self.vertices.push(PathVertex {
922            xy_position: xy.0,
923            st_position: st.0,
924            content_mask: Default::default(),
925        });
926        self.vertices.push(PathVertex {
927            xy_position: xy.1,
928            st_position: st.1,
929            content_mask: Default::default(),
930        });
931        self.vertices.push(PathVertex {
932            xy_position: xy.2,
933            st_position: st.2,
934            content_mask: Default::default(),
935        });
936    }
937}
938
939impl<T> Path<T>
940where
941    T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
942{
943    #[allow(unused)]
944    pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
945        self.bounds.intersect(&self.content_mask.bounds)
946    }
947}
948
949impl From<Path<ScaledPixels>> for Primitive {
950    fn from(path: Path<ScaledPixels>) -> Self {
951        Primitive::Path(path)
952    }
953}
954
955#[derive(Clone, Debug)]
956#[repr(C)]
957pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
958    pub(crate) xy_position: Point<P>,
959    pub(crate) st_position: Point<f32>,
960    pub(crate) content_mask: ContentMask<P>,
961}
962
963impl PathVertex<Pixels> {
964    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
965        PathVertex {
966            xy_position: self.xy_position.scale(factor),
967            st_position: self.st_position,
968            content_mask: self.content_mask.scale(factor),
969        }
970    }
971}