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