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