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::{
12 fmt::Debug,
13 iter::Peekable,
14 ops::{Add, Range, Sub},
15 slice,
16};
17
18#[allow(non_camel_case_types, unused)]
19pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
20
21pub(crate) type DrawOrder = u32;
22
23#[derive(Default)]
24pub(crate) struct Scene {
25 pub(crate) paint_operations: Vec<PaintOperation>,
26 primitive_bounds: BoundsTree<ScaledPixels>,
27 layer_stack: Vec<DrawOrder>,
28 pub(crate) shadows: Vec<Shadow>,
29 pub(crate) quads: Vec<Quad>,
30 pub(crate) paths: Vec<Path<ScaledPixels>>,
31 pub(crate) underlines: Vec<Underline>,
32 pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
33 pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
34 pub(crate) surfaces: Vec<PaintSurface>,
35}
36
37impl Scene {
38 pub fn clear(&mut self) {
39 self.paint_operations.clear();
40 self.primitive_bounds.clear();
41 self.layer_stack.clear();
42 self.paths.clear();
43 self.shadows.clear();
44 self.quads.clear();
45 self.underlines.clear();
46 self.monochrome_sprites.clear();
47 self.polychrome_sprites.clear();
48 self.surfaces.clear();
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: u32,
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 pub(crate) 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}
687
688impl Path<Pixels> {
689 /// Create a new path with the given starting point.
690 pub fn new(start: Point<Pixels>) -> Self {
691 Self {
692 id: PathId(0),
693 order: DrawOrder::default(),
694 vertices: Vec::new(),
695 start,
696 current: start,
697 bounds: Bounds {
698 origin: start,
699 size: Default::default(),
700 },
701 content_mask: Default::default(),
702 color: Default::default(),
703 contour_count: 0,
704 }
705 }
706
707 /// Scale this path by the given factor.
708 pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
709 Path {
710 id: self.id,
711 order: self.order,
712 bounds: self.bounds.scale(factor),
713 content_mask: self.content_mask.scale(factor),
714 vertices: self
715 .vertices
716 .iter()
717 .map(|vertex| vertex.scale(factor))
718 .collect(),
719 start: self.start.map(|start| start.scale(factor)),
720 current: self.current.scale(factor),
721 contour_count: self.contour_count,
722 color: self.color,
723 }
724 }
725
726 /// Move the start, current point to the given point.
727 pub fn move_to(&mut self, to: Point<Pixels>) {
728 self.contour_count += 1;
729 self.start = to;
730 self.current = to;
731 }
732
733 /// Draw a straight line from the current point to the given point.
734 pub fn line_to(&mut self, to: Point<Pixels>) {
735 self.contour_count += 1;
736 if self.contour_count > 1 {
737 self.push_triangle(
738 (self.start, self.current, to),
739 (point(0., 1.), point(0., 1.), point(0., 1.)),
740 );
741 }
742 self.current = to;
743 }
744
745 /// Draw a curve from the current point to the given point, using the given control point.
746 pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
747 self.contour_count += 1;
748 if self.contour_count > 1 {
749 self.push_triangle(
750 (self.start, self.current, to),
751 (point(0., 1.), point(0., 1.), point(0., 1.)),
752 );
753 }
754
755 self.push_triangle(
756 (self.current, ctrl, to),
757 (point(0., 0.), point(0.5, 0.), point(1., 1.)),
758 );
759 self.current = to;
760 }
761
762 /// Push a triangle to the Path.
763 pub fn push_triangle(
764 &mut self,
765 xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
766 st: (Point<f32>, Point<f32>, Point<f32>),
767 ) {
768 self.bounds = self
769 .bounds
770 .union(&Bounds {
771 origin: xy.0,
772 size: Default::default(),
773 })
774 .union(&Bounds {
775 origin: xy.1,
776 size: Default::default(),
777 })
778 .union(&Bounds {
779 origin: xy.2,
780 size: Default::default(),
781 });
782
783 self.vertices.push(PathVertex {
784 xy_position: xy.0,
785 st_position: st.0,
786 content_mask: Default::default(),
787 });
788 self.vertices.push(PathVertex {
789 xy_position: xy.1,
790 st_position: st.1,
791 content_mask: Default::default(),
792 });
793 self.vertices.push(PathVertex {
794 xy_position: xy.2,
795 st_position: st.2,
796 content_mask: Default::default(),
797 });
798 }
799}
800
801impl<T> Path<T>
802where
803 T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
804{
805 #[allow(unused)]
806 pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
807 self.bounds.intersect(&self.content_mask.bounds)
808 }
809}
810
811impl From<Path<ScaledPixels>> for Primitive {
812 fn from(path: Path<ScaledPixels>) -> Self {
813 Primitive::Path(path)
814 }
815}
816
817#[derive(Clone, Debug)]
818#[repr(C)]
819pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
820 pub(crate) xy_position: Point<P>,
821 pub(crate) st_position: Point<f32>,
822 pub(crate) content_mask: ContentMask<P>,
823}
824
825impl PathVertex<Pixels> {
826 pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
827 PathVertex {
828 xy_position: self.xy_position.scale(factor),
829 st_position: self.st_position,
830 content_mask: self.content_mask.scale(factor),
831 }
832 }
833}