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