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#[expect(missing_docs)]
20pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
21
22#[expect(missing_docs)]
23pub type DrawOrder = u32;
24
25#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
30#[repr(transparent)]
31pub struct PaddedBool32(u32);
32
33impl From<bool> for PaddedBool32 {
34 fn from(value: bool) -> Self {
35 PaddedBool32(value as u32)
36 }
37}
38
39#[derive(Default)]
40#[expect(missing_docs)]
41pub struct Scene {
42 pub(crate) paint_operations: Vec<PaintOperation>,
43 primitive_bounds: BoundsTree<ScaledPixels>,
44 layer_stack: Vec<DrawOrder>,
45 pub shadows: Vec<Shadow>,
46 pub quads: Vec<Quad>,
47 pub paths: Vec<Path<ScaledPixels>>,
48 pub underlines: Vec<Underline>,
49 pub monochrome_sprites: Vec<MonochromeSprite>,
50 pub subpixel_sprites: Vec<SubpixelSprite>,
51 pub polychrome_sprites: Vec<PolychromeSprite>,
52 pub surfaces: Vec<PaintSurface>,
53 pub custom_draws: Vec<CustomDraw>,
54 pub shader_draws: Vec<ShaderDraw>,
56 pub backdrops: Vec<crate::BackdropDraw>,
58 pub layer_boundaries: Vec<crate::LayerBoundary>,
60}
61
62#[expect(missing_docs)]
63impl Scene {
64 pub fn clear(&mut self) {
65 self.paint_operations.clear();
66 self.primitive_bounds.clear();
67 self.layer_stack.clear();
68 self.paths.clear();
69 self.shadows.clear();
70 self.quads.clear();
71 self.underlines.clear();
72 self.monochrome_sprites.clear();
73 self.subpixel_sprites.clear();
74 self.polychrome_sprites.clear();
75 self.surfaces.clear();
76 self.custom_draws.clear();
77 self.shader_draws.clear();
78 self.backdrops.clear();
79 self.layer_boundaries.clear();
80 }
81
82 pub fn len(&self) -> usize {
83 self.paint_operations.len()
84 }
85
86 pub fn push_layer(&mut self, bounds: Bounds<ScaledPixels>) {
87 let order = self.primitive_bounds.insert(bounds);
88 self.layer_stack.push(order);
89 self.paint_operations
90 .push(PaintOperation::StartLayer(bounds));
91 }
92
93 pub fn pop_layer(&mut self) {
94 self.layer_stack.pop();
95 self.paint_operations.push(PaintOperation::EndLayer);
96 }
97
98 pub fn insert_primitive(&mut self, primitive: impl Into<Primitive>) {
99 static NEXT_COMMAND: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
100 self.insert_retained_primitive(
101 primitive.into(),
102 NEXT_COMMAND.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
103 );
104 }
105
106 fn insert_retained_primitive(&mut self, mut primitive: Primitive, identity: u64) {
107 let clipped_bounds = primitive
108 .bounds()
109 .intersect(&primitive.content_mask().bounds);
110
111 if clipped_bounds.is_empty() {
112 return;
113 }
114
115 let order = if matches!(
118 primitive,
119 Primitive::Backdrop(_) | Primitive::LayerBoundary(_)
120 ) {
121 let fence = primitive.content_mask().bounds;
122 let order = self.primitive_bounds.insert(fence);
123 let after = self.primitive_bounds.insert(fence);
124 self.layer_stack.fill(after);
125 order
126 } else {
127 self.layer_stack
128 .last()
129 .copied()
130 .unwrap_or_else(|| self.primitive_bounds.insert(clipped_bounds))
131 };
132 match &mut primitive {
133 Primitive::LayerBoundary(draw) => {
134 draw.order = order;
135 self.layer_boundaries.push(draw.clone());
136 }
137 Primitive::Backdrop(draw) => {
138 draw.order = order;
139 self.backdrops.push(draw.clone());
140 }
141 Primitive::Shadow(shadow) => {
142 shadow.order = order;
143 self.shadows.push(*shadow);
144 }
145 Primitive::Quad(quad) => {
146 quad.order = order;
147 self.quads.push(*quad);
148 }
149 Primitive::Path(path) => {
150 path.order = order;
151 path.id = PathId(self.paths.len());
152 self.paths.push(path.clone());
153 }
154 Primitive::Underline(underline) => {
155 underline.order = order;
156 self.underlines.push(*underline);
157 }
158 Primitive::MonochromeSprite(sprite) => {
159 sprite.order = order;
160 self.monochrome_sprites.push(*sprite);
161 }
162 Primitive::SubpixelSprite(sprite) => {
163 sprite.order = order;
164 self.subpixel_sprites.push(*sprite);
165 }
166 Primitive::PolychromeSprite(sprite) => {
167 sprite.order = order;
168 self.polychrome_sprites.push(*sprite);
169 }
170 Primitive::Shader(draw) => {
171 draw.order = order;
172 self.shader_draws.push(draw.clone());
173 }
174 Primitive::Custom(draw) => {
175 draw.order = order;
176 self.custom_draws.push(draw.clone());
177 }
178 Primitive::Surface(surface) => {
179 surface.order = order;
180 self.surfaces.push(surface.clone());
181 }
182 }
183 self.paint_operations
184 .push(PaintOperation::Primitive(primitive, identity));
185 }
186
187 pub fn replay(&mut self, range: Range<usize>, prev_scene: &Scene) {
188 for operation in &prev_scene.paint_operations[range] {
189 match operation {
190 PaintOperation::Primitive(primitive, identity) => {
191 self.insert_retained_primitive(primitive.clone(), *identity)
192 }
193 PaintOperation::StartLayer(bounds) => self.push_layer(*bounds),
194 PaintOperation::EndLayer => self.pop_layer(),
195 }
196 }
197 }
198
199 pub fn begin_layer_capture(
201 &mut self,
202 capture: std::sync::Arc<crate::LayerCapture>,
203 viewport: ContentMask<ScaledPixels>,
204 ) {
205 self.insert_primitive(Primitive::LayerBoundary(crate::LayerBoundary {
206 order: 0,
207 bounds: viewport.bounds,
208 content_mask: viewport,
209 capture,
210 begin: true,
211 }));
212 }
213 pub fn end_layer_capture(
215 &mut self,
216 capture: std::sync::Arc<crate::LayerCapture>,
217 viewport: ContentMask<ScaledPixels>,
218 ) {
219 self.insert_primitive(Primitive::LayerBoundary(crate::LayerBoundary {
220 order: 0,
221 bounds: viewport.bounds,
222 content_mask: viewport,
223 capture,
224 begin: false,
225 }));
226 }
227 pub fn layer_plans(
229 &self,
230 viewport: Size<crate::DevicePixels>,
231 ) -> anyhow::Result<Vec<crate::LayerPlan>> {
232 use anyhow::ensure;
233 use std::sync::Arc;
234 if self.layer_boundaries.is_empty() {
235 return Ok(Vec::new());
236 }
237 ensure!(
238 self.layer_boundaries.len() <= crate::MAX_LAYER_GROUPS * 2,
239 "at most four captured groups per frame"
240 );
241 let mut plans: Vec<crate::LayerPlan> = Vec::new();
242 let mut revisions: Vec<Vec<u64>> = Vec::new();
243 let mut stack: Vec<(Arc<crate::LayerCapture>, usize)> = Vec::new();
244 for operation in &self.paint_operations {
245 let PaintOperation::Primitive(primitive, identity) = operation else {
246 continue;
247 };
248 let closing = matches!(primitive,Primitive::LayerBoundary(boundary) if !boundary.begin);
249 for (depth, (_, slot)) in stack.iter().enumerate() {
250 if closing && depth + 1 == stack.len() {
254 continue;
255 }
256 revisions[*slot].push(*identity);
257 ensure!(
258 revisions[*slot].len() <= crate::MAX_LAYER_COMMANDS,
259 "captured group exceeds 65536 commands"
260 );
261 }
262 if let Primitive::LayerBoundary(boundary) = primitive {
263 let index = self
264 .layer_boundaries
265 .iter()
266 .position(|b| {
267 b.order == boundary.order
268 && b.begin == boundary.begin
269 && Arc::ptr_eq(&b.capture, &boundary.capture)
270 })
271 .ok_or_else(|| anyhow::anyhow!("missing layer boundary"))?;
272 if boundary.begin {
273 ensure!(
274 plans.len() < crate::MAX_LAYER_GROUPS,
275 "at most four captured groups per frame"
276 );
277 let plan = crate::LayerPlan::new(&boundary.capture, viewport, index)?;
278 if let Some((_, parent)) = stack.last() {
279 let p = &plans[*parent];
280 ensure!(
281 plan.origin[0] >= p.origin[0]
282 && plan.origin[1] >= p.origin[1]
283 && plan.origin[0] + plan.size[0] <= p.origin[0] + p.size[0]
284 && plan.origin[1] + plan.size[1] <= p.origin[1] + p.size[1],
285 "nested layer escapes its parent capture"
286 );
287 }
288 let slot = plans.len();
289 plans.push(plan);
290 revisions.push(Vec::new());
291 stack.push((boundary.capture.clone(), slot));
292 } else {
293 let (capture, slot) = stack
294 .pop()
295 .ok_or_else(|| anyhow::anyhow!("unmatched layer end"))?;
296 ensure!(
297 Arc::ptr_eq(&capture, &boundary.capture),
298 "crossed layer boundaries"
299 );
300 plans[slot].end_index = index;
301 plans[slot].revision = std::mem::take(&mut revisions[slot]).into();
302 }
303 continue;
304 }
305 if !stack.is_empty() {
306 ensure!(
307 !matches!(primitive, Primitive::Backdrop(_)),
308 "backdrop sampling inside captured groups is not supported"
309 );
310 ensure!(
311 !matches!(primitive, Primitive::SubpixelSprite(_)),
312 "captured text requires grayscale glyph coverage"
313 );
314 }
315 for (_, slot) in &stack {
316 let plan = &mut plans[*slot];
317 if matches!(primitive, Primitive::Custom(_) | Primitive::Surface(_)) {
318 plan.reusable = false;
319 }
320 let clipped = primitive
321 .bounds()
322 .intersect(&primitive.content_mask().bounds);
323 ensure!(
324 clipped.is_empty()
325 || (clipped.left().0 >= plan.origin[0] as f32
326 && clipped.top().0 >= plan.origin[1] as f32
327 && clipped.right().0 <= (plan.origin[0] + plan.size[0]) as f32
328 && clipped.bottom().0 <= (plan.origin[1] + plan.size[1]) as f32),
329 "paint escapes its layer capture; apply the capture content mask"
330 );
331 }
332 }
333 ensure!(stack.is_empty(), "unclosed layer capture");
334 Ok(plans)
335 }
336
337 pub fn backdrop_cache_keys(
340 &self,
341 viewport: Size<crate::DevicePixels>,
342 ) -> anyhow::Result<Vec<crate::BackdropCacheKey>> {
343 if self.backdrops.is_empty() {
344 return Ok(Vec::new());
345 }
346 anyhow::ensure!(
347 self.backdrops.len() <= crate::MAX_BACKDROPS,
348 "at most four backdrop boundaries per frame"
349 );
350 let mut keys = Vec::new();
351 let mut revision = Vec::new();
352 let mut reusable = true;
353 for operation in &self.paint_operations {
354 let PaintOperation::Primitive(primitive, identity) = operation else {
355 continue;
356 };
357 if let Primitive::Backdrop(draw) = primitive {
358 let plan = draw.plan(viewport)?;
359 keys.push((
360 draw.order,
361 crate::BackdropCacheKey {
362 viewport,
363 origin: plan.origin,
364 size: plan.size,
365 filter: [plan.parameters[0], plan.parameters[1]],
366 revision: revision.clone().into(),
367 reusable,
368 },
369 ));
370 }
371 if matches!(primitive, Primitive::Custom(_) | Primitive::Surface(_)) {
373 reusable = false;
374 }
375 if revision.len() < crate::MAX_LAYER_COMMANDS {
376 let order = match primitive {
377 Primitive::Shadow(p) => p.order,
378 Primitive::Quad(p) => p.order,
379 Primitive::Path(p) => p.order,
380 Primitive::Underline(p) => p.order,
381 Primitive::MonochromeSprite(p) => p.order,
382 Primitive::SubpixelSprite(p) => p.order,
383 Primitive::PolychromeSprite(p) => p.order,
384 Primitive::Surface(p) => p.order,
385 Primitive::Custom(p) => p.order,
386 Primitive::Shader(p) => p.order,
387 Primitive::Backdrop(p) => p.order,
388 Primitive::LayerBoundary(p) => p.order,
389 };
390 revision.push((*identity, order));
391 } else {
392 reusable = false;
393 }
394 }
395 anyhow::ensure!(
396 keys.len() == self.backdrops.len(),
397 "backdrops require scene insertion/replay"
398 );
399 keys.sort_by_key(|(order, _)| *order);
400 Ok(keys.into_iter().map(|(_, key)| key).collect())
401 }
402
403 pub fn finish(&mut self) {
404 self.shadows.sort_by_key(|shadow| shadow.order);
405 self.quads.sort_by_key(|quad| quad.order);
406 self.paths.sort_by_key(|path| path.order);
407 self.underlines.sort_by_key(|underline| underline.order);
408 self.monochrome_sprites
409 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
410 self.subpixel_sprites
411 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
412 self.polychrome_sprites
413 .sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
414 self.surfaces.sort_by_key(|surface| surface.order);
415 self.custom_draws.sort_by_key(|draw| draw.order);
416 self.shader_draws.sort_by_key(|draw| draw.order);
417 self.backdrops.sort_by_key(|draw| draw.order);
418 self.layer_boundaries.sort_by_key(|draw| draw.order);
419 }
420
421 #[cfg_attr(
422 all(
423 any(target_os = "linux", target_os = "freebsd"),
424 not(any(feature = "x11", feature = "wayland"))
425 ),
426 allow(dead_code)
427 )]
428 pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
429 BatchIterator {
430 shadows_start: 0,
431 shadows_iter: self.shadows.iter().peekable(),
432 quads_start: 0,
433 quads_iter: self.quads.iter().peekable(),
434 paths_start: 0,
435 paths_iter: self.paths.iter().peekable(),
436 underlines_start: 0,
437 underlines_iter: self.underlines.iter().peekable(),
438 monochrome_sprites_start: 0,
439 monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
440 subpixel_sprites_start: 0,
441 subpixel_sprites_iter: self.subpixel_sprites.iter().peekable(),
442 polychrome_sprites_start: 0,
443 polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
444 layer_boundary_start: 0,
445 layer_boundary_iter: self.layer_boundaries.iter().peekable(),
446 backdrop_start: 0,
447 backdrop_iter: self.backdrops.iter().peekable(),
448 shader_start: 0,
449 shader_iter: self.shader_draws.iter().peekable(),
450 custom_start: 0,
451 custom_iter: self.custom_draws.iter().peekable(),
452 surfaces_start: 0,
453 surfaces_iter: self.surfaces.iter().peekable(),
454 }
455 }
456}
457
458#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
459#[cfg_attr(
460 all(
461 any(target_os = "linux", target_os = "freebsd"),
462 not(any(feature = "x11", feature = "wayland"))
463 ),
464 allow(dead_code)
465)]
466pub(crate) enum PrimitiveKind {
467 Shadow,
468 #[default]
469 Quad,
470 Path,
471 Underline,
472 MonochromeSprite,
473 SubpixelSprite,
474 PolychromeSprite,
475 Surface,
476 Custom,
477 Shader,
478 Backdrop,
479 LayerBoundary,
480}
481
482pub(crate) enum PaintOperation {
483 Primitive(Primitive, u64),
484 StartLayer(Bounds<ScaledPixels>),
485 EndLayer,
486}
487
488#[derive(Clone)]
489#[expect(missing_docs)]
490pub enum Primitive {
491 Shadow(Shadow),
492 Quad(Quad),
493 Path(Path<ScaledPixels>),
494 Underline(Underline),
495 MonochromeSprite(MonochromeSprite),
496 SubpixelSprite(SubpixelSprite),
497 PolychromeSprite(PolychromeSprite),
498 Surface(PaintSurface),
499 Custom(CustomDraw),
500 Shader(ShaderDraw),
501 Backdrop(crate::BackdropDraw),
502 LayerBoundary(crate::LayerBoundary),
503}
504
505#[expect(missing_docs)]
506impl Primitive {
507 pub fn bounds(&self) -> &Bounds<ScaledPixels> {
508 match self {
509 Primitive::Shadow(shadow) => &shadow.bounds,
510 Primitive::Quad(quad) => &quad.bounds,
511 Primitive::Path(path) => &path.bounds,
512 Primitive::Underline(underline) => &underline.bounds,
513 Primitive::MonochromeSprite(sprite) => &sprite.bounds,
514 Primitive::SubpixelSprite(sprite) => &sprite.bounds,
515 Primitive::PolychromeSprite(sprite) => &sprite.bounds,
516 Primitive::Surface(surface) => &surface.bounds,
517 Primitive::Custom(draw) => &draw.bounds,
518 Primitive::Shader(draw) => &draw.bounds,
519 Primitive::Backdrop(draw) => &draw.bounds,
520 Primitive::LayerBoundary(draw) => &draw.bounds,
521 }
522 }
523
524 pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
525 match self {
526 Primitive::Shadow(shadow) => &shadow.content_mask,
527 Primitive::Quad(quad) => &quad.content_mask,
528 Primitive::Path(path) => &path.content_mask,
529 Primitive::Underline(underline) => &underline.content_mask,
530 Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
531 Primitive::SubpixelSprite(sprite) => &sprite.content_mask,
532 Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
533 Primitive::Surface(surface) => &surface.content_mask,
534 Primitive::Custom(draw) => &draw.content_mask,
535 Primitive::Shader(draw) => &draw.content_mask,
536 Primitive::Backdrop(draw) => &draw.content_mask,
537 Primitive::LayerBoundary(draw) => &draw.content_mask,
538 }
539 }
540}
541
542#[cfg_attr(
543 all(
544 any(target_os = "linux", target_os = "freebsd"),
545 not(any(feature = "x11", feature = "wayland"))
546 ),
547 allow(dead_code)
548)]
549struct BatchIterator<'a> {
550 layer_boundary_start: usize,
551 layer_boundary_iter: Peekable<slice::Iter<'a, crate::LayerBoundary>>,
552 backdrop_start: usize,
553 backdrop_iter: Peekable<slice::Iter<'a, crate::BackdropDraw>>,
554 shader_start: usize,
555 shader_iter: Peekable<slice::Iter<'a, ShaderDraw>>,
556 custom_start: usize,
557 custom_iter: Peekable<slice::Iter<'a, CustomDraw>>,
558 shadows_start: usize,
559 shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
560 quads_start: usize,
561 quads_iter: Peekable<slice::Iter<'a, Quad>>,
562 paths_start: usize,
563 paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
564 underlines_start: usize,
565 underlines_iter: Peekable<slice::Iter<'a, Underline>>,
566 monochrome_sprites_start: usize,
567 monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
568 subpixel_sprites_start: usize,
569 subpixel_sprites_iter: Peekable<slice::Iter<'a, SubpixelSprite>>,
570 polychrome_sprites_start: usize,
571 polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
572 surfaces_start: usize,
573 surfaces_iter: Peekable<slice::Iter<'a, PaintSurface>>,
574}
575
576impl<'a> Iterator for BatchIterator<'a> {
577 type Item = PrimitiveBatch;
578
579 fn next(&mut self) -> Option<Self::Item> {
580 let mut orders_and_kinds = [
581 (
582 self.layer_boundary_iter.peek().map(|d| d.order),
583 PrimitiveKind::LayerBoundary,
584 ),
585 (
586 self.backdrop_iter.peek().map(|d| d.order),
587 PrimitiveKind::Backdrop,
588 ),
589 (
590 self.shader_iter.peek().map(|d| d.order),
591 PrimitiveKind::Shader,
592 ),
593 (
594 self.custom_iter.peek().map(|d| d.order),
595 PrimitiveKind::Custom,
596 ),
597 (
598 self.shadows_iter.peek().map(|s| s.order),
599 PrimitiveKind::Shadow,
600 ),
601 (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
602 (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
603 (
604 self.underlines_iter.peek().map(|u| u.order),
605 PrimitiveKind::Underline,
606 ),
607 (
608 self.monochrome_sprites_iter.peek().map(|s| s.order),
609 PrimitiveKind::MonochromeSprite,
610 ),
611 (
612 self.subpixel_sprites_iter.peek().map(|s| s.order),
613 PrimitiveKind::SubpixelSprite,
614 ),
615 (
616 self.polychrome_sprites_iter.peek().map(|s| s.order),
617 PrimitiveKind::PolychromeSprite,
618 ),
619 (
620 self.surfaces_iter.peek().map(|s| s.order),
621 PrimitiveKind::Surface,
622 ),
623 ];
624 orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
625
626 let first = orders_and_kinds[0];
627 let second = orders_and_kinds[1];
628 let (batch_kind, max_order_and_kind) = if first.0.is_some() {
629 (first.1, (second.0.unwrap_or(u32::MAX), second.1))
630 } else {
631 return None;
632 };
633
634 match batch_kind {
635 PrimitiveKind::LayerBoundary => {
636 let index = self.layer_boundary_start;
637 self.layer_boundary_iter.next();
638 self.layer_boundary_start += 1;
639 Some(PrimitiveBatch::LayerBoundary(index))
640 }
641 PrimitiveKind::Backdrop => {
642 let index = self.backdrop_start;
643 self.backdrop_iter.next();
644 self.backdrop_start += 1;
645 Some(PrimitiveBatch::Backdrop(index))
646 }
647 PrimitiveKind::Shader => {
648 let start = self.shader_start;
649 let first = self.shader_iter.next().unwrap();
650 let program_id = first.program.id();
651 let texture_id = first.glyph_mask.map(|tile| tile.texture_id);
652 self.shader_start += 1;
653 let mut coverage_bounds = Vec::new();
654 if first.path_mask.is_some() {
655 coverage_bounds.push(first.bounds.dilate(ScaledPixels(1.)));
656 }
657 while self
658 .shader_iter
659 .next_if(|draw| {
660 let compatible = draw.program.id() == program_id
661 && draw.glyph_mask.map(|tile| tile.texture_id) == texture_id
662 && (draw.order, batch_kind) < max_order_and_kind;
663 if !compatible || first.path_mask.is_some() != draw.path_mask.is_some() {
664 return false;
665 }
666 if first.path_mask.is_some() {
667 if coverage_bounds.len() >= 64 {
668 return false;
669 }
670 let bounds = draw.bounds.dilate(ScaledPixels(1.));
673 if coverage_bounds
674 .iter()
675 .any(|previous| previous.intersects(&bounds))
676 {
677 return false;
678 }
679 coverage_bounds.push(bounds);
680 }
681 true
682 })
683 .is_some()
684 {
685 self.shader_start += 1;
686 }
687 Some(PrimitiveBatch::Shaders {
688 program_id,
689 range: start..self.shader_start,
690 })
691 }
692 PrimitiveKind::Custom => {
693 let start = self.custom_start;
694 self.custom_iter.next();
695 self.custom_start += 1;
696 while self
697 .custom_iter
698 .next_if(|d| (d.order, batch_kind) < max_order_and_kind)
699 .is_some()
700 {
701 self.custom_start += 1;
702 }
703 Some(PrimitiveBatch::Custom(start..self.custom_start))
704 }
705 PrimitiveKind::Shadow => {
706 let shadows_start = self.shadows_start;
707 let mut shadows_end = shadows_start + 1;
708 self.shadows_iter.next();
709 while self
710 .shadows_iter
711 .next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
712 .is_some()
713 {
714 shadows_end += 1;
715 }
716 self.shadows_start = shadows_end;
717 Some(PrimitiveBatch::Shadows(shadows_start..shadows_end))
718 }
719 PrimitiveKind::Quad => {
720 let quads_start = self.quads_start;
721 let mut quads_end = quads_start + 1;
722 self.quads_iter.next();
723 while self
724 .quads_iter
725 .next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
726 .is_some()
727 {
728 quads_end += 1;
729 }
730 self.quads_start = quads_end;
731 Some(PrimitiveBatch::Quads(quads_start..quads_end))
732 }
733 PrimitiveKind::Path => {
734 let paths_start = self.paths_start;
735 let mut paths_end = paths_start + 1;
736 self.paths_iter.next();
737 while self
738 .paths_iter
739 .next_if(|path| (path.order, batch_kind) < max_order_and_kind)
740 .is_some()
741 {
742 paths_end += 1;
743 }
744 self.paths_start = paths_end;
745 Some(PrimitiveBatch::Paths(paths_start..paths_end))
746 }
747 PrimitiveKind::Underline => {
748 let underlines_start = self.underlines_start;
749 let mut underlines_end = underlines_start + 1;
750 self.underlines_iter.next();
751 while self
752 .underlines_iter
753 .next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
754 .is_some()
755 {
756 underlines_end += 1;
757 }
758 self.underlines_start = underlines_end;
759 Some(PrimitiveBatch::Underlines(underlines_start..underlines_end))
760 }
761 PrimitiveKind::MonochromeSprite => {
762 let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
763 let sprites_start = self.monochrome_sprites_start;
764 let mut sprites_end = sprites_start + 1;
765 self.monochrome_sprites_iter.next();
766 while self
767 .monochrome_sprites_iter
768 .next_if(|sprite| {
769 (sprite.order, batch_kind) < max_order_and_kind
770 && sprite.tile.texture_id == texture_id
771 })
772 .is_some()
773 {
774 sprites_end += 1;
775 }
776 self.monochrome_sprites_start = sprites_end;
777 Some(PrimitiveBatch::MonochromeSprites {
778 texture_id,
779 range: sprites_start..sprites_end,
780 })
781 }
782 PrimitiveKind::SubpixelSprite => {
783 let texture_id = self.subpixel_sprites_iter.peek().unwrap().tile.texture_id;
784 let sprites_start = self.subpixel_sprites_start;
785 let mut sprites_end = sprites_start + 1;
786 self.subpixel_sprites_iter.next();
787 while self
788 .subpixel_sprites_iter
789 .next_if(|sprite| {
790 (sprite.order, batch_kind) < max_order_and_kind
791 && sprite.tile.texture_id == texture_id
792 })
793 .is_some()
794 {
795 sprites_end += 1;
796 }
797 self.subpixel_sprites_start = sprites_end;
798 Some(PrimitiveBatch::SubpixelSprites {
799 texture_id,
800 range: sprites_start..sprites_end,
801 })
802 }
803 PrimitiveKind::PolychromeSprite => {
804 let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
805 let sprites_start = self.polychrome_sprites_start;
806 let mut sprites_end = sprites_start + 1;
807 self.polychrome_sprites_iter.next();
808 while self
809 .polychrome_sprites_iter
810 .next_if(|sprite| {
811 (sprite.order, batch_kind) < max_order_and_kind
812 && sprite.tile.texture_id == texture_id
813 })
814 .is_some()
815 {
816 sprites_end += 1;
817 }
818 self.polychrome_sprites_start = sprites_end;
819 Some(PrimitiveBatch::PolychromeSprites {
820 texture_id,
821 range: sprites_start..sprites_end,
822 })
823 }
824 PrimitiveKind::Surface => {
825 let surfaces_start = self.surfaces_start;
826 let mut surfaces_end = surfaces_start + 1;
827 self.surfaces_iter.next();
828 while self
829 .surfaces_iter
830 .next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
831 .is_some()
832 {
833 surfaces_end += 1;
834 }
835 self.surfaces_start = surfaces_end;
836 Some(PrimitiveBatch::Surfaces(surfaces_start..surfaces_end))
837 }
838 }
839 }
840}
841
842#[derive(Debug)]
843#[cfg_attr(
844 all(
845 any(target_os = "linux", target_os = "freebsd"),
846 not(any(feature = "x11", feature = "wayland"))
847 ),
848 allow(dead_code)
849)]
850#[allow(missing_docs)]
851pub enum PrimitiveBatch {
852 Backdrop(usize),
854 LayerBoundary(usize),
856 Shadows(Range<usize>),
857 Quads(Range<usize>),
858 Paths(Range<usize>),
859 Underlines(Range<usize>),
860 MonochromeSprites {
861 texture_id: AtlasTextureId,
862 range: Range<usize>,
863 },
864 #[cfg_attr(target_os = "macos", allow(dead_code))]
865 SubpixelSprites {
866 texture_id: AtlasTextureId,
867 range: Range<usize>,
868 },
869 PolychromeSprites {
870 texture_id: AtlasTextureId,
871 range: Range<usize>,
872 },
873 Surfaces(Range<usize>),
874 Custom(Range<usize>),
875 Shaders {
877 program_id: u64,
878 range: Range<usize>,
879 },
880}
881
882impl PrimitiveBatch {
883 #[expect(missing_docs)]
884 pub fn label(&self) -> String {
885 match self {
886 Self::LayerBoundary(index) => format!("layer boundary ({index})"),
887 Self::Backdrop(index) => format!("backdrop ({index})"),
888 Self::Shaders { range, .. } => format!("shaders ({})", range.len()),
889 Self::Shadows(range) => format!("shadows ({})", range.len()),
890 Self::Quads(range) => format!("quads ({})", range.len()),
891 Self::Paths(range) => format!("paths ({})", range.len()),
892 Self::Underlines(range) => format!("underlines ({})", range.len()),
893 Self::MonochromeSprites { texture_id, range } => {
894 format!(
895 "monochrome sprites ({}) on atlas {}",
896 range.len(),
897 texture_id.index
898 )
899 }
900 Self::SubpixelSprites { texture_id, range } => {
901 format!(
902 "subpixel sprites ({}) on atlas {}",
903 range.len(),
904 texture_id.index
905 )
906 }
907 Self::PolychromeSprites { texture_id, range } => {
908 format!(
909 "polychrome sprites ({}) on atlas {}",
910 range.len(),
911 texture_id.index
912 )
913 }
914 Self::Custom(range) => format!("custom ({})", range.len()),
915 Self::Surfaces(range) => format!("surfaces ({})", range.len()),
916 }
917 }
918}
919
920#[derive(Default, Debug, Copy, Clone)]
921#[repr(C)]
922#[expect(missing_docs)]
923pub struct Quad {
924 pub order: DrawOrder,
925 pub border_style: BorderStyle,
926 pub bounds: Bounds<ScaledPixels>,
927 pub content_mask: ContentMask<ScaledPixels>,
928 pub background: Background,
929 pub border_color: Hsla,
930 pub corner_radii: Corners<ScaledPixels>,
931 pub border_widths: Edges<ScaledPixels>,
932}
933
934impl From<Quad> for Primitive {
935 fn from(quad: Quad) -> Self {
936 Primitive::Quad(quad)
937 }
938}
939
940#[derive(Debug, Copy, Clone)]
941#[repr(C)]
942#[expect(missing_docs)]
943pub struct Underline {
944 pub order: DrawOrder,
945 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
947 pub content_mask: ContentMask<ScaledPixels>,
948 pub color: Hsla,
949 pub thickness: ScaledPixels,
950 pub wavy: PaddedBool32,
951}
952
953impl From<Underline> for Primitive {
954 fn from(underline: Underline) -> Self {
955 Primitive::Underline(underline)
956 }
957}
958
959#[derive(Debug, Copy, Clone)]
960#[repr(C)]
961#[expect(missing_docs)]
962pub struct Shadow {
963 pub order: DrawOrder,
964 pub blur_radius: ScaledPixels,
965 pub bounds: Bounds<ScaledPixels>,
966 pub corner_radii: Corners<ScaledPixels>,
967 pub content_mask: ContentMask<ScaledPixels>,
968 pub color: Hsla,
969 pub element_bounds: Bounds<ScaledPixels>,
970 pub element_corner_radii: Corners<ScaledPixels>,
971 pub inset: u32,
973 pub pad: u32, }
975
976impl From<Shadow> for Primitive {
977 fn from(shadow: Shadow) -> Self {
978 Primitive::Shadow(shadow)
979 }
980}
981
982#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
984#[repr(C)]
985pub enum BorderStyle {
986 #[default]
988 Solid = 0,
989 Dashed = 1,
991}
992
993#[derive(Debug, Clone, Copy, PartialEq)]
995#[repr(C)]
996pub struct TransformationMatrix {
997 pub rotation_scale: [[f32; 2]; 2],
1000 pub translation: [f32; 2],
1002}
1003
1004impl Eq for TransformationMatrix {}
1005
1006impl TransformationMatrix {
1007 pub fn unit() -> Self {
1009 Self {
1010 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
1011 translation: [0.0, 0.0],
1012 }
1013 }
1014
1015 pub fn translate(mut self, point: Point<ScaledPixels>) -> Self {
1017 self.compose(Self {
1018 rotation_scale: [[1.0, 0.0], [0.0, 1.0]],
1019 translation: [point.x.0, point.y.0],
1020 })
1021 }
1022
1023 pub fn rotate(self, angle: Radians) -> Self {
1025 self.compose(Self {
1026 rotation_scale: [
1027 [angle.0.cos(), -angle.0.sin()],
1028 [angle.0.sin(), angle.0.cos()],
1029 ],
1030 translation: [0.0, 0.0],
1031 })
1032 }
1033
1034 pub fn scale(self, size: Size<f32>) -> Self {
1036 self.compose(Self {
1037 rotation_scale: [[size.width, 0.0], [0.0, size.height]],
1038 translation: [0.0, 0.0],
1039 })
1040 }
1041
1042 #[inline]
1046 pub fn compose(self, other: TransformationMatrix) -> TransformationMatrix {
1047 if other == Self::unit() {
1048 return self;
1049 }
1050 TransformationMatrix {
1052 rotation_scale: [
1053 [
1054 self.rotation_scale[0][0] * other.rotation_scale[0][0]
1055 + self.rotation_scale[0][1] * other.rotation_scale[1][0],
1056 self.rotation_scale[0][0] * other.rotation_scale[0][1]
1057 + self.rotation_scale[0][1] * other.rotation_scale[1][1],
1058 ],
1059 [
1060 self.rotation_scale[1][0] * other.rotation_scale[0][0]
1061 + self.rotation_scale[1][1] * other.rotation_scale[1][0],
1062 self.rotation_scale[1][0] * other.rotation_scale[0][1]
1063 + self.rotation_scale[1][1] * other.rotation_scale[1][1],
1064 ],
1065 ],
1066 translation: [
1067 self.translation[0]
1068 + self.rotation_scale[0][0] * other.translation[0]
1069 + self.rotation_scale[0][1] * other.translation[1],
1070 self.translation[1]
1071 + self.rotation_scale[1][0] * other.translation[0]
1072 + self.rotation_scale[1][1] * other.translation[1],
1073 ],
1074 }
1075 }
1076
1077 pub fn apply(&self, point: Point<Pixels>) -> Point<Pixels> {
1079 let input = [point.x.0, point.y.0];
1080 let mut output = self.translation;
1081 for (i, output_cell) in output.iter_mut().enumerate() {
1082 for (k, input_cell) in input.iter().enumerate() {
1083 *output_cell += self.rotation_scale[i][k] * *input_cell;
1084 }
1085 }
1086 Point::new(output[0].into(), output[1].into())
1087 }
1088}
1089
1090impl Default for TransformationMatrix {
1091 fn default() -> Self {
1092 Self::unit()
1093 }
1094}
1095
1096#[derive(Copy, Clone, Debug)]
1097#[repr(C)]
1098#[expect(missing_docs)]
1099pub struct MonochromeSprite {
1100 pub order: DrawOrder,
1101 pub pad: u32,
1102 pub bounds: Bounds<ScaledPixels>,
1103 pub content_mask: ContentMask<ScaledPixels>,
1104 pub color: Hsla,
1105 pub tile: AtlasTile,
1106 pub transformation: TransformationMatrix,
1107}
1108
1109impl From<MonochromeSprite> for Primitive {
1110 fn from(sprite: MonochromeSprite) -> Self {
1111 Primitive::MonochromeSprite(sprite)
1112 }
1113}
1114
1115#[derive(Copy, Clone, Debug)]
1116#[repr(C)]
1117#[expect(missing_docs)]
1118pub struct SubpixelSprite {
1119 pub order: DrawOrder,
1120 pub pad: u32, pub bounds: Bounds<ScaledPixels>,
1122 pub content_mask: ContentMask<ScaledPixels>,
1123 pub color: Hsla,
1124 pub tile: AtlasTile,
1125 pub transformation: TransformationMatrix,
1126}
1127
1128impl From<SubpixelSprite> for Primitive {
1129 fn from(sprite: SubpixelSprite) -> Self {
1130 Primitive::SubpixelSprite(sprite)
1131 }
1132}
1133
1134#[derive(Copy, Clone, Debug)]
1135#[repr(C)]
1136#[expect(missing_docs)]
1137pub struct PolychromeSprite {
1138 pub order: DrawOrder,
1139 pub pad: u32,
1140 pub grayscale: PaddedBool32,
1141 pub opacity: f32,
1142 pub bounds: Bounds<ScaledPixels>,
1143 pub content_mask: ContentMask<ScaledPixels>,
1144 pub corner_radii: Corners<ScaledPixels>,
1145 pub tile: AtlasTile,
1146}
1147
1148impl From<PolychromeSprite> for Primitive {
1149 fn from(sprite: PolychromeSprite) -> Self {
1150 Primitive::PolychromeSprite(sprite)
1151 }
1152}
1153
1154#[derive(Clone, Debug)]
1155#[allow(missing_docs)]
1156pub struct PaintSurface {
1157 pub order: DrawOrder,
1158 pub bounds: Bounds<ScaledPixels>,
1159 pub content_mask: ContentMask<ScaledPixels>,
1160 #[cfg(any(target_os = "macos", target_os = "ios"))]
1161 pub image_buffer: objc2_core_foundation::CFRetained<objc2_core_video::CVPixelBuffer>,
1162}
1163
1164impl From<PaintSurface> for Primitive {
1165 fn from(surface: PaintSurface) -> Self {
1166 Primitive::Surface(surface)
1167 }
1168}
1169
1170#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1171#[expect(missing_docs)]
1172pub struct PathId(pub usize);
1173
1174#[derive(Clone, Debug)]
1176#[expect(missing_docs)]
1177pub struct Path<P: Clone + Debug + Default + PartialEq> {
1178 pub id: PathId,
1179 pub order: DrawOrder,
1180 pub bounds: Bounds<P>,
1181 pub content_mask: ContentMask<P>,
1182 pub vertices: Vec<PathVertex<P>>,
1183 pub color: Background,
1184 start: Point<P>,
1185 current: Point<P>,
1186 contour_count: usize,
1187}
1188
1189impl Path<Pixels> {
1190 pub fn new(start: Point<Pixels>) -> Self {
1192 Self {
1193 id: PathId(0),
1194 order: DrawOrder::default(),
1195 vertices: Vec::new(),
1196 start,
1197 current: start,
1198 bounds: Bounds {
1199 origin: start,
1200 size: Default::default(),
1201 },
1202 content_mask: Default::default(),
1203 color: Default::default(),
1204 contour_count: 0,
1205 }
1206 }
1207
1208 pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
1210 Path {
1211 id: self.id,
1212 order: self.order,
1213 bounds: self.bounds.scale(factor),
1214 content_mask: self.content_mask.scale(factor),
1215 vertices: self
1216 .vertices
1217 .iter()
1218 .map(|vertex| vertex.scale(factor))
1219 .collect(),
1220 start: self.start.map(|start| start.scale(factor)),
1221 current: self.current.scale(factor),
1222 contour_count: self.contour_count,
1223 color: self.color,
1224 }
1225 }
1226
1227 pub fn move_to(&mut self, to: Point<Pixels>) {
1229 self.contour_count += 1;
1230 self.start = to;
1231 self.current = to;
1232 }
1233
1234 pub fn line_to(&mut self, to: Point<Pixels>) {
1236 self.contour_count += 1;
1237 if self.contour_count > 1 {
1238 self.push_triangle(
1239 (self.start, self.current, to),
1240 (point(0., 1.), point(0., 1.), point(0., 1.)),
1241 );
1242 }
1243 self.current = to;
1244 }
1245
1246 pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
1248 self.contour_count += 1;
1249 if self.contour_count > 1 {
1250 self.push_triangle(
1251 (self.start, self.current, to),
1252 (point(0., 1.), point(0., 1.), point(0., 1.)),
1253 );
1254 }
1255
1256 self.push_triangle(
1257 (self.current, ctrl, to),
1258 (point(0., 0.), point(0.5, 0.), point(1., 1.)),
1259 );
1260 self.current = to;
1261 }
1262
1263 pub fn push_triangle(
1265 &mut self,
1266 xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
1267 st: (Point<f32>, Point<f32>, Point<f32>),
1268 ) {
1269 self.bounds = self
1270 .bounds
1271 .union(&Bounds {
1272 origin: xy.0,
1273 size: Default::default(),
1274 })
1275 .union(&Bounds {
1276 origin: xy.1,
1277 size: Default::default(),
1278 })
1279 .union(&Bounds {
1280 origin: xy.2,
1281 size: Default::default(),
1282 });
1283
1284 self.vertices.push(PathVertex {
1285 xy_position: xy.0,
1286 st_position: st.0,
1287 content_mask: Default::default(),
1288 });
1289 self.vertices.push(PathVertex {
1290 xy_position: xy.1,
1291 st_position: st.1,
1292 content_mask: Default::default(),
1293 });
1294 self.vertices.push(PathVertex {
1295 xy_position: xy.2,
1296 st_position: st.2,
1297 content_mask: Default::default(),
1298 });
1299 }
1300}
1301
1302impl<T> Path<T>
1303where
1304 T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
1305{
1306 #[allow(unused)]
1307 #[expect(missing_docs)]
1308 pub fn clipped_bounds(&self) -> Bounds<T> {
1309 self.bounds.intersect(&self.content_mask.bounds)
1310 }
1311}
1312
1313impl From<Path<ScaledPixels>> for Primitive {
1314 fn from(path: Path<ScaledPixels>) -> Self {
1315 Primitive::Path(path)
1316 }
1317}
1318
1319#[derive(Clone, Debug)]
1320#[repr(C)]
1321#[expect(missing_docs)]
1322pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
1323 pub xy_position: Point<P>,
1324 pub st_position: Point<f32>,
1325 pub content_mask: ContentMask<P>,
1326}
1327
1328#[expect(missing_docs)]
1329impl PathVertex<Pixels> {
1330 pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
1331 PathVertex {
1332 xy_position: self.xy_position.scale(factor),
1333 st_position: self.st_position,
1334 content_mask: self.content_mask.scale(factor),
1335 }
1336 }
1337}
1338
1339#[derive(Clone, Copy, Debug, PartialEq)]
1345pub struct PreparationRequest {
1346 logical_size: Size<Pixels>,
1347 raster_size: Size<crate::DevicePixels>,
1348 scale_factor: f32,
1349}
1350
1351impl PreparationRequest {
1352 pub fn new(
1354 logical_size: Size<Pixels>,
1355 raster_size: Size<crate::DevicePixels>,
1356 scale_factor: f32,
1357 ) -> anyhow::Result<Self> {
1358 anyhow::ensure!(
1359 logical_size.width.0.is_finite()
1360 && logical_size.width.0 > 0.
1361 && logical_size.height.0.is_finite()
1362 && logical_size.height.0 > 0.
1363 && raster_size.width.0 > 0
1364 && raster_size.height.0 > 0
1365 && scale_factor.is_finite()
1366 && scale_factor > 0.,
1367 "invalid custom preparation geometry"
1368 );
1369 Ok(Self {
1370 logical_size,
1371 raster_size,
1372 scale_factor,
1373 })
1374 }
1375
1376 pub fn logical_size(&self) -> Size<Pixels> {
1378 self.logical_size
1379 }
1380
1381 pub fn raster_size(&self) -> Size<crate::DevicePixels> {
1383 self.raster_size
1384 }
1385
1386 pub fn scale_factor(&self) -> f32 {
1388 self.scale_factor
1389 }
1390}
1391
1392pub trait CustomContent: std::fmt::Debug + Send + Sync + 'static {
1396 fn image_color_transform(&self) -> [[f32; 4]; 3] {
1399 [[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.]]
1400 }
1401
1402 fn image_source_size(&self) -> Option<Size<crate::DevicePixels>> {
1406 None
1407 }
1408 fn image_sample_opacity(&self) -> f32 {
1411 1.
1412 }
1413 fn image_color_mode(&self) -> crate::ImageColorMode {
1415 crate::ImageColorMode::EncodedSrgb
1416 }
1417 fn image_linear_adjustments(&self) -> Option<crate::LinearImagePaint> {
1420 None
1421 }
1422
1423 fn prepare(
1425 &self,
1426 backend: &dyn std::any::Any,
1427 size: Size<crate::DevicePixels>,
1428 ) -> anyhow::Result<Box<dyn std::any::Any>>;
1429
1430 fn prepare_in_frame(
1433 &self,
1434 backend: &dyn std::any::Any,
1435 request: &PreparationRequest,
1436 _frame: &crate::PreparationFrame,
1437 ) -> anyhow::Result<Box<dyn std::any::Any>> {
1438 self.prepare_with_request(backend, request)
1439 }
1440
1441 fn prepare_with_request(
1445 &self,
1446 backend: &dyn std::any::Any,
1447 request: &PreparationRequest,
1448 ) -> anyhow::Result<Box<dyn std::any::Any>> {
1449 self.prepare(backend, request.raster_size())
1450 }
1451}
1452
1453#[derive(Debug)]
1456struct ContentWithPreparationGeometry {
1457 content: std::sync::Arc<dyn CustomContent>,
1458 logical_size: Size<Pixels>,
1459 scale_factor: f32,
1460}
1461
1462impl CustomContent for ContentWithPreparationGeometry {
1463 fn image_source_size(&self) -> Option<Size<crate::DevicePixels>> {
1464 self.content.image_source_size()
1465 }
1466 fn image_sample_opacity(&self) -> f32 {
1467 self.content.image_sample_opacity()
1468 }
1469 fn image_color_mode(&self) -> crate::ImageColorMode {
1470 self.content.image_color_mode()
1471 }
1472 fn image_linear_adjustments(&self) -> Option<crate::LinearImagePaint> {
1473 self.content.image_linear_adjustments()
1474 }
1475
1476 fn image_color_transform(&self) -> [[f32; 4]; 3] {
1477 self.content.image_color_transform()
1478 }
1479
1480 fn prepare(
1481 &self,
1482 backend: &dyn std::any::Any,
1483 size: Size<crate::DevicePixels>,
1484 ) -> anyhow::Result<Box<dyn std::any::Any>> {
1485 let request = PreparationRequest::new(self.logical_size, size, self.scale_factor)?;
1486 self.content.prepare_with_request(backend, &request)
1487 }
1488
1489 fn prepare_in_frame(
1490 &self,
1491 backend: &dyn std::any::Any,
1492 request: &PreparationRequest,
1493 frame: &crate::PreparationFrame,
1494 ) -> anyhow::Result<Box<dyn std::any::Any>> {
1495 let request =
1496 PreparationRequest::new(self.logical_size, request.raster_size(), self.scale_factor)?;
1497 self.content.prepare_in_frame(backend, &request, frame)
1498 }
1499
1500 fn prepare_with_request(
1501 &self,
1502 backend: &dyn std::any::Any,
1503 request: &PreparationRequest,
1504 ) -> anyhow::Result<Box<dyn std::any::Any>> {
1505 self.prepare(backend, request.raster_size())
1506 }
1507}
1508
1509pub(crate) fn content_with_preparation_geometry(
1510 content: std::sync::Arc<dyn CustomContent>,
1511 logical_size: Size<Pixels>,
1512 scale_factor: f32,
1513) -> std::sync::Arc<dyn CustomContent> {
1514 std::sync::Arc::new(ContentWithPreparationGeometry {
1515 content,
1516 logical_size,
1517 scale_factor,
1518 })
1519}
1520
1521#[cfg(test)]
1522mod preparation_tests;
1523
1524#[derive(Clone)]
1527pub enum CustomGeometry {
1528 RoundedRect {
1530 bounds: Bounds<Pixels>,
1532 corner_radii: Corners<Pixels>,
1534 },
1535 Border(crate::PaintQuad),
1537 Path(Path<Pixels>),
1539}
1540
1541#[derive(Clone, Debug)]
1543pub enum CustomCoverage {
1544 Border {
1546 widths: Edges<ScaledPixels>,
1548 style: BorderStyle,
1550 },
1551 Path(std::sync::Arc<Path<ScaledPixels>>),
1553}
1554
1555#[derive(Clone, Debug)]
1557pub struct CustomDraw {
1558 pub order: DrawOrder,
1560 pub bounds: Bounds<ScaledPixels>,
1562 pub content_mask: ContentMask<ScaledPixels>,
1564 pub corner_radii: Corners<ScaledPixels>,
1566 pub opacity: f32,
1568 pub content: std::sync::Arc<dyn CustomContent>,
1572 pub coverage: Option<CustomCoverage>,
1574}
1575
1576impl From<CustomDraw> for Primitive {
1577 fn from(draw: CustomDraw) -> Self {
1578 Self::Custom(draw)
1579 }
1580}
1581
1582pub const SHADER_DATA_SLOTS: usize = 8;
1584
1585#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1588#[non_exhaustive]
1589pub enum ShaderOutputEncoding {
1590 EncodedSrgb,
1592 LinearDisplayP3,
1595 LinearRec2020,
1598}
1599
1600#[derive(Debug)]
1603pub struct SceneShaderProgram {
1604 id: u64,
1605 output_encoding: ShaderOutputEncoding,
1606 glyph_mask: bool,
1607 path_mask: bool,
1608 wgsl: String,
1610 metal: (String, String, String),
1612 hlsl: (String, String, String),
1614}
1615impl SceneShaderProgram {
1616 pub fn new(
1618 wgsl: String,
1619 metal: (String, String, String),
1620 hlsl: (String, String, String),
1621 ) -> Self {
1622 Self::new_with_output_encoding(wgsl, metal, hlsl, ShaderOutputEncoding::EncodedSrgb)
1623 }
1624 pub fn new_with_output_encoding(
1627 wgsl: String,
1628 metal: (String, String, String),
1629 hlsl: (String, String, String),
1630 output_encoding: ShaderOutputEncoding,
1631 ) -> Self {
1632 static NEXT_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
1633 Self {
1634 id: NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
1635 output_encoding,
1636 glyph_mask: false,
1637 path_mask: false,
1638 wgsl,
1639 metal,
1640 hlsl,
1641 }
1642 }
1643 pub fn with_glyph_mask(mut self) -> Self {
1646 self.glyph_mask = true;
1647 self.path_mask = false;
1648 self
1649 }
1650 pub fn with_path_mask(mut self) -> Self {
1652 self.path_mask = true;
1653 self.glyph_mask = false;
1654 self
1655 }
1656 pub fn uses_path_mask(&self) -> bool {
1658 self.path_mask
1659 }
1660 pub fn uses_glyph_mask(&self) -> bool {
1662 self.glyph_mask
1663 }
1664 pub fn output_encoding(&self) -> ShaderOutputEncoding {
1666 self.output_encoding
1667 }
1668 pub fn wgsl(&self) -> &str {
1670 &self.wgsl
1671 }
1672 pub fn metal(&self) -> &(String, String, String) {
1674 &self.metal
1675 }
1676 pub fn hlsl(&self) -> &(String, String, String) {
1678 &self.hlsl
1679 }
1680 pub fn id(&self) -> u64 {
1682 self.id
1683 }
1684}
1685
1686#[derive(Clone, Debug)]
1688pub struct ShaderDraw {
1689 pub order: DrawOrder,
1691 pub bounds: Bounds<ScaledPixels>,
1693 pub content_mask: ContentMask<ScaledPixels>,
1695 pub opacity: f32,
1697 pub scale_factor: f32,
1699 pub program: std::sync::Arc<SceneShaderProgram>,
1701 pub glyph_mask: Option<AtlasTile>,
1703 pub path_mask: Option<std::sync::Arc<Path<ScaledPixels>>>,
1706 pub data: [[f32; 4]; SHADER_DATA_SLOTS],
1708}
1709impl ShaderDraw {
1710 pub fn validate_masks(&self) -> anyhow::Result<()> {
1712 anyhow::ensure!(
1713 self.program.uses_glyph_mask() == self.glyph_mask.is_some(),
1714 "shader glyph mask/program mismatch"
1715 );
1716 anyhow::ensure!(
1717 self.program.uses_path_mask() == self.path_mask.is_some(),
1718 "shader path mask/program mismatch"
1719 );
1720 anyhow::ensure!(
1721 self.glyph_mask.is_none() || self.path_mask.is_none(),
1722 "shader cannot combine glyph and path masks"
1723 );
1724 if let Some(path) = &self.path_mask {
1725 anyhow::ensure!(
1726 !path.vertices.is_empty()
1727 && path.vertices.len() <= 65_536
1728 && path.vertices.len() % 3 == 0,
1729 "path brush requires 1..65536 triangle vertices"
1730 );
1731 anyhow::ensure!(
1732 path.color.as_solid().is_some_and(|c| c.a == 1.),
1733 "path brush coverage must be opaque"
1734 );
1735 }
1736 Ok(())
1737 }
1738 pub fn instance_data(&self) -> [[f32; 4]; 3 + SHADER_DATA_SLOTS] {
1740 let mut result = [[0.; 4]; 3 + SHADER_DATA_SLOTS];
1741 result[0] = [
1742 self.bounds.origin.x.0,
1743 self.bounds.origin.y.0,
1744 self.bounds.size.width.0,
1745 self.bounds.size.height.0,
1746 ];
1747 let clip = self.content_mask.bounds;
1748 result[1] = [
1749 clip.origin.x.0,
1750 clip.origin.y.0,
1751 clip.origin.x.0 + clip.size.width.0,
1752 clip.origin.y.0 + clip.size.height.0,
1753 ];
1754 result[2] = [self.opacity, self.scale_factor, 0., 0.];
1755 result[3..].copy_from_slice(&self.data);
1756 result
1757 }
1758}
1759
1760pub trait GlyphBrush: std::fmt::Debug + Send + Sync + 'static {
1764 fn cache_key(&self) -> std::sync::Arc<()>;
1767 fn paint_glyph(
1769 &self,
1770 window: &mut crate::Window,
1771 glyph: MonochromeSprite,
1772 brush_bounds: Bounds<Pixels>,
1773 opacity: f32,
1774 ) -> anyhow::Result<()>;
1775}