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