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