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