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