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