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