1#![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)]
19#[allow(missing_docs)]
20pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
21
22#[allow(missing_docs)]
23pub type DrawOrder = u32;
24
25#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
28#[repr(transparent)]
29#[allow(missing_docs)]
30pub struct PaddedBool32(u32);
31
32impl From<bool> for PaddedBool32 {
33 fn from(value: bool) -> Self {
34 PaddedBool32(value as u32)
35 }
36}
37
38#[derive(Default)]
39#[allow(missing_docs)]
40pub struct Scene {
41 pub(crate) paint_operations: Vec<PaintOperation>,
42 primitive_bounds: BoundsTree<ScaledPixels>,
43 layer_stack: Vec<DrawOrder>,
44 pub shadows: Vec<Shadow>,
45 pub quads: Vec<Quad>,
46 pub paths: Vec<Path<ScaledPixels>>,
47 pub underlines: Vec<Underline>,
48 pub monochrome_sprites: Vec<MonochromeSprite>,
49 pub polychrome_sprites: Vec<PolychromeSprite>,
50 pub surfaces: Vec<PaintSurface>,
51}
52
53#[allow(missing_docs)]
54impl Scene {
55 pub(crate) fn clear(&mut self) {
56 self.paint_operations.clear();
57 self.primitive_bounds.clear();
58 self.layer_stack.clear();
59 self.paths.clear();
60 self.shadows.clear();
61 self.quads.clear();
62 self.underlines.clear();
63 self.monochrome_sprites.clear();
64 self.polychrome_sprites.clear();
65 self.surfaces.clear();
66 }
67
68 pub fn len(&self) -> usize {
69 self.paint_operations.len()
70 }
71
72 pub(crate) fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
73 let order = self.primitive_bounds.insert(bounds);
74 self.layer_stack.push(order);
75 self.paint_operations
76 .push(PaintOperation::StartLayer(bounds));
77 }
78
79 pub(crate) fn pop_layer(&mut self) {
80 self.layer_stack.pop();
81 self.paint_operations.push(PaintOperation::EndLayer);
82 }
83
84 pub(crate) fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
85 let primitive = primitive.into();
86 let clipped_bounds = primitive
87 .bounds()
88 .intersect(&primitive.content_mask().bounds);
89
90 if clipped_bounds.is_empty() {
91 return;
92 }
93
94 let order = self
95 .layer_stack
96 .last()
97 .copied()
98 .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds));
99 let (kind, index) = match primitive {
100 Primitive::Shadow(mut shadow) => {
101 shadow.order = order;
102 let idx = self.shadows.len();
103 self.shadows.push(shadow);
104 (PrimitiveKind::Shadow, idx)
105 }
106 Primitive::Quad(mut quad) => {
107 quad.order = order;
108 let idx = self.quads.len();
109 self.quads.push(quad);
110 (PrimitiveKind::Quad, idx)
111 }
112 Primitive::Path(mut path) => {
113 path.order = order;
114 path.id = PathId(self.paths.len());
115 let idx = self.paths.len();
116 self.paths.push(path);
117 (PrimitiveKind::Path, idx)
118 }
119 Primitive::Underline(mut underline) => {
120 underline.order = order;
121 let idx = self.underlines.len();
122 self.underlines.push(underline);
123 (PrimitiveKind::Underline, idx)
124 }
125 Primitive::MonochromeSprite(mut sprite) => {
126 sprite.order = order;
127 let idx = self.monochrome_sprites.len();
128 self.monochrome_sprites.push(sprite);
129 (PrimitiveKind::MonochromeSprite, idx)
130 }
131 Primitive::PolychromeSprite(mut sprite) => {
132 sprite.order = order;
133 let idx = self.polychrome_sprites.len();
134 self.polychrome_sprites.push(sprite);
135 (PrimitiveKind::PolychromeSprite, idx)
136 }
137 Primitive::Surface(mut surface) => {
138 surface.order = order;
139 let idx = self.surfaces.len();
140 self.surfaces.push(surface);
141 (PrimitiveKind::Surface, idx)
142 }
143 };
144 self.paint_operations
145 .push(PaintOperation::Primitive(kind, index));
146 }
147
148 pub(crate) fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
149 for operation in &prev_scene.paint_operations[range] {
150 match operation {
151 PaintOperation::Primitive(kind, index) => {
152 let primitive = match kind {
153 PrimitiveKind::Shadow => Primitive::Shadow(prev_scene.shadows[*index]),
154 PrimitiveKind::Quad => Primitive::Quad(prev_scene.quads[*index]),
155 PrimitiveKind::Path => Primitive::Path(prev_scene.paths[*index].clone()),
156 PrimitiveKind::Underline => {
157 Primitive::Underline(prev_scene.underlines[*index])
158 }
159 PrimitiveKind::MonochromeSprite => {
160 Primitive::MonochromeSprite(prev_scene.monochrome_sprites[*index])
161 }
162 PrimitiveKind::PolychromeSprite => {
163 Primitive::PolychromeSprite(prev_scene.polychrome_sprites[*index])
164 }
165 PrimitiveKind::Surface => {
166 Primitive::Surface(prev_scene.surfaces[*index].clone())
167 }
168 };
169 self.insert_primitive(primitive);
170 }
171 PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
172 PaintOperation::EndLayer => self.pop_layer(),
173 }
174 }
175 }
176
177 pub(crate) fn finish(&mut self) {
178 if !self.shadows.is_sorted_by_key(|s| s.order) {
181 self.shadows.sort_unstable_by_key(|s| s.order);
182 }
183 if !self.quads.is_sorted_by_key(|q| q.order) {
184 self.quads.sort_unstable_by_key(|q| q.order);
185 }
186 if !self.paths.is_sorted_by_key(|p| p.order) {
187 self.paths.sort_unstable_by_key(|p| p.order);
188 }
189 if !self.underlines.is_sorted_by_key(|u| u.order) {
190 self.underlines.sort_unstable_by_key(|u| u.order);
191 }
192 if !self
193 .monochrome_sprites
194 .is_sorted_by_key(|s| (s.order, s.tile.tile_id))
195 {
196 self.monochrome_sprites
197 .sort_unstable_by_key(|s| (s.order, s.tile.tile_id));
198 }
199 if !self
200 .polychrome_sprites
201 .is_sorted_by_key(|s| (s.order, s.tile.tile_id))
202 {
203 self.polychrome_sprites
204 .sort_unstable_by_key(|s| (s.order, s.tile.tile_id));
205 }
206 if !self.surfaces.is_sorted_by_key(|s| s.order) {
207 self.surfaces.sort_unstable_by_key(|s| s.order);
208 }
209 }
210
211 #[cfg_attr(
212 all(
213 any(target_os = "linux", target_os = "freebsd"),
214 not(any(feature = "x11", feature = "wayland"))
215 ),
216 allow(dead_code)
217 )]
218 pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch<'_>> {
219 BatchIterator {
220 shadows: &self.shadows,
221 shadows_start: 0,
222 shadows_iter: self.shadows.iter().peekable(),
223 quads: &self.quads,
224 quads_start: 0,
225 quads_iter: self.quads.iter().peekable(),
226 paths: &self.paths,
227 paths_start: 0,
228 paths_iter: self.paths.iter().peekable(),
229 underlines: &self.underlines,
230 underlines_start: 0,
231 underlines_iter: self.underlines.iter().peekable(),
232 monochrome_sprites: &self.monochrome_sprites,
233 monochrome_sprites_start: 0,
234 monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
235 polychrome_sprites: &self.polychrome_sprites,
236 polychrome_sprites_start: 0,
237 polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
238 surfaces: &self.surfaces,
239 surfaces_start: 0,
240 surfaces_iter: self.surfaces.iter().peekable(),
241 }
242 }
243}
244
245#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
246#[cfg_attr(
247 all(
248 any(target_os = "linux", target_os = "freebsd"),
249 not(any(feature = "x11", feature = "wayland"))
250 ),
251 allow(dead_code)
252)]
253pub(crate) enum PrimitiveKind {
254 Shadow,
255 #[default]
256 Quad,
257 Path,
258 Underline,
259 MonochromeSprite,
260 PolychromeSprite,
261 Surface,
262}
263
264pub(crate) enum PaintOperation {
265 Primitive(PrimitiveKind, usize),
266 StartLayer(Bounds<ScaledPixels>),
267 EndLayer,
268}
269
270#[derive(Clone)]
271pub(crate) enum Primitive {
272 Shadow(Shadow),
273 Quad(Quad),
274 Path(Path<ScaledPixels>),
275 Underline(Underline),
276 MonochromeSprite(MonochromeSprite),
277 PolychromeSprite(PolychromeSprite),
278 Surface(PaintSurface),
279}
280
281#[allow(missing_docs)]
282impl Primitive {
283 pub fn bounds(&self) -> &Bounds<ScaledPixels> {
284 match self {
285 Primitive::Shadow(shadow) => &shadow.bounds,
286 Primitive::Quad(quad) => &quad.bounds,
287 Primitive::Path(path) => &path.bounds,
288 Primitive::Underline(underline) => &underline.bounds,
289 Primitive::MonochromeSprite(sprite) => &sprite.bounds,
290 Primitive::PolychromeSprite(sprite) => &sprite.bounds,
291 Primitive::Surface(surface) => &surface.bounds,
292 }
293 }
294
295 pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
296 match self {
297 Primitive::Shadow(shadow) => &shadow.content_mask,
298 Primitive::Quad(quad) => &quad.content_mask,
299 Primitive::Path(path) => &path.content_mask,
300 Primitive::Underline(underline) => &underline.content_mask,
301 Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
302 Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
303 Primitive::Surface(surface) => &surface.content_mask,
304 }
305 }
306}
307
308#[cfg_attr(
309 all(
310 any(target_os = "linux", target_os = "freebsd"),
311 not(any(feature = "x11", feature = "wayland"))
312 ),
313 allow(dead_code)
314)]
315struct BatchIterator<'a> {
316 shadows: &'a [Shadow],
317 shadows_start: usize,
318 shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
319 quads: &'a [Quad],
320 quads_start: usize,
321 quads_iter: Peekable<slice::Iter<'a, Quad>>,
322 paths: &'a [Path<ScaledPixels>],
323 paths_start: usize,
324 paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
325 underlines: &'a [Underline],
326 underlines_start: usize,
327 underlines_iter: Peekable<slice::Iter<'a, Underline>>,
328 monochrome_sprites: &'a [MonochromeSprite],
329 monochrome_sprites_start: usize,
330 monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
331 polychrome_sprites: &'a [PolychromeSprite],
332 polychrome_sprites_start: usize,
333 polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
334 surfaces: &'a [PaintSurface],
335 surfaces_start: usize,
336 surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
337}
338
339impl<'a> Iterator for BatchIterator<'a> {
340 type Item = PrimitiveBatch<'a>;
341
342 fn next(&mut self) -> Option<Self::Item> {
343 let mut orders_and_kinds = [
344 (
345 self.shadows_iter.peek().map(|s| s.order),
346 PrimitiveKind::Shadow,
347 ),
348 (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
349 (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
350 (
351 self.underlines_iter.peek().map(|u| u.order),
352 PrimitiveKind::Underline,
353 ),
354 (
355 self.monochrome_sprites_iter.peek().map(|s| s.order),
356 PrimitiveKind::MonochromeSprite,
357 ),
358 (
359 self.polychrome_sprites_iter.peek().map(|s| s.order),
360 PrimitiveKind::PolychromeSprite,
361 ),
362 (
363 self.surfaces_iter.peek().map(|s| s.order),
364 PrimitiveKind::Surface,
365 ),
366 ];
367 orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
368
369 let first = orders_and_kinds[0];
370 let second = orders_and_kinds[1];
371 let (batch_kind, max_order_and_kind) = if first.0.is_some() {
372 (first.1, (second.0.unwrap_or(u32::MAX), second.1))
373 } else {
374 return None;
375 };
376
377 match batch_kind {
378 PrimitiveKind::Shadow => {
379 let shadows_start = self.shadows_start;
380 let mut shadows_end = shadows_start + 1;
381 self.shadows_iter.next();
382 while self
383 .shadows_iter
384 .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
385 .is_some()
386 {
387 shadows_end += 1;
388 }
389 self.shadows_start = shadows_end;
390 Some(PrimitiveBatch::Shadows(
391 &self.shadows[shadows_start..shadows_end],
392 ))
393 }
394 PrimitiveKind::Quad => {
395 let quads_start = self.quads_start;
396 let mut quads_end = quads_start + 1;
397 let first = self.quads_iter.next().unwrap();
398 while self
399 .quads_iter
400 .next_if(|quad| {
401 first.blend_mode == 0
402 && quad.blend_mode == 0
403 && (quad.order, batch_kind) < max_order_and_kind
404 })
405 .is_some()
406 {
407 quads_end += 1;
408 }
409 self.quads_start = quads_end;
410 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
411 }
412 PrimitiveKind::Path => {
413 let paths_start = self.paths_start;
414 let mut paths_end = paths_start + 1;
415 self.paths_iter.next();
416 while self
417 .paths_iter
418 .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
419 .is_some()
420 {
421 paths_end += 1;
422 }
423 self.paths_start = paths_end;
424 Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
425 }
426 PrimitiveKind::Underline => {
427 let underlines_start = self.underlines_start;
428 let mut underlines_end = underlines_start + 1;
429 self.underlines_iter.next();
430 while self
431 .underlines_iter
432 .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
433 .is_some()
434 {
435 underlines_end += 1;
436 }
437 self.underlines_start = underlines_end;
438 Some(PrimitiveBatch::Underlines(
439 &self.underlines[underlines_start..underlines_end],
440 ))
441 }
442 PrimitiveKind::MonochromeSprite => {
443 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
444 let sprites_start = self.monochrome_sprites_start;
445 let mut sprites_end = sprites_start + 1;
446 self.monochrome_sprites_iter.next();
447 while self
448 .monochrome_sprites_iter
449 .next_if(|sprite| {
450 (sprite.order, batch_kind) < max_order_and_kind
451 && sprite.tile.texture_id == texture_id
452 })
453 .is_some()
454 {
455 sprites_end += 1;
456 }
457 self.monochrome_sprites_start = sprites_end;
458 Some(PrimitiveBatch::MonochromeSprites {
459 texture_id,
460 sprites: &self.monochrome_sprites[sprites_start..sprites_end],
461 })
462 }
463 PrimitiveKind::PolychromeSprite => {
464 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
465 let sprites_start = self.polychrome_sprites_start;
466 let mut sprites_end = self.polychrome_sprites_start + 1;
467 self.polychrome_sprites_iter.next();
468 while self
469 .polychrome_sprites_iter
470 .next_if(|sprite| {
471 (sprite.order, batch_kind) < max_order_and_kind
472 && sprite.tile.texture_id == texture_id
473 })
474 .is_some()
475 {
476 sprites_end += 1;
477 }
478 self.polychrome_sprites_start = sprites_end;
479 Some(PrimitiveBatch::PolychromeSprites {
480 texture_id,
481 sprites: &self.polychrome_sprites[sprites_start..sprites_end],
482 })
483 }
484 PrimitiveKind::Surface => {
485 let surfaces_start = self.surfaces_start;
486 let mut surfaces_end = surfaces_start + 1;
487 self.surfaces_iter.next();
488 while self
489 .surfaces_iter
490 .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
491 .is_some()
492 {
493 surfaces_end += 1;
494 }
495 self.surfaces_start = surfaces_end;
496 Some(PrimitiveBatch::Surfaces(
497 &self.surfaces[surfaces_start..surfaces_end],
498 ))
499 }
500 }
501 }
502}
503
504#[derive(Debug)]
505#[cfg_attr(
506 all(
507 any(target_os = "linux", target_os = "freebsd"),
508 not(any(feature = "x11", feature = "wayland"))
509 ),
510 allow(dead_code)
511)]
512#[allow(missing_docs)]
513pub enum PrimitiveBatch<'a> {
514 Shadows(&'a [Shadow]),
515 Quads(&'a [Quad]),
516 Paths(&'a [Path<ScaledPixels>]),
517 Underlines(&'a [Underline]),
518 MonochromeSprites {
519 texture_id: AtlasTextureId,
520 sprites: &'a [MonochromeSprite],
521 },
522 PolychromeSprites {
523 texture_id: AtlasTextureId,
524 sprites: &'a [PolychromeSprite],
525 },
526 #[allow(dead_code)]
528 Surfaces(&'a [PaintSurface]),
529}
530
531#[derive(Default, Debug, Copy, Clone)]
532#[repr(C)]
533#[allow(missing_docs)]
534pub struct Quad {
535 pub order: DrawOrder,
536 pub border_style: BorderStyle,
537 pub bounds: Bounds<ScaledPixels>,
538 pub content_mask: ContentMask<ScaledPixels>,
539 pub background: Background,
540 pub border_color: Hsla,
541 pub corner_radii: Corners<ScaledPixels>,
542 pub border_widths: Edges<ScaledPixels>,
543 pub continuous_corners: u32,
544 pub _pad_before_transform: u32, pub transform: TransformationMatrix,
546 pub blend_mode: u32,
547 pub _pad_end: u32, }
549
550impl From<Quad> for Primitive {
551 fn from(quad: Quad) -> Self {
552 Primitive::Quad(quad)
553 }
554}
555
556#[derive(Debug, Copy, Clone)]
557#[repr(C)]
558#[allow(missing_docs)]
559pub struct Underline {
560 pub order: DrawOrder,
561 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
563 pub content_mask: ContentMask<ScaledPixels>,
564 pub color: Hsla,
565 pub thickness: ScaledPixels,
566 pub wavy: PaddedBool32,
567}
568
569impl From<Underline> for Primitive {
570 fn from(underline: Underline) -> Self {
571 Primitive::Underline(underline)
572 }
573}
574
575#[derive(Debug, Copy, Clone)]
576#[repr(C)]
577#[allow(missing_docs)]
578pub struct Shadow {
579 pub order: DrawOrder,
580 pub blur_radius: ScaledPixels,
581 pub bounds: Bounds<ScaledPixels>,
582 pub corner_radii: Corners<ScaledPixels>,
583 pub content_mask: ContentMask<ScaledPixels>,
584 pub color: Hsla,
585 pub inset: u32,
586 pub _pad_end: u32,
587}
588
589impl From<Shadow> for Primitive {
590 fn from(shadow: Shadow) -> Self {
591 Primitive::Shadow(shadow)
592 }
593}
594
595#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
597#[repr(C)]
598pub enum BlendMode {
599 #[default]
601 Normal = 0,
602 Multiply = 1,
604 Screen = 2,
606 Overlay = 3,
608 SoftLight = 4,
610 Difference = 5,
612}
613
614#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
616#[repr(C)]
617pub enum BorderStyle {
618 #[default]
620 Solid = 0,
621 Dashed = 1,
623}
624
625#[derive(Debug, Clone, Copy, PartialEq)]
627#[repr(C)]
628pub struct TransformationMatrix {
629 pub rotation_scale: [[f32; 2]; 2],
632 pub translation: [f32; 2],
634}
635
636impl Eq for TransformationMatrix {}
637
638impl TransformationMatrix {
639 pub fn unit() -> Self {
641 Self {
642 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
643 translation: [0.0, 0.0],
644 }
645 }
646
647 pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
649 self.compose(Self {
650 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
651 translation: [point.x.0, point.y.0],
652 })
653 }
654
655 pub fn rotate(self, angle: Radians) -> Self {
657 self.compose(Self {
658 rotation_scale: [
659 [angle.0.cos(), -angle.0.sin()],
660 [angle.0.sin(), angle.0.cos()],
661 ],
662 translation: [0.0, 0.0],
663 })
664 }
665
666 pub fn scale(self, size: Size<f32>) -> Self {
668 self.compose(Self {
669 rotation_scale: [[size.width, 0.0], [0.0, size.height]],
670 translation: [0.0, 0.0],
671 })
672 }
673
674 #[inline]
678 pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
679 if other == Self::unit() {
680 return self;
681 }
682 TransformationMatrix {
684 rotation_scale: [
685 [
686 self.rotation_scale[0][0] * other.rotation_scale[0][0]
687 + self.rotation_scale[0][1] * other.rotation_scale[1][0],
688 self.rotation_scale[0][0] * other.rotation_scale[0][1]
689 + self.rotation_scale[0][1] * other.rotation_scale[1][1],
690 ],
691 [
692 self.rotation_scale[1][0] * other.rotation_scale[0][0]
693 + self.rotation_scale[1][1] * other.rotation_scale[1][0],
694 self.rotation_scale[1][0] * other.rotation_scale[0][1]
695 + self.rotation_scale[1][1] * other.rotation_scale[1][1],
696 ],
697 ],
698 translation: [
699 self.translation[0]
700 + self.rotation_scale[0][0] * other.translation[0]
701 + self.rotation_scale[0][1] * other.translation[1],
702 self.translation[1]
703 + self.rotation_scale[1][0] * other.translation[0]
704 + self.rotation_scale[1][1] * other.translation[1],
705 ],
706 }
707 }
708
709 pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
711 let input = [point.x.0, point.y.0];
712 let mut output = self.translation;
713 for (i, output_cell) in output.iter_mut().enumerate() {
714 for (k, input_cell) in input.iter().enumerate() {
715 *output_cell += self.rotation_scale[i][k] * *input_cell;
716 }
717 }
718 Point::new(output[0].into(), output[1].into())
719 }
720}
721
722impl Default for TransformationMatrix {
723 fn default() -> Self {
724 Self::unit()
725 }
726}
727
728#[derive(Copy, Clone, Debug)]
729#[repr(C)]
730#[allow(missing_docs)]
731pub struct MonochromeSprite {
732 pub order: DrawOrder,
733 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
735 pub content_mask: ContentMask<ScaledPixels>,
736 pub color: Hsla,
737 pub tile: AtlasTile,
738 pub transformation: TransformationMatrix,
739}
740
741impl From<MonochromeSprite> for Primitive {
742 fn from(sprite: MonochromeSprite) -> Self {
743 Primitive::MonochromeSprite(sprite)
744 }
745}
746
747#[derive(Copy, Clone, Debug)]
748#[repr(C)]
749#[allow(missing_docs)]
750pub struct PolychromeSprite {
751 pub order: DrawOrder,
752 pub pad: u32, pub grayscale: PaddedBool32,
754 pub opacity: f32,
755 pub bounds: Bounds<ScaledPixels>,
756 pub content_mask: ContentMask<ScaledPixels>,
757 pub corner_radii: Corners<ScaledPixels>,
758 pub tile: AtlasTile,
759}
760
761impl From<PolychromeSprite> for Primitive {
762 fn from(sprite: PolychromeSprite) -> Self {
763 Primitive::PolychromeSprite(sprite)
764 }
765}
766
767#[derive(Clone, Debug)]
768#[allow(missing_docs)]
769pub struct PaintSurface {
770 pub order: DrawOrder,
771 pub bounds: Bounds<ScaledPixels>,
772 pub content_mask: ContentMask<ScaledPixels>,
773 #[cfg(target_os = "macos")]
774 pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
775}
776
777impl From<PaintSurface> for Primitive {
778 fn from(surface: PaintSurface) -> Self {
779 Primitive::Surface(surface)
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::{bounds, point, size, transparent_black};
787 use std::mem::{align_of, size_of};
788
789 fn test_quad(blend_mode: BlendMode) -> Quad {
790 let bounds = bounds(
791 point(0.0.into(), 0.0.into()),
792 size(10.0.into(), 10.0.into()),
793 );
794 Quad {
795 bounds,
796 content_mask: ContentMask { bounds },
797 background: transparent_black().into(),
798 blend_mode: blend_mode as u32,
799 ..Default::default()
800 }
801 }
802
803 #[test]
804 fn non_normal_blend_mode_quads_are_not_batched() {
805 let mut scene = Scene::default();
806 scene.insert_primitive(test_quad(BlendMode::Normal));
807 scene.insert_primitive(test_quad(BlendMode::Normal));
808 scene.insert_primitive(test_quad(BlendMode::Multiply));
809 scene.insert_primitive(test_quad(BlendMode::Normal));
810 scene.insert_primitive(test_quad(BlendMode::Screen));
811 scene.finish();
812
813 let batch_lengths = scene
814 .batches()
815 .filter_map(|batch| match batch {
816 PrimitiveBatch::Quads(quads) => Some(quads.len()),
817 _ => None,
818 })
819 .collect::<Vec<_>>();
820
821 assert_eq!(batch_lengths, vec![2, 1, 1, 1]);
822 }
823
824 #[test]
825 fn padded_bool_matches_gpu_scalar_layout() {
826 assert_eq!(size_of::<PaddedBool32>(), size_of::<u32>());
827 assert_eq!(align_of::<PaddedBool32>(), align_of::<u32>());
828 }
829}
830
831#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
832pub(crate) struct PathId(pub(crate) usize);
833
834#[derive(Clone, Debug)]
836#[allow(missing_docs)]
837pub struct Path<P: Clone + Debug + Default + PartialEq> {
838 pub(crate) id: PathId,
839 pub order: DrawOrder,
840 pub bounds: Bounds<P>,
841 pub content_mask: ContentMask<P>,
842 pub vertices: Vec<PathVertex<P>>,
843 pub color: Background,
844 start: Point<P>,
845 current: Point<P>,
846 contour_count: usize,
847}
848
849impl Path<Pixels> {
850 pub fn new(start: Point<Pixels>) -> Self {
852 Self {
853 id: PathId(0),
854 order: DrawOrder::default(),
855 vertices: Vec::new(),
856 start,
857 current: start,
858 bounds: Bounds {
859 origin: start,
860 size: Default::default(),
861 },
862 content_mask: Default::default(),
863 color: Default::default(),
864 contour_count: 0,
865 }
866 }
867
868 pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
870 Path {
871 id: self.id,
872 order: self.order,
873 bounds: self.bounds.scale(factor),
874 content_mask: self.content_mask.scale(factor),
875 vertices: self
876 .vertices
877 .iter()
878 .map(|vertex| vertex.scale(factor))
879 .collect(),
880 start: self.start.map(|start| start.scale(factor)),
881 current: self.current.scale(factor),
882 contour_count: self.contour_count,
883 color: self.color,
884 }
885 }
886
887 pub fn move_to(&mut self, to: Point<Pixels>) {
889 self.contour_count += 1;
890 self.start = to;
891 self.current = to;
892 }
893
894 pub fn line_to(&mut self, to: Point<Pixels>) {
896 self.contour_count += 1;
897 if self.contour_count > 1 {
898 self.push_triangle(
899 (self.start, self.current, to),
900 (point(0., 1.), point(0., 1.), point(0., 1.)),
901 );
902 }
903 self.current = to;
904 }
905
906 pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
908 self.contour_count += 1;
909 if self.contour_count > 1 {
910 self.push_triangle(
911 (self.start, self.current, to),
912 (point(0., 1.), point(0., 1.), point(0., 1.)),
913 );
914 }
915
916 self.push_triangle(
917 (self.current, ctrl, to),
918 (point(0., 0.), point(0.5, 0.), point(1., 1.)),
919 );
920 self.current = to;
921 }
922
923 pub fn push_triangle(
925 &mut self,
926 xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
927 st: (Point<f32>, Point<f32>, Point<f32>),
928 ) {
929 self.bounds = self
930 .bounds
931 .union(&Bounds {
932 origin: xy.0,
933 size: Default::default(),
934 })
935 .union(&Bounds {
936 origin: xy.1,
937 size: Default::default(),
938 })
939 .union(&Bounds {
940 origin: xy.2,
941 size: Default::default(),
942 });
943
944 self.vertices.push(PathVertex {
945 xy_position: xy.0,
946 st_position: st.0,
947 content_mask: Default::default(),
948 });
949 self.vertices.push(PathVertex {
950 xy_position: xy.1,
951 st_position: st.1,
952 content_mask: Default::default(),
953 });
954 self.vertices.push(PathVertex {
955 xy_position: xy.2,
956 st_position: st.2,
957 content_mask: Default::default(),
958 });
959 }
960}
961
962impl<T> Path<T>
963where
964 T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
965{
966 #[allow(unused)]
967 #[allow(missing_docs)]
968 pub fn clipped_bounds(&self) -> Bounds<T> {
969 self.bounds.intersect(&self.content_mask.bounds)
970 }
971}
972
973impl From<Path<ScaledPixels>> for Primitive {
974 fn from(path: Path<ScaledPixels>) -> Self {
975 Primitive::Path(path)
976 }
977}
978
979#[derive(Clone, Debug)]
980#[repr(C)]
981#[allow(missing_docs)]
982pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
983 pub xy_position: Point<P>,
984 pub st_position: Point<f32>,
985 pub content_mask: ContentMask<P>,
986}
987
988#[allow(missing_docs)]
989impl PathVertex<Pixels> {
990 pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
991 PathVertex {
992 xy_position: self.xy_position.scale(factor),
993 st_position: self.st_position,
994 content_mask: self.content_mask.scale(factor),
995 }
996 }
997}