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