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