scene.rs

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