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