Skip to main content

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