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