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
235 .quads
236 .iter()
237 .map(|q| RenderedQuad {
238 bounds: q.bounds,
239 background_color: q.background.as_solid(),
240 border_color: q.border_color,
241 })
242 .collect();
243
244 let glyphs = self
245 .monochrome_sprites
246 .iter()
247 .map(|s| RenderedGlyph {
248 origin: s.bounds.origin,
249 size: s.bounds.size,
250 color: s.color,
251 })
252 .collect();
253
254 SceneSnapshot {
255 quads,
256 glyphs,
257 shadow_count: self.shadows.len(),
258 path_count: self.paths.len(),
259 underline_count: self.underlines.len(),
260 polychrome_sprite_count: self.polychrome_sprites.len(),
261 surface_count: self.surfaces.len(),
262 }
263 }
264
265 pub fn finish(&mut self) {
266 self.shadows.sort_by_key(|shadow| shadow.order);
267 self.quads.sort_by_key(|quad| quad.order);
268 self.paths.sort_by_key(|path| path.order);
269 self.underlines.sort_by_key(|underline| underline.order);
270 self.monochrome_sprites
271 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
272 self.polychrome_sprites
273 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
274 self.surfaces.sort_by_key(|surface| surface.order);
275 }
276
277 #[cfg_attr(
278 all(
279 any(target_os = "linux", target_os = "freebsd"),
280 not(any(feature = "x11", feature = "wayland"))
281 ),
282 allow(dead_code)
283 )]
284 pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
285 BatchIterator {
286 shadows: &self.shadows,
287 shadows_start: 0,
288 shadows_iter: self.shadows.iter().peekable(),
289 quads: &self.quads,
290 quads_start: 0,
291 quads_iter: self.quads.iter().peekable(),
292 paths: &self.paths,
293 paths_start: 0,
294 paths_iter: self.paths.iter().peekable(),
295 underlines: &self.underlines,
296 underlines_start: 0,
297 underlines_iter: self.underlines.iter().peekable(),
298 monochrome_sprites: &self.monochrome_sprites,
299 monochrome_sprites_start: 0,
300 monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
301 polychrome_sprites: &self.polychrome_sprites,
302 polychrome_sprites_start: 0,
303 polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
304 surfaces: &self.surfaces,
305 surfaces_start: 0,
306 surfaces_iter: self.surfaces.iter().peekable(),
307 }
308 }
309}
310
311#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
312#[cfg_attr(
313 all(
314 any(target_os = "linux", target_os = "freebsd"),
315 not(any(feature = "x11", feature = "wayland"))
316 ),
317 allow(dead_code)
318)]
319pub(crate) enum PrimitiveKind {
320 Shadow,
321 #[default]
322 Quad,
323 Path,
324 Underline,
325 MonochromeSprite,
326 PolychromeSprite,
327 Surface,
328}
329
330pub(crate) enum PaintOperation {
331 Primitive(Primitive),
332 StartLayer(Bounds<ScaledPixels>),
333 EndLayer,
334}
335
336#[derive(Clone)]
337pub(crate) enum Primitive {
338 Shadow(Shadow),
339 Quad(Quad),
340 Path(Path<ScaledPixels>),
341 Underline(Underline),
342 MonochromeSprite(MonochromeSprite),
343 PolychromeSprite(PolychromeSprite),
344 Surface(PaintSurface),
345}
346
347impl Primitive {
348 pub fn bounds(&self) -> &Bounds<ScaledPixels> {
349 match self {
350 Primitive::Shadow(shadow) => &shadow.bounds,
351 Primitive::Quad(quad) => &quad.bounds,
352 Primitive::Path(path) => &path.bounds,
353 Primitive::Underline(underline) => &underline.bounds,
354 Primitive::MonochromeSprite(sprite) => &sprite.bounds,
355 Primitive::PolychromeSprite(sprite) => &sprite.bounds,
356 Primitive::Surface(surface) => &surface.bounds,
357 }
358 }
359
360 pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
361 match self {
362 Primitive::Shadow(shadow) => &shadow.content_mask,
363 Primitive::Quad(quad) => &quad.content_mask,
364 Primitive::Path(path) => &path.content_mask,
365 Primitive::Underline(underline) => &underline.content_mask,
366 Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
367 Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
368 Primitive::Surface(surface) => &surface.content_mask,
369 }
370 }
371}
372
373#[cfg_attr(
374 all(
375 any(target_os = "linux", target_os = "freebsd"),
376 not(any(feature = "x11", feature = "wayland"))
377 ),
378 allow(dead_code)
379)]
380struct BatchIterator<'a> {
381 shadows: &'a [Shadow],
382 shadows_start: usize,
383 shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
384 quads: &'a [Quad],
385 quads_start: usize,
386 quads_iter: Peekable<slice::Iter<'a, Quad>>,
387 paths: &'a [Path<ScaledPixels>],
388 paths_start: usize,
389 paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
390 underlines: &'a [Underline],
391 underlines_start: usize,
392 underlines_iter: Peekable<slice::Iter<'a, Underline>>,
393 monochrome_sprites: &'a [MonochromeSprite],
394 monochrome_sprites_start: usize,
395 monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
396 polychrome_sprites: &'a [PolychromeSprite],
397 polychrome_sprites_start: usize,
398 polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
399 surfaces: &'a [PaintSurface],
400 surfaces_start: usize,
401 surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
402}
403
404impl<'a> Iterator for BatchIterator<'a> {
405 type Item = PrimitiveBatch<'a>;
406
407 fn next(&mut self) -> Option<Self::Item> {
408 let mut orders_and_kinds = [
409 (
410 self.shadows_iter.peek().map(|s| s.order),
411 PrimitiveKind::Shadow,
412 ),
413 (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
414 (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
415 (
416 self.underlines_iter.peek().map(|u| u.order),
417 PrimitiveKind::Underline,
418 ),
419 (
420 self.monochrome_sprites_iter.peek().map(|s| s.order),
421 PrimitiveKind::MonochromeSprite,
422 ),
423 (
424 self.polychrome_sprites_iter.peek().map(|s| s.order),
425 PrimitiveKind::PolychromeSprite,
426 ),
427 (
428 self.surfaces_iter.peek().map(|s| s.order),
429 PrimitiveKind::Surface,
430 ),
431 ];
432 orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
433
434 let first = orders_and_kinds[0];
435 let second = orders_and_kinds[1];
436 let (batch_kind, max_order_and_kind) = if first.0.is_some() {
437 (first.1, (second.0.unwrap_or(u32::MAX), second.1))
438 } else {
439 return None;
440 };
441
442 match batch_kind {
443 PrimitiveKind::Shadow => {
444 let shadows_start = self.shadows_start;
445 let mut shadows_end = shadows_start + 1;
446 self.shadows_iter.next();
447 while self
448 .shadows_iter
449 .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
450 .is_some()
451 {
452 shadows_end += 1;
453 }
454 self.shadows_start = shadows_end;
455 Some(PrimitiveBatch::Shadows(
456 &self.shadows[shadows_start..shadows_end],
457 ))
458 }
459 PrimitiveKind::Quad => {
460 let quads_start = self.quads_start;
461 let mut quads_end = quads_start + 1;
462 self.quads_iter.next();
463 while self
464 .quads_iter
465 .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
466 .is_some()
467 {
468 quads_end += 1;
469 }
470 self.quads_start = quads_end;
471 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
472 }
473 PrimitiveKind::Path => {
474 let paths_start = self.paths_start;
475 let mut paths_end = paths_start + 1;
476 self.paths_iter.next();
477 while self
478 .paths_iter
479 .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
480 .is_some()
481 {
482 paths_end += 1;
483 }
484 self.paths_start = paths_end;
485 Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
486 }
487 PrimitiveKind::Underline => {
488 let underlines_start = self.underlines_start;
489 let mut underlines_end = underlines_start + 1;
490 self.underlines_iter.next();
491 while self
492 .underlines_iter
493 .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
494 .is_some()
495 {
496 underlines_end += 1;
497 }
498 self.underlines_start = underlines_end;
499 Some(PrimitiveBatch::Underlines(
500 &self.underlines[underlines_start..underlines_end],
501 ))
502 }
503 PrimitiveKind::MonochromeSprite => {
504 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
505 let sprites_start = self.monochrome_sprites_start;
506 let mut sprites_end = sprites_start + 1;
507 self.monochrome_sprites_iter.next();
508 while self
509 .monochrome_sprites_iter
510 .next_if(|sprite| {
511 (sprite.order, batch_kind) < max_order_and_kind
512 && sprite.tile.texture_id == texture_id
513 })
514 .is_some()
515 {
516 sprites_end += 1;
517 }
518 self.monochrome_sprites_start = sprites_end;
519 Some(PrimitiveBatch::MonochromeSprites {
520 texture_id,
521 sprites: &self.monochrome_sprites[sprites_start..sprites_end],
522 })
523 }
524 PrimitiveKind::PolychromeSprite => {
525 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
526 let sprites_start = self.polychrome_sprites_start;
527 let mut sprites_end = self.polychrome_sprites_start + 1;
528 self.polychrome_sprites_iter.next();
529 while self
530 .polychrome_sprites_iter
531 .next_if(|sprite| {
532 (sprite.order, batch_kind) < max_order_and_kind
533 && sprite.tile.texture_id == texture_id
534 })
535 .is_some()
536 {
537 sprites_end += 1;
538 }
539 self.polychrome_sprites_start = sprites_end;
540 Some(PrimitiveBatch::PolychromeSprites {
541 texture_id,
542 sprites: &self.polychrome_sprites[sprites_start..sprites_end],
543 })
544 }
545 PrimitiveKind::Surface => {
546 let surfaces_start = self.surfaces_start;
547 let mut surfaces_end = surfaces_start + 1;
548 self.surfaces_iter.next();
549 while self
550 .surfaces_iter
551 .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
552 .is_some()
553 {
554 surfaces_end += 1;
555 }
556 self.surfaces_start = surfaces_end;
557 Some(PrimitiveBatch::Surfaces(
558 &self.surfaces[surfaces_start..surfaces_end],
559 ))
560 }
561 }
562 }
563}
564
565#[derive(Debug)]
566#[cfg_attr(
567 all(
568 any(target_os = "linux", target_os = "freebsd"),
569 not(any(feature = "x11", feature = "wayland"))
570 ),
571 allow(dead_code)
572)]
573pub(crate) enum PrimitiveBatch<'a> {
574 Shadows(&'a [Shadow]),
575 Quads(&'a [Quad]),
576 Paths(&'a [Path<ScaledPixels>]),
577 Underlines(&'a [Underline]),
578 MonochromeSprites {
579 texture_id: AtlasTextureId,
580 sprites: &'a [MonochromeSprite],
581 },
582 PolychromeSprites {
583 texture_id: AtlasTextureId,
584 sprites: &'a [PolychromeSprite],
585 },
586 Surfaces(&'a [PaintSurface]),
587}
588
589#[derive(Default, Debug, Clone)]
590#[repr(C)]
591pub(crate) struct Quad {
592 pub order: DrawOrder,
593 pub border_style: BorderStyle,
594 pub bounds: Bounds<ScaledPixels>,
595 pub content_mask: ContentMask<ScaledPixels>,
596 pub background: Background,
597 pub border_color: Hsla,
598 pub corner_radii: Corners<ScaledPixels>,
599 pub border_widths: Edges<ScaledPixels>,
600}
601
602impl From<Quad> for Primitive {
603 fn from(quad: Quad) -> Self {
604 Primitive::Quad(quad)
605 }
606}
607
608#[derive(Debug, Clone)]
609#[repr(C)]
610pub(crate) struct Underline {
611 pub order: DrawOrder,
612 pub pad: u32, // align to 8 bytes
613 pub bounds: Bounds<ScaledPixels>,
614 pub content_mask: ContentMask<ScaledPixels>,
615 pub color: Hsla,
616 pub thickness: ScaledPixels,
617 pub wavy: u32,
618}
619
620impl From<Underline> for Primitive {
621 fn from(underline: Underline) -> Self {
622 Primitive::Underline(underline)
623 }
624}
625
626#[derive(Debug, Clone)]
627#[repr(C)]
628pub(crate) struct Shadow {
629 pub order: DrawOrder,
630 pub blur_radius: ScaledPixels,
631 pub bounds: Bounds<ScaledPixels>,
632 pub corner_radii: Corners<ScaledPixels>,
633 pub content_mask: ContentMask<ScaledPixels>,
634 pub color: Hsla,
635}
636
637impl From<Shadow> for Primitive {
638 fn from(shadow: Shadow) -> Self {
639 Primitive::Shadow(shadow)
640 }
641}
642
643/// The style of a border.
644#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
645#[repr(C)]
646pub enum BorderStyle {
647 /// A solid border.
648 #[default]
649 Solid = 0,
650 /// A dashed border.
651 Dashed = 1,
652}
653
654/// A data type representing a 2 dimensional transformation that can be applied to an element.
655#[derive(Debug, Clone, Copy, PartialEq)]
656#[repr(C)]
657pub struct TransformationMatrix {
658 /// 2x2 matrix containing rotation and scale,
659 /// stored row-major
660 pub rotation_scale: [[f32; 2]; 2],
661 /// translation vector
662 pub translation: [f32; 2],
663}
664
665impl Eq for TransformationMatrix {}
666
667impl TransformationMatrix {
668 /// The unit matrix, has no effect.
669 pub fn unit() -> Self {
670 Self {
671 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
672 translation: [0.0, 0.0],
673 }
674 }
675
676 /// Move the origin by a given point
677 pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
678 self.compose(Self {
679 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
680 translation: [point.x.0, point.y.0],
681 })
682 }
683
684 /// Clockwise rotation in radians around the origin
685 pub fn rotate(self, angle: Radians) -> Self {
686 self.compose(Self {
687 rotation_scale: [
688 [angle.0.cos(), -angle.0.sin()],
689 [angle.0.sin(), angle.0.cos()],
690 ],
691 translation: [0.0, 0.0],
692 })
693 }
694
695 /// Scale around the origin
696 pub fn scale(self, size: Size<f32>) -> Self {
697 self.compose(Self {
698 rotation_scale: [[size.width, 0.0], [0.0, size.height]],
699 translation: [0.0, 0.0],
700 })
701 }
702
703 /// Perform matrix multiplication with another transformation
704 /// to produce a new transformation that is the result of
705 /// applying both transformations: first, `other`, then `self`.
706 #[inline]
707 pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
708 if other == Self::unit() {
709 return self;
710 }
711 // Perform matrix multiplication
712 TransformationMatrix {
713 rotation_scale: [
714 [
715 self.rotation_scale[0][0] * other.rotation_scale[0][0]
716 + self.rotation_scale[0][1] * other.rotation_scale[1][0],
717 self.rotation_scale[0][0] * other.rotation_scale[0][1]
718 + self.rotation_scale[0][1] * other.rotation_scale[1][1],
719 ],
720 [
721 self.rotation_scale[1][0] * other.rotation_scale[0][0]
722 + self.rotation_scale[1][1] * other.rotation_scale[1][0],
723 self.rotation_scale[1][0] * other.rotation_scale[0][1]
724 + self.rotation_scale[1][1] * other.rotation_scale[1][1],
725 ],
726 ],
727 translation: [
728 self.translation[0]
729 + self.rotation_scale[0][0] * other.translation[0]
730 + self.rotation_scale[0][1] * other.translation[1],
731 self.translation[1]
732 + self.rotation_scale[1][0] * other.translation[0]
733 + self.rotation_scale[1][1] * other.translation[1],
734 ],
735 }
736 }
737
738 /// Apply transformation to a point, mainly useful for debugging
739 pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
740 let input = [point.x.0, point.y.0];
741 let mut output = self.translation;
742 for (i, output_cell) in output.iter_mut().enumerate() {
743 for (k, input_cell) in input.iter().enumerate() {
744 *output_cell += self.rotation_scale[i][k] * *input_cell;
745 }
746 }
747 Point::new(output[0].into(), output[1].into())
748 }
749}
750
751impl Default for TransformationMatrix {
752 fn default() -> Self {
753 Self::unit()
754 }
755}
756
757#[derive(Clone, Debug)]
758#[repr(C)]
759pub(crate) struct MonochromeSprite {
760 pub order: DrawOrder,
761 pub pad: u32,
762 pub bounds: Bounds<ScaledPixels>,
763 pub content_mask: ContentMask<ScaledPixels>,
764 pub color: Hsla,
765 pub tile: AtlasTile,
766 pub transformation: TransformationMatrix,
767}
768
769impl From<MonochromeSprite> for Primitive {
770 fn from(sprite: MonochromeSprite) -> Self {
771 Primitive::MonochromeSprite(sprite)
772 }
773}
774
775#[derive(Clone, Debug)]
776#[repr(C)]
777pub(crate) struct PolychromeSprite {
778 pub order: DrawOrder,
779 pub pad: u32,
780 pub grayscale: bool,
781 pub opacity: f32,
782 pub bounds: Bounds<ScaledPixels>,
783 pub content_mask: ContentMask<ScaledPixels>,
784 pub corner_radii: Corners<ScaledPixels>,
785 pub tile: AtlasTile,
786}
787
788impl From<PolychromeSprite> for Primitive {
789 fn from(sprite: PolychromeSprite) -> Self {
790 Primitive::PolychromeSprite(sprite)
791 }
792}
793
794#[derive(Clone, Debug)]
795pub(crate) struct PaintSurface {
796 pub order: DrawOrder,
797 pub bounds: Bounds<ScaledPixels>,
798 pub content_mask: ContentMask<ScaledPixels>,
799 #[cfg(target_os = "macos")]
800 pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
801}
802
803impl From<PaintSurface> for Primitive {
804 fn from(surface: PaintSurface) -> Self {
805 Primitive::Surface(surface)
806 }
807}
808
809#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
810pub(crate) struct PathId(pub(crate) usize);
811
812/// A line made up of a series of vertices and control points.
813#[derive(Clone, Debug)]
814pub struct Path<P: Clone + Debug + Default + PartialEq> {
815 pub(crate) id: PathId,
816 pub(crate) order: DrawOrder,
817 pub(crate) bounds: Bounds<P>,
818 pub(crate) content_mask: ContentMask<P>,
819 pub(crate) vertices: Vec<PathVertex<P>>,
820 pub(crate) color: Background,
821 start: Point<P>,
822 current: Point<P>,
823 contour_count: usize,
824}
825
826impl Path<Pixels> {
827 /// Create a new path with the given starting point.
828 pub fn new(start: Point<Pixels>) -> Self {
829 Self {
830 id: PathId(0),
831 order: DrawOrder::default(),
832 vertices: Vec::new(),
833 start,
834 current: start,
835 bounds: Bounds {
836 origin: start,
837 size: Default::default(),
838 },
839 content_mask: Default::default(),
840 color: Default::default(),
841 contour_count: 0,
842 }
843 }
844
845 /// Scale this path by the given factor.
846 pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
847 Path {
848 id: self.id,
849 order: self.order,
850 bounds: self.bounds.scale(factor),
851 content_mask: self.content_mask.scale(factor),
852 vertices: self
853 .vertices
854 .iter()
855 .map(|vertex| vertex.scale(factor))
856 .collect(),
857 start: self.start.map(|start| start.scale(factor)),
858 current: self.current.scale(factor),
859 contour_count: self.contour_count,
860 color: self.color,
861 }
862 }
863
864 /// Move the start, current point to the given point.
865 pub fn move_to(&mut self, to: Point<Pixels>) {
866 self.contour_count += 1;
867 self.start = to;
868 self.current = to;
869 }
870
871 /// Draw a straight line from the current point to the given point.
872 pub fn line_to(&mut self, to: Point<Pixels>) {
873 self.contour_count += 1;
874 if self.contour_count > 1 {
875 self.push_triangle(
876 (self.start, self.current, to),
877 (point(0., 1.), point(0., 1.), point(0., 1.)),
878 );
879 }
880 self.current = to;
881 }
882
883 /// Draw a curve from the current point to the given point, using the given control point.
884 pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
885 self.contour_count += 1;
886 if self.contour_count > 1 {
887 self.push_triangle(
888 (self.start, self.current, to),
889 (point(0., 1.), point(0., 1.), point(0., 1.)),
890 );
891 }
892
893 self.push_triangle(
894 (self.current, ctrl, to),
895 (point(0., 0.), point(0.5, 0.), point(1., 1.)),
896 );
897 self.current = to;
898 }
899
900 /// Push a triangle to the Path.
901 pub fn push_triangle(
902 &mut self,
903 xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
904 st: (Point<f32>, Point<f32>, Point<f32>),
905 ) {
906 self.bounds = self
907 .bounds
908 .union(&Bounds {
909 origin: xy.0,
910 size: Default::default(),
911 })
912 .union(&Bounds {
913 origin: xy.1,
914 size: Default::default(),
915 })
916 .union(&Bounds {
917 origin: xy.2,
918 size: Default::default(),
919 });
920
921 self.vertices.push(PathVertex {
922 xy_position: xy.0,
923 st_position: st.0,
924 content_mask: Default::default(),
925 });
926 self.vertices.push(PathVertex {
927 xy_position: xy.1,
928 st_position: st.1,
929 content_mask: Default::default(),
930 });
931 self.vertices.push(PathVertex {
932 xy_position: xy.2,
933 st_position: st.2,
934 content_mask: Default::default(),
935 });
936 }
937}
938
939impl<T> Path<T>
940where
941 T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
942{
943 #[allow(unused)]
944 pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
945 self.bounds.intersect(&self.content_mask.bounds)
946 }
947}
948
949impl From<Path<ScaledPixels>> for Primitive {
950 fn from(path: Path<ScaledPixels>) -> Self {
951 Primitive::Path(path)
952 }
953}
954
955#[derive(Clone, Debug)]
956#[repr(C)]
957pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
958 pub(crate) xy_position: Point<P>,
959 pub(crate) st_position: Point<f32>,
960 pub(crate) content_mask: ContentMask<P>,
961}
962
963impl PathVertex<Pixels> {
964 pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
965 PathVertex {
966 xy_position: self.xy_position.scale(factor),
967 st_position: self.st_position,
968 content_mask: self.content_mask.scale(factor),
969 }
970 }
971}