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