scene.rs

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