1use std::{
2 cell::OnceCell,
3 hash::{Hash, Hasher},
4 ops::Range,
5 sync::{Arc, OnceLock},
6};
7
8use bytemuck::{Pod, Zeroable};
9
10use crate::{
11 ArcGeometry, BlendMode, Brush, Color, CornerRadii, DrawPrimitive, FxHasher, LineGeometry,
12 Point, Rect, RenderHash, ShapeRecordBody, ShapeRecordCurve, ShapeRecords, Stroke, StrokeCap,
13 StrokeJoin, TAU, TileMode, Trapezoid, arc_band,
14 float::{at_least, within},
15 vertex_gradient,
16};
17
18pub const RECORD_KIND_RECT: u32 = 0;
20pub const RECORD_KIND_ROUND_RECT: u32 = 1;
22pub const RECORD_KIND_ARC: u32 = 2;
24pub const RECORD_KIND_LINE: u32 = 3;
27pub const TRAPEZOID_CAP: u32 = 3;
30
31const KIND_SHIFT: u32 = 0;
32const STROKED_BIT: u32 = 1 << 2;
33const CAP_SHIFT: u32 = 3;
34const JOIN_SHIFT: u32 = 5;
35const BLEND_SHIFT: u32 = 8;
36const BAND_CAP_SHIFT: u32 = 16;
37const ARC_DEGENERATE_BIT: u32 = 1 << 18;
38const ARC_RECT_LOOSE_BIT: u32 = 1 << 19;
39const ARC_BANDED_BIT: u32 = 1 << 20;
40const BAND_CLASS_SHIFT: u32 = 21;
41const BAND_CLASS_MASK: u32 = 0b111;
42const VERTEX_GRADIENT_BIT: u32 = 1 << 24;
43const OPEN_LEFT_BIT: u32 = 1 << JOIN_SHIFT;
44const OPEN_RIGHT_BIT: u32 = 2 << JOIN_SHIFT;
45const RECT_FILL_MASK: u32 = STROKED_BIT | (RECORD_KIND_ARC << KIND_SHIFT);
46const NO_SEGMENT_KEY: u32 = u32::MAX;
47
48pub const ARC_BUCKETS: usize = 7;
51pub const ARC_BUCKET_SEGMENTS: [u32; ARC_BUCKETS] = [1, 2, 4, 8, 16, 32, 64];
53pub const ARC_RING_SEGMENTS: [u32; 4] = [8, 16, 32, 64];
59pub const ARC_RING_RADII: [f32; 3] = [24.0, 96.0, 384.0];
62const ARC_RING_SEGMENTS_PER_RADIAN: [f32; ARC_RING_SEGMENTS.len()] = ring_segments_per_radian();
63
64const fn ring_segments_per_radian() -> [f32; ARC_RING_SEGMENTS.len()] {
65 let mut out = [0.0; ARC_RING_SEGMENTS.len()];
66 let mut index = 0;
67 while index < ARC_RING_SEGMENTS.len() {
68 out[index] = ARC_RING_SEGMENTS[index] as f32 / TAU;
69 index += 1;
70 }
71 out
72}
73pub const ARC_BAND_MIN_RADIUS: f32 = 11.0;
76pub const ARC_BAND_MIN_INNER_RADIUS: f32 = 1.0;
79pub const BAND_MARGIN: f32 = 0.5 + 1.0 / 16.0;
84pub const BAND_MAX_STEP: f32 = std::f32::consts::FRAC_PI_2;
90pub const BAND_ANGULAR_PAD: f32 = 0.001;
95pub const QUAD_VERTICES: u32 = 4;
98pub const QUAD_INDICES: u32 = 6;
100pub const BAND_MIN_SEGMENTS: u32 = 1;
103
104pub const fn strip_vertices(segments: u32) -> u32 {
107 segments * 2 + 2
108}
109
110pub const fn strip_indices(segments: u32) -> u32 {
112 segments * QUAD_INDICES
113}
114
115pub fn strip_index_pattern(segments: u32) -> impl Iterator<Item = u32> {
122 (0..segments).flat_map(|quad| {
123 let base = quad * 2;
124 [base, base + 1, base + 2, base + 2, base + 1, base + 3]
125 })
126}
127pub const BAND_VERTEX_PIXELS: f32 = 32.0;
134const BAND_STRIP_OVERSHOOT: f32 = 1.25;
137const SEGMENT_WASTE_QUADS: u32 = 512;
142
143struct BandRing {
148 mid: f32,
149 ring_half: f32,
150 range: f32,
151 segments_per_radian: f32,
152}
153
154impl BandRing {
155 fn of_geometry(geometry: &ArcGeometry) -> Self {
156 Self::new(
157 geometry.inner_radius,
158 geometry.outer_radius,
159 geometry.start_angle,
160 geometry.sweep_angle,
161 )
162 }
163
164 fn new(inner: f32, outer: f32, start: f32, sweep: f32) -> Self {
165 let mid = (outer + inner) * 0.5;
166 let ring_half = at_least((outer - inner) * 0.5, 0.0) + BAND_MARGIN;
167 let (_, range) = band_padded_range(mid, ring_half, start, sweep);
168 Self {
169 mid,
170 ring_half,
171 range,
172 segments_per_radian: Self::segments_per_radian(outer),
173 }
174 }
175
176 #[inline]
179 fn segments_per_radian(outer_radius: f32) -> f32 {
180 let bucket = usize::from(outer_radius > ARC_RING_RADII[0])
181 + usize::from(outer_radius > ARC_RING_RADII[1])
182 + usize::from(outer_radius > ARC_RING_RADII[2]);
183 ARC_RING_SEGMENTS_PER_RADIAN[bucket]
184 }
185
186 #[inline]
189 fn segments(&self) -> u32 {
190 let exact = self.range * self.segments_per_radian;
191 if exact <= BAND_MIN_SEGMENTS as f32 {
192 return BAND_MIN_SEGMENTS;
193 }
194 let floor = exact as u32;
195 let needed = if (floor as f32) < exact {
196 floor + 1
197 } else {
198 floor
199 };
200 needed
201 .max(BAND_MIN_SEGMENTS)
202 .next_power_of_two()
203 .min(ARC_BUCKET_SEGMENTS[ARC_BUCKETS - 1])
204 }
205
206 fn strip_pixels(&self, segments: u32) -> f32 {
209 self.range * self.mid * (self.ring_half + self.ring_half) * BAND_STRIP_OVERSHOOT
210 + vertex_pixels(strip_vertices(segments))
211 }
212}
213
214fn vertex_pixels(vertices: u32) -> f32 {
215 vertices as f32 * BAND_VERTEX_PIXELS
216}
217
218pub fn band_padded_range(mid: f32, ring_half: f32, start: f32, sweep: f32) -> (f32, f32) {
225 let inner_padded = mid - ring_half;
226 if inner_padded <= 0.0 {
227 return (0.0, TAU);
228 }
229 let pad = ring_half / inner_padded + BAND_ANGULAR_PAD;
230 let padded = sweep + pad + pad;
231 if padded < TAU {
232 (start - pad, padded)
233 } else {
234 (0.0, TAU)
235 }
236}
237
238pub fn band_bucket(segments: u32) -> usize {
240 segments.trailing_zeros() as usize
241}
242
243pub fn band_class_segments(class: u8) -> u32 {
245 ARC_BUCKET_SEGMENTS[class as usize]
246}
247
248#[inline]
253fn band_bucket_for(
254 geometry: &ArcGeometry,
255 degenerate: bool,
256 ring: &BandRing,
257 rect: Rect,
258) -> Option<usize> {
259 if degenerate
260 || geometry.outer_radius < ARC_BAND_MIN_RADIUS
261 || geometry.inner_radius <= ARC_BAND_MIN_INNER_RADIUS
262 {
263 return None;
264 }
265 let segments = ring.segments();
266 (ring.strip_pixels(segments) < rect.width * rect.height + vertex_pixels(QUAD_VERTICES))
267 .then(|| band_bucket(segments))
268}
269
270pub fn band_pays(geometry: &ArcGeometry, rect: Rect) -> bool {
273 band_bucket_for(
274 geometry,
275 geometry.is_degenerate(),
276 &BandRing::of_geometry(geometry),
277 rect,
278 )
279 .is_some()
280}
281
282pub const FRAGMENT_KIND_FILL: u32 = 0;
285pub const FRAGMENT_KIND_STROKE: u32 = 1;
286pub const FRAGMENT_KIND_ARC: u32 = 2;
287pub const FRAGMENT_KIND_LINE: u32 = 3;
288pub const FRAGMENT_KIND_TRAPEZOID: u32 = 4;
289
290const TWO_BITS: u32 = 0b11;
291const BLEND_MASK: u32 = 0xff;
292
293pub const BRUSH_KIND_LINEAR: u32 = 1;
295pub const BRUSH_KIND_RADIAL: u32 = 2;
297pub const BRUSH_KIND_SWEEP: u32 = 3;
299
300#[repr(C)]
305#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
306pub struct ShapeRecord {
307 pub rect: [f32; 4],
311 pub radii: [f32; 4],
315 pub color: [f32; 4],
317 pub stroke_width: f32,
319 pub flags: u32,
322 pub brush: u32,
325 pub reserved: u32,
327 pub arc: [f32; 4],
329 pub arc_band: [f32; 4],
332 pub arc_normalized: [f32; 4],
335}
336
337impl ShapeRecord {
338 pub fn kind(&self) -> u32 {
339 (self.flags >> KIND_SHIFT) & TWO_BITS
340 }
341
342 pub fn is_stroked(&self) -> bool {
343 self.flags & STROKED_BIT != 0
344 }
345
346 pub fn fragment_kind(&self) -> u32 {
348 fragment_kind(self.flags)
349 }
350
351 pub fn stroke(&self) -> Option<Stroke> {
352 self.is_stroked().then(|| Stroke {
353 width: self.stroke_width,
354 cap: STROKE_CAPS[((self.flags >> CAP_SHIFT) & TWO_BITS) as usize],
355 join: STROKE_JOINS[((self.flags >> JOIN_SHIFT) & TWO_BITS) as usize],
356 })
357 }
358
359 pub fn blend_mode(&self) -> BlendMode {
360 BlendMode::ALL[((self.flags >> BLEND_SHIFT) & BLEND_MASK) as usize]
361 }
362
363 pub fn band_cap(&self) -> StrokeCap {
365 STROKE_CAPS[((self.flags >> BAND_CAP_SHIFT) & TWO_BITS) as usize]
366 }
367
368 pub fn is_degenerate_arc(&self) -> bool {
372 self.flags & ARC_DEGENERATE_BIT != 0
373 }
374
375 pub fn is_gradient(&self) -> bool {
376 self.brush != 0
377 }
378
379 pub fn is_vertex_gradient(&self) -> bool {
386 self.flags & VERTEX_GRADIENT_BIT != 0
387 }
388
389 pub fn is_banded(&self) -> bool {
392 self.flags & ARC_BANDED_BIT != 0
393 }
394
395 pub fn band_class(&self) -> usize {
399 ((self.flags >> BAND_CLASS_SHIFT) & BAND_CLASS_MASK) as usize
400 }
401
402 pub fn band_segments(&self) -> u32 {
404 ARC_BUCKET_SEGMENTS[self.band_class()]
405 }
406
407 pub fn has_loose_rect(&self) -> bool {
411 self.flags & ARC_RECT_LOOSE_BIT != 0
412 }
413
414 pub fn stored_rect(&self) -> Rect {
416 row_rect(self.rect)
417 }
418
419 pub fn rect_value(&self) -> Rect {
422 match self.arc_geometry() {
423 Some(geometry) if self.has_loose_rect() => geometry.bounds(),
424 _ => self.stored_rect(),
425 }
426 }
427
428 pub fn coverage_rect(&self) -> Rect {
432 match self.line_geometry() {
433 Some(line) => line.bounds(),
434 None => expand_rect(self.rect_value(), self.half_stroke()),
435 }
436 }
437
438 fn half_stroke(&self) -> f32 {
439 if self.is_stroked() {
440 self.stroke_width * 0.5
441 } else {
442 0.0
443 }
444 }
445
446 pub fn line_geometry(&self) -> Option<LineGeometry> {
448 (self.kind() == RECORD_KIND_LINE && !is_trapezoid(self.flags)).then(|| LineGeometry {
449 start: Point::new(self.arc[0], self.arc[1]),
450 end: Point::new(self.arc_band[2], self.arc_band[3]),
451 half_width: self.stroke_width * 0.5,
452 cap: STROKE_CAPS[((self.flags >> CAP_SHIFT) & TWO_BITS) as usize],
453 })
454 }
455
456 pub fn trapezoid(&self) -> Option<Trapezoid> {
460 is_trapezoid(self.flags).then(|| Trapezoid {
461 left: self.arc_normalized[0],
462 right: self.arc_normalized[1],
463 top: [self.arc[0], self.arc[1]],
464 bottom: [self.arc_band[2], self.arc_band[3]],
465 open_left: self.flags & OPEN_LEFT_BIT != 0,
466 open_right: self.flags & OPEN_RIGHT_BIT != 0,
467 })
468 }
469
470 pub fn arc_geometry(&self) -> Option<ArcGeometry> {
472 (self.kind() == RECORD_KIND_ARC).then(|| ArcGeometry {
473 center: Point::new(self.arc[0], self.arc[1]),
474 inner_radius: self.arc_band[2],
475 outer_radius: self.arc_band[3],
476 start_angle: self.arc_normalized[0],
477 sweep_angle: self.arc_normalized[1],
478 cap: self.band_cap(),
479 })
480 }
481}
482
483fn band_interior_area(width: f32, height: f32, radii: [f32; 4]) -> f32 {
489 let [top_left, top_right, bottom_right, bottom_left] =
490 radii.map(|radius| at_least(radius, 0.0));
491 let across = inset_area(
492 width,
493 height,
494 top_left.max(bottom_left) + top_right.max(bottom_right),
495 0.0,
496 );
497 let down = inset_area(
498 width,
499 height,
500 0.0,
501 top_left.max(top_right) + bottom_left.max(bottom_right),
502 );
503 across.max(down)
504}
505
506fn fill_interior_area(width: f32, height: f32, radii: [f32; 4]) -> f32 {
511 let [top_left, top_right, bottom_right, bottom_left] =
512 radii.map(|radius| at_least(radius, 0.0) * (1.0 - std::f32::consts::FRAC_1_SQRT_2));
513 band_interior_area(width, height, radii).max(inset_area(
514 width,
515 height,
516 top_left.max(bottom_left) + top_right.max(bottom_right),
517 top_left.max(top_right) + bottom_left.max(bottom_right),
518 ))
519}
520
521fn inset_area(width: f32, height: f32, less_width: f32, less_height: f32) -> f32 {
523 at_least(width - less_width, 0.0) * at_least(height - less_height, 0.0)
524}
525
526fn interior_repays(body: &ShapeRecordBody, curve: &ShapeRecordCurve) -> bool {
530 if fragment_kind(body.flags) != FRAGMENT_KIND_FILL {
531 return false;
532 }
533 let [top_left, top_right, bottom_right, bottom_left] = curve.radii;
534 let rounded = top_left.max(top_right).max(bottom_right.max(bottom_left)) > 0.0;
535 let [_, _, width, height] = body.rect;
536 let area = width * height;
537 rounded && area > 0.0 && band_interior_area(width, height, curve.radii) * 2.0 >= area
538}
539
540pub const OCCLUDER_MIN_AREA: f32 = 1024.0;
544
545pub(crate) fn interior_occludes(body: &ShapeRecordBody, curve: &ShapeRecordCurve) -> bool {
548 if fragment_kind(body.flags) != FRAGMENT_KIND_FILL || body.brush != 0 || body.color[3] < 1.0 {
549 return false;
550 }
551 let [_, _, width, height] = body.rect;
552 fill_interior_area(width, height, curve.radii) >= OCCLUDER_MIN_AREA
553}
554
555fn fragment_kind(flags: u32) -> u32 {
556 let kind = (flags >> KIND_SHIFT) & TWO_BITS;
557 if kind == RECORD_KIND_ARC {
558 FRAGMENT_KIND_ARC
559 } else if is_trapezoid(flags) {
560 FRAGMENT_KIND_TRAPEZOID
561 } else if kind == RECORD_KIND_LINE {
562 FRAGMENT_KIND_LINE
563 } else if flags & STROKED_BIT != 0 {
564 FRAGMENT_KIND_STROKE
565 } else {
566 FRAGMENT_KIND_FILL
567 }
568}
569
570fn is_trapezoid(flags: u32) -> bool {
571 (flags >> KIND_SHIFT) & TWO_BITS == RECORD_KIND_LINE
572 && (flags >> CAP_SHIFT) & TWO_BITS == TRAPEZOID_CAP
573}
574
575const STROKE_CAPS: [StrokeCap; 3] = [StrokeCap::Butt, StrokeCap::Round, StrokeCap::Square];
576const STROKE_JOINS: [StrokeJoin; 3] = [StrokeJoin::Miter, StrokeJoin::Round, StrokeJoin::Bevel];
577const TILE_MODES: [TileMode; 4] = [
578 TileMode::Clamp,
579 TileMode::Repeated,
580 TileMode::Mirror,
581 TileMode::Decal,
582];
583
584#[inline]
585fn pack_flags(kind: u32, stroke: Option<Stroke>, blend: BlendMode, band_cap: StrokeCap) -> u32 {
586 let mut flags = (kind << KIND_SHIFT) | ((blend as u32) << BLEND_SHIFT);
587 if let Some(stroke) = stroke {
588 flags |=
589 STROKED_BIT | ((stroke.cap as u32) << CAP_SHIFT) | ((stroke.join as u32) << JOIN_SHIFT);
590 }
591 flags | ((band_cap as u32) << BAND_CAP_SHIFT)
592}
593
594#[repr(C)]
596#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
597pub struct BrushRecord {
598 pub kind: u32,
600 pub tile_mode: u32,
602 pub stop_start: u32,
604 pub stop_count: u32,
605 pub params: [f32; 4],
608 pub explicit_start: u32,
612 pub explicit_len: u32,
613 pub reserved: [u32; 2],
614}
615
616const NO_EXPLICIT_STOPS: u32 = u32::MAX;
617
618#[derive(Clone, Copy, PartialEq, Eq)]
619enum BrushShading {
620 Solid,
621 Fragment,
622 Vertex,
623}
624
625impl BrushShading {
626 fn flag(self) -> u32 {
627 if self == Self::Vertex {
628 VERTEX_GRADIENT_BIT
629 } else {
630 0
631 }
632 }
633}
634
635#[repr(C)]
637#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
638pub struct GradientStopRecord {
639 pub color: [f32; 4],
640 pub position: [f32; 4],
642}
643
644#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
646pub enum RecordLane {
647 Shapes,
649 Others,
652 Content,
654}
655
656#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
660pub struct RecordSegment {
661 pub lane: RecordLane,
662 pub start: u32,
663 pub count: u32,
664 pub blend: BlendMode,
665 pub gradient: bool,
666 pub vertex_gradient: bool,
670 pub brushes: u8,
673 pub kinds: u8,
675 pub band_class: u8,
679 pub interiors: bool,
683 pub occluders: bool,
687 pub bare_interiors: bool,
691}
692
693impl RecordSegment {
694 pub fn range(&self) -> Range<usize> {
695 self.start as usize..(self.start + self.count) as usize
696 }
697
698 pub fn uniform_kind(&self) -> Option<u32> {
700 (self.kinds.count_ones() == 1).then(|| self.kinds.trailing_zeros())
701 }
702
703 pub fn uniform_brush(&self) -> Option<u32> {
706 (self.brushes.count_ones() == 1).then(|| self.brushes.trailing_zeros())
707 }
708}
709
710#[derive(Clone, Debug, Default)]
713pub struct RecordTables {
714 pub shapes: ShapeRecords,
715 pub brushes: Vec<BrushRecord>,
716 pub stops: Vec<GradientStopRecord>,
717 pub explicit_stops: Vec<f32>,
718 pub segments: Vec<RecordSegment>,
719 fingerprint: OnceLock<u64>,
720}
721
722impl PartialEq for RecordTables {
723 fn eq(&self, other: &Self) -> bool {
724 self.shapes == other.shapes
725 && self.brushes == other.brushes
726 && self.stops == other.stops
727 && self.explicit_stops == other.explicit_stops
728 && self.segments == other.segments
729 }
730}
731
732impl RecordTables {
733 fn clear(&mut self) {
734 self.shapes.clear();
735 self.brushes.clear();
736 self.stops.clear();
737 self.explicit_stops.clear();
738 self.segments.clear();
739 self.fingerprint.take();
740 }
741
742 fn trim_growth(&mut self) {
746 use crate::shape_records::trim_growth;
747 self.shapes.trim_growth();
748 trim_growth(&mut self.brushes);
749 trim_growth(&mut self.stops);
750 trim_growth(&mut self.explicit_stops);
751 trim_growth(&mut self.segments);
752 }
753
754 fn with_capacity_of(&self) -> Self {
757 Self {
758 shapes: ShapeRecords::with_capacity(self.shapes.capacity()),
759 brushes: Vec::with_capacity(self.brushes.capacity()),
760 stops: Vec::with_capacity(self.stops.capacity()),
761 explicit_stops: Vec::with_capacity(self.explicit_stops.capacity()),
762 segments: Vec::with_capacity(self.segments.capacity()),
763 fingerprint: OnceLock::new(),
764 }
765 }
766
767 pub fn fingerprint(&self) -> u64 {
770 *self.fingerprint.get_or_init(|| {
771 let mut hasher = FxHasher::default();
772 hasher.write(bytemuck::cast_slice(self.shapes.bodies()));
773 hasher.write(bytemuck::cast_slice(self.shapes.curves()));
774 hasher.write(self.shapes.source_bytes());
775 hasher.write(bytemuck::cast_slice(&self.brushes));
776 hasher.write(bytemuck::cast_slice(&self.stops));
777 hasher.write(bytemuck::cast_slice(&self.explicit_stops));
778 self.segments.hash(&mut hasher);
779 hasher.finish()
780 })
781 }
782
783 pub fn heap_bytes(&self) -> usize {
785 self.shapes.heap_bytes()
786 + self.brushes.capacity() * std::mem::size_of::<BrushRecord>()
787 + self.stops.capacity() * std::mem::size_of::<GradientStopRecord>()
788 + self.explicit_stops.capacity() * std::mem::size_of::<f32>()
789 + self.segments.capacity() * std::mem::size_of::<RecordSegment>()
790 }
791}
792
793#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
796pub struct RecordingSummary {
797 pub has_text: bool,
799 pub has_shadow: bool,
801 pub has_non_shadow: bool,
803 pub has_pixel_sensitive: bool,
806 pub has_shapes: bool,
808 pub has_others: bool,
810 pub has_content_markers: bool,
812}
813
814impl RecordingSummary {
815 fn note(&mut self, primitive: &DrawPrimitive) {
816 let primitive = unwrap_blend(primitive);
817 if matches!(primitive, DrawPrimitive::Shadow(_)) {
818 self.has_shadow = true;
819 return;
820 }
821 if matches!(primitive, DrawPrimitive::Content) {
822 return;
823 }
824 self.has_non_shadow = true;
825 match primitive {
826 DrawPrimitive::Text(_) => {
827 self.has_text = true;
828 self.has_pixel_sensitive = true;
829 }
830 DrawPrimitive::Image { .. } => self.has_pixel_sensitive = true,
831 _ => {}
832 }
833 }
834
835 fn merge(&mut self, other: Self) {
836 self.has_text |= other.has_text;
837 self.has_shadow |= other.has_shadow;
838 self.has_non_shadow |= other.has_non_shadow;
839 self.has_pixel_sensitive |= other.has_pixel_sensitive;
840 self.has_shapes |= other.has_shapes;
841 self.has_others |= other.has_others;
842 self.has_content_markers |= other.has_content_markers;
843 }
844
845 pub fn shapes_only(&self) -> bool {
848 self.has_shapes && !self.has_others && !self.has_content_markers
849 }
850}
851
852fn unwrap_blend(mut primitive: &DrawPrimitive) -> &DrawPrimitive {
853 while let DrawPrimitive::Blend {
854 primitive: inner, ..
855 } = primitive
856 {
857 primitive = inner;
858 }
859 primitive
860}
861
862pub fn expand_rect(rect: Rect, margin: f32) -> Rect {
864 Rect {
865 x: rect.x - margin,
866 y: rect.y - margin,
867 width: rect.width + margin * 2.0,
868 height: rect.height + margin * 2.0,
869 }
870}
871
872pub fn primitive_coverage_rect(primitive: &DrawPrimitive) -> Option<Rect> {
875 match primitive {
876 DrawPrimitive::Blend { primitive, .. } => primitive_coverage_rect(primitive),
877 DrawPrimitive::Rect { rect, stroke, .. }
878 | DrawPrimitive::RoundRect { rect, stroke, .. }
879 | DrawPrimitive::Arc { rect, stroke, .. } => {
880 let half_stroke = stroke.as_ref().map_or(0.0, |stroke| stroke.width * 0.5);
881 Some(expand_rect(*rect, half_stroke))
882 }
883 DrawPrimitive::Line {
884 start, end, stroke, ..
885 } => Some(LineGeometry::new(*start, *end, *stroke).bounds()),
886 DrawPrimitive::Trapezoid { trapezoid, .. } => Some(trapezoid.bounds()),
887 DrawPrimitive::Image { rect, .. } => Some(*rect),
888 DrawPrimitive::Text(text) => Some(text.rect),
889 DrawPrimitive::Content | DrawPrimitive::Shadow(_) => None,
890 }
891}
892
893#[derive(Clone, Debug)]
898pub struct ShapeRecorder {
899 tables: RecordTables,
900 last_segment_key: u32,
901 segment_waste: u32,
902 min: [f32; 2],
903 max: [f32; 2],
904}
905
906impl Default for ShapeRecorder {
907 fn default() -> Self {
908 Self {
909 tables: RecordTables::default(),
910 last_segment_key: NO_SEGMENT_KEY,
911 segment_waste: 0,
912 min: [f32::INFINITY; 2],
913 max: [f32::NEG_INFINITY; 2],
914 }
915 }
916}
917
918impl PartialEq for ShapeRecorder {
919 fn eq(&self, other: &Self) -> bool {
920 self.tables == other.tables
921 }
922}
923
924pub enum Recorded {
926 Shape(Rect),
928 Other(DrawPrimitive),
930}
931
932#[inline]
933fn extend_segment_in(tables: &mut RecordTables, extend: bool, opened: RecordSegment) {
934 if extend {
935 let last = tables.segments.last_mut().expect("a keyed segment exists");
936 last.count += 1;
937 last.brushes |= opened.brushes;
938 last.kinds |= opened.kinds;
939 last.band_class = last.band_class.max(opened.band_class);
940 last.interiors |= opened.interiors;
941 last.occluders |= opened.occluders;
942 last.bare_interiors |= opened.bare_interiors;
943 return;
944 }
945 tables.segments.push(opened);
946}
947
948impl ShapeRecorder {
949 pub fn tables(&self) -> &RecordTables {
951 &self.tables
952 }
953
954 fn tables_mut(&mut self) -> &mut RecordTables {
955 let tables = &mut self.tables;
956 tables.fingerprint.take();
957 tables
958 }
959
960 pub fn is_empty(&self) -> bool {
961 self.tables.shapes.is_empty()
962 }
963
964 pub fn all_segments(&self) -> Range<u32> {
966 0..self.tables.segments.len() as u32
967 }
968
969 pub fn bounds(&self) -> Option<Rect> {
972 (self.min[0] <= self.max[0] && self.min[1] <= self.max[1]).then(|| Rect {
973 x: self.min[0],
974 y: self.min[1],
975 width: self.max[0] - self.min[0],
976 height: self.max[1] - self.min[1],
977 })
978 }
979
980 pub fn fingerprint(&self) -> u64 {
982 self.tables.fingerprint()
983 }
984
985 pub fn clear(&mut self) {
987 self.tables.clear();
988 self.last_segment_key = NO_SEGMENT_KEY;
989 self.segment_waste = 0;
990 self.min = [f32::INFINITY; 2];
991 self.max = [f32::NEG_INFINITY; 2];
992 }
993
994 pub fn push_primitive(&mut self, primitive: DrawPrimitive) -> Recorded {
997 match primitive {
998 DrawPrimitive::Blend {
999 primitive,
1000 blend_mode,
1001 } => match self.push_shape_primitive(*primitive, blend_mode) {
1002 Recorded::Other(inner) => Recorded::Other(DrawPrimitive::Blend {
1003 primitive: Box::new(inner),
1004 blend_mode,
1005 }),
1006 recorded => recorded,
1007 },
1008 other => self.push_shape_primitive(other, BlendMode::SrcOver),
1009 }
1010 }
1011
1012 pub fn push_shape_primitive(
1015 &mut self,
1016 primitive: DrawPrimitive,
1017 blend_mode: BlendMode,
1018 ) -> Recorded {
1019 Recorded::Shape(match primitive {
1020 DrawPrimitive::Rect {
1021 rect,
1022 brush,
1023 stroke,
1024 } => self.push_rect(rect, &brush, stroke, blend_mode),
1025 DrawPrimitive::RoundRect {
1026 rect,
1027 brush,
1028 radii,
1029 stroke,
1030 } => self.push_round_rect(rect, &brush, radii, stroke, blend_mode),
1031 DrawPrimitive::Arc {
1032 rect,
1033 brush,
1034 center,
1035 radius,
1036 start_angle,
1037 sweep_angle,
1038 stroke,
1039 inner_radius,
1040 } => self.push_arc(
1041 rect,
1042 &ArcRecordArgs {
1043 brush: &brush,
1044 center,
1045 radius,
1046 start_angle,
1047 sweep_angle,
1048 stroke,
1049 inner_radius,
1050 blend_mode,
1051 },
1052 ),
1053 DrawPrimitive::Line {
1054 brush,
1055 start,
1056 end,
1057 stroke,
1058 ..
1059 } => self.push_line(
1060 &LineGeometry::new(start, end, stroke),
1061 &brush,
1062 stroke,
1063 blend_mode,
1064 ),
1065 DrawPrimitive::Trapezoid {
1066 rect,
1067 brush,
1068 trapezoid,
1069 } => self.push_trapezoids(rect, &[trapezoid], &brush, blend_mode),
1070 other => return Recorded::Other(other),
1071 })
1072 }
1073
1074 fn push_content_segment(&mut self) {
1075 self.last_segment_key = NO_SEGMENT_KEY;
1076 self.tables_mut().segments.push(RecordSegment {
1077 lane: RecordLane::Content,
1078 start: 0,
1079 count: 1,
1080 blend: BlendMode::SrcOver,
1081 gradient: false,
1082 vertex_gradient: false,
1083 brushes: 0,
1084 kinds: 0,
1085 band_class: 0,
1086 interiors: false,
1087 occluders: false,
1088 bare_interiors: false,
1089 });
1090 }
1091
1092 pub fn push_line(
1098 &mut self,
1099 line: &LineGeometry,
1100 brush: &Brush,
1101 stroke: Stroke,
1102 blend: BlendMode,
1103 ) -> Rect {
1104 let (handle, color) = self.intern_brush(brush);
1105 let mut flags = pack_flags(RECORD_KIND_LINE, Some(stroke), blend, StrokeCap::Butt);
1106 if line.is_degenerate() {
1107 flags |= ARC_DEGENERATE_BIT;
1108 }
1109 let frame = line.frame();
1110 let ends = [line.start.x, line.start.y, line.end.x, line.end.y];
1111 let bounds = line.bounds();
1114 self.include_bounds(bounds);
1115 self.push_shape(
1116 ShapeRecordBody {
1117 rect: rect_row(line.end_bounds()),
1118 color,
1119 stroke_width: stroke.width,
1120 flags,
1121 brush: handle,
1122 placement: 0,
1123 arc_geometry: ends,
1124 },
1125 ShapeRecordCurve {
1126 radii: [0.0; 4],
1127 arc_normalized: [frame.direction.x, frame.direction.y, frame.half_length, 0.0],
1128 },
1129 ends,
1130 blend,
1131 None,
1132 );
1133 bounds
1134 }
1135
1136 pub fn push_trapezoids(
1143 &mut self,
1144 brush_rect: Rect,
1145 trapezoids: &[Trapezoid],
1146 brush: &Brush,
1147 blend: BlendMode,
1148 ) -> Rect {
1149 let (handle, color) = self.intern_brush(brush);
1150 let flags = pack_flags(RECORD_KIND_LINE, None, blend, StrokeCap::Butt)
1151 | (TRAPEZOID_CAP << CAP_SHIFT);
1152 let radii = rect_row(brush_rect);
1153 let shading = self.brush_shading(
1156 &ShapeRecordBody {
1157 brush: handle,
1158 flags,
1159 ..ShapeRecordBody::zeroed()
1160 },
1161 &ShapeRecordCurve {
1162 radii,
1163 arc_normalized: [0.0; 4],
1164 },
1165 );
1166 let mut reach: Option<Rect> = None;
1167 for trapezoid in trapezoids {
1168 let bounds = trapezoid.bounds();
1169 let open = (u32::from(trapezoid.open_left) * OPEN_LEFT_BIT)
1170 | (u32::from(trapezoid.open_right) * OPEN_RIGHT_BIT);
1171 self.push_shaded(
1172 ShapeRecordBody {
1173 rect: rect_row(bounds),
1174 color,
1175 stroke_width: 0.0,
1176 flags: flags | open,
1177 brush: handle,
1178 placement: 0,
1179 arc_geometry: [
1180 trapezoid.top[0],
1181 trapezoid.top[1],
1182 trapezoid.bottom[0],
1183 trapezoid.bottom[1],
1184 ],
1185 },
1186 ShapeRecordCurve {
1187 radii,
1188 arc_normalized: [
1189 trapezoid.left,
1190 trapezoid.right,
1191 trapezoid.right - trapezoid.left,
1192 0.0,
1193 ],
1194 },
1195 [0.0; 4],
1196 (blend, shading),
1197 None,
1198 );
1199 reach = Some(reach.map_or(bounds, |reach| reach.union(bounds)));
1200 }
1201 reach.unwrap_or(Rect::EMPTY)
1202 }
1203
1204 #[inline]
1205 pub fn push_rect(
1206 &mut self,
1207 rect: Rect,
1208 brush: &Brush,
1209 stroke: Option<Stroke>,
1210 blend: BlendMode,
1211 ) -> Rect {
1212 let (handle, color) = self.intern_brush(brush);
1213 self.push_shape(
1214 ShapeRecordBody {
1215 rect: rect_row(rect),
1216 color,
1217 stroke_width: stroke.map_or(0.0, |stroke| stroke.width),
1218 flags: pack_flags(RECORD_KIND_RECT, stroke, blend, StrokeCap::Butt),
1219 brush: handle,
1220 placement: 0,
1221 arc_geometry: [0.0; 4],
1222 },
1223 ShapeRecordCurve {
1224 radii: [0.0; 4],
1225 arc_normalized: [0.0; 4],
1226 },
1227 [0.0; 4],
1228 blend,
1229 None,
1230 )
1231 }
1232
1233 pub fn push_round_rect(
1234 &mut self,
1235 rect: Rect,
1236 brush: &Brush,
1237 radii: CornerRadii,
1238 stroke: Option<Stroke>,
1239 blend: BlendMode,
1240 ) -> Rect {
1241 let (handle, color) = self.intern_brush(brush);
1242 let mut flags = pack_flags(RECORD_KIND_ROUND_RECT, stroke, blend, StrokeCap::Butt);
1243 let mut arc_geometry = [0.0; 4];
1244 let mut source = [0.0; 4];
1245 let mut arc_normalized = [0.0; 4];
1246 let mut bucket = None;
1247 if let Some(ring) = stroked_circle_ring(rect, radii, stroke)
1248 && let band = BandRing::of_geometry(&ring)
1249 && let Some(ring_bucket) = band_bucket_for(
1250 &ring,
1251 ring.is_degenerate(),
1252 &band,
1253 expand_rect(rect, ring.half_thickness()),
1254 )
1255 {
1256 flags |= ARC_BANDED_BIT;
1257 arc_geometry = [
1258 ring.center.x,
1259 ring.center.y,
1260 ring.inner_radius,
1261 ring.outer_radius,
1262 ];
1263 source = [ring.mid_radius(), ring.inner_radius, 0.0, TAU];
1264 arc_normalized = [0.0, TAU, 0.0, 0.0];
1265 bucket = Some(ring_bucket);
1266 }
1267 self.push_shape(
1268 ShapeRecordBody {
1269 rect: rect_row(rect),
1270 color,
1271 stroke_width: stroke.map_or(0.0, |stroke| stroke.width),
1272 flags,
1273 brush: handle,
1274 placement: 0,
1275 arc_geometry,
1276 },
1277 ShapeRecordCurve {
1278 radii: [
1279 radii.top_left,
1280 radii.top_right,
1281 radii.bottom_right,
1282 radii.bottom_left,
1283 ],
1284 arc_normalized,
1285 },
1286 source,
1287 blend,
1288 bucket,
1289 )
1290 }
1291
1292 pub fn push_arc(&mut self, rect: Rect, args: &ArcRecordArgs<'_>) -> Rect {
1295 let geometry = normalized_band(args);
1296 self.push_arc_band(args, &geometry, Some(rect), geometry.is_degenerate())
1297 }
1298
1299 #[inline]
1306 pub fn push_scope_arc(&mut self, args: &ArcRecordArgs<'_>, geometry: &ArcGeometry) -> Rect {
1307 debug_assert!(
1308 !geometry.is_degenerate(),
1309 "the scope skips degenerate bands"
1310 );
1311 self.push_arc_band(args, geometry, None, false)
1312 }
1313
1314 #[inline]
1315 fn push_arc_band(
1316 &mut self,
1317 args: &ArcRecordArgs<'_>,
1318 geometry: &ArcGeometry,
1319 rect: Option<Rect>,
1320 degenerate: bool,
1321 ) -> Rect {
1322 let (handle, color) = self.intern_brush(args.brush);
1323 let mut flags = pack_flags(RECORD_KIND_ARC, args.stroke, args.blend_mode, geometry.cap);
1324 if degenerate {
1325 flags |= ARC_DEGENERATE_BIT;
1326 }
1327 let rect = rect.unwrap_or_else(|| {
1328 flags |= ARC_RECT_LOOSE_BIT;
1329 band_disc(geometry)
1330 });
1331 let ring = BandRing::of_geometry(geometry);
1332 let bucket = band_bucket_for(geometry, degenerate, &ring, rect);
1333 if bucket.is_some() {
1334 flags |= ARC_BANDED_BIT;
1335 }
1336 self.push_shape(
1337 ShapeRecordBody {
1338 rect: rect_row(rect),
1339 color,
1340 stroke_width: args.stroke.map_or(0.0, |stroke| stroke.width),
1341 flags,
1342 brush: handle,
1343 placement: 0,
1344 arc_geometry: [
1345 args.center.x,
1346 args.center.y,
1347 geometry.inner_radius,
1348 geometry.outer_radius,
1349 ],
1350 },
1351 ShapeRecordCurve {
1352 radii: [0.0; 4],
1353 arc_normalized: [geometry.start_angle, geometry.sweep_angle, 0.0, 0.0],
1354 },
1355 [
1356 args.radius,
1357 args.inner_radius,
1358 args.start_angle,
1359 args.sweep_angle,
1360 ],
1361 args.blend_mode,
1362 bucket,
1363 )
1364 }
1365
1366 #[inline(always)]
1367 fn push_shape(
1368 &mut self,
1369 body: ShapeRecordBody,
1370 curve: ShapeRecordCurve,
1371 source: [f32; 4],
1372 blend: BlendMode,
1373 band_bucket: Option<usize>,
1374 ) -> Rect {
1375 let shading = self.brush_shading(&body, &curve);
1376 self.push_shaded(body, curve, source, (blend, shading), band_bucket)
1377 }
1378
1379 fn push_shaded(
1381 &mut self,
1382 mut body: ShapeRecordBody,
1383 curve: ShapeRecordCurve,
1384 source: [f32; 4],
1385 (blend, shading): (BlendMode, BrushShading),
1386 band_bucket: Option<usize>,
1387 ) -> Rect {
1388 let half_stroke = if body.flags & STROKED_BIT != 0 {
1389 body.stroke_width * 0.5
1390 } else {
1391 0.0
1392 };
1393 let coverage = expand_rect(row_rect(body.rect), half_stroke);
1394 self.include_bounds(coverage);
1395 let index = self.tables.shapes.len() as u32;
1396 body.flags |= shading.flag();
1397 let brush_bit = 1u8
1398 << match body.brush {
1399 0 => 0,
1400 index => self.tables.brushes[index as usize - 1].kind,
1401 };
1402 let kind_bit = 1u8 << fragment_kind(body.flags);
1403 let band_class = band_bucket.unwrap_or(0) as u8;
1404 body.flags |= u32::from(band_class) << BAND_CLASS_SHIFT;
1405 let extend =
1406 self.note_segment_key(RecordLane::Shapes, blend, shading, is_trapezoid(body.flags))
1407 && self.segment_takes_class(band_class);
1408 if !extend {
1409 self.segment_waste = 0;
1410 }
1411 let joined = extend.then(|| self.tables.segments.last()).flatten();
1416 let translucent = body.brush != 0 || body.color[3] < 1.0;
1417 let interiors = !joined
1418 .is_some_and(|segment| segment.interiors && (segment.bare_interiors || !translucent))
1419 && interior_repays(&body, &curve);
1420 let occluders =
1421 !joined.is_some_and(|segment| segment.occluders) && interior_occludes(&body, &curve);
1422 let bare_interiors = interiors && translucent;
1423 let tables = self.tables_mut();
1424 tables.shapes.push(body, curve, source);
1425 extend_segment_in(
1426 tables,
1427 extend,
1428 RecordSegment {
1429 lane: RecordLane::Shapes,
1430 start: index,
1431 count: 1,
1432 blend,
1433 gradient: shading == BrushShading::Fragment,
1434 vertex_gradient: shading == BrushShading::Vertex,
1435 brushes: brush_bit,
1436 kinds: kind_bit,
1437 band_class,
1438 interiors,
1439 occluders,
1440 bare_interiors,
1441 },
1442 );
1443 coverage
1444 }
1445
1446 fn extend_segment(&mut self, lane: RecordLane, index: u32, blend: BlendMode, kind_bit: u8) {
1447 let extend = self.note_segment_key(lane, blend, BrushShading::Solid, false);
1448 if !extend {
1449 self.segment_waste = 0;
1450 }
1451 extend_segment_in(
1452 self.tables_mut(),
1453 extend,
1454 RecordSegment {
1455 lane,
1456 start: index,
1457 count: 1,
1458 blend,
1459 gradient: false,
1460 vertex_gradient: false,
1461 brushes: 0,
1462 kinds: kind_bit,
1463 band_class: 0,
1464 interiors: false,
1465 occluders: false,
1466 bare_interiors: false,
1467 },
1468 );
1469 }
1470
1471 #[inline]
1476 fn segment_takes_class(&mut self, band_class: u8) -> bool {
1477 let last = self.tables.segments.last().expect("a keyed segment exists");
1478 let held = ARC_BUCKET_SEGMENTS[last.band_class as usize];
1479 let wanted = ARC_BUCKET_SEGMENTS[band_class as usize];
1480 let waste = if wanted > held {
1481 last.count * (wanted - held)
1482 } else {
1483 held - wanted
1484 };
1485 if self.segment_waste + waste > SEGMENT_WASTE_QUADS {
1486 return false;
1487 }
1488 self.segment_waste += waste;
1489 true
1490 }
1491
1492 #[inline]
1497 fn note_segment_key(
1498 &mut self,
1499 lane: RecordLane,
1500 blend: BlendMode,
1501 shading: BrushShading,
1502 slice: bool,
1503 ) -> bool {
1504 let key = ((lane as u32) << 16)
1505 | (u32::from(slice) << 15)
1506 | ((blend as u32) << 2)
1507 | shading as u32;
1508 let extend = key == self.last_segment_key;
1509 self.last_segment_key = key;
1510 extend
1511 }
1512
1513 #[inline]
1516 fn include_bounds(&mut self, rect: Rect) {
1517 let right = rect.x + rect.width;
1518 let bottom = rect.y + rect.height;
1519 if rect.x < self.min[0] {
1520 self.min[0] = rect.x;
1521 }
1522 if rect.y < self.min[1] {
1523 self.min[1] = rect.y;
1524 }
1525 if right > self.max[0] {
1526 self.max[0] = right;
1527 }
1528 if bottom > self.max[1] {
1529 self.max[1] = bottom;
1530 }
1531 }
1532
1533 #[inline]
1534 fn brush_shading(&self, body: &ShapeRecordBody, curve: &ShapeRecordCurve) -> BrushShading {
1540 if body.brush == 0 {
1541 return BrushShading::Solid;
1542 }
1543 let tables = &self.tables;
1544 let Some(brush) = tables.brushes.get(body.brush as usize - 1) else {
1545 return BrushShading::Fragment;
1546 };
1547 let vertex = if is_trapezoid(body.flags) {
1548 vertex_gradient::clamps_between_two_stops(brush, &tables.stops, curve.radii)
1549 } else {
1550 body.flags & RECT_FILL_MASK == 0
1551 && vertex_gradient::spans_quad(brush, &tables.stops, body.rect)
1552 };
1553 if vertex {
1554 BrushShading::Vertex
1555 } else {
1556 BrushShading::Fragment
1557 }
1558 }
1559
1560 #[inline]
1564 fn intern_brush(&mut self, brush: &Brush) -> (u32, [f32; 4]) {
1565 match brush {
1566 Brush::Solid(color) => (0, [color.0, color.1, color.2, color.3]),
1567 gradient => self.intern_gradient(gradient),
1568 }
1569 }
1570
1571 #[cold]
1572 #[inline(never)]
1573 fn intern_gradient(&mut self, brush: &Brush) -> (u32, [f32; 4]) {
1574 let (kind, tile_mode, params, colors, stops) = match brush {
1575 Brush::Solid(color) => return (0, [color.0, color.1, color.2, color.3]),
1576 Brush::LinearGradient {
1577 colors,
1578 stops,
1579 start,
1580 end,
1581 tile_mode,
1582 } => (
1583 BRUSH_KIND_LINEAR,
1584 *tile_mode,
1585 [start.x, start.y, end.x, end.y],
1586 colors,
1587 stops,
1588 ),
1589 Brush::RadialGradient {
1590 colors,
1591 stops,
1592 center,
1593 radius,
1594 tile_mode,
1595 } => (
1596 BRUSH_KIND_RADIAL,
1597 *tile_mode,
1598 [center.x, center.y, *radius, 0.0],
1599 colors,
1600 stops,
1601 ),
1602 Brush::SweepGradient {
1603 colors,
1604 stops,
1605 center,
1606 } => (
1607 BRUSH_KIND_SWEEP,
1608 TileMode::Clamp,
1609 [center.x, center.y, 0.0, 0.0],
1610 colors,
1611 stops,
1612 ),
1613 };
1614 let tables = self.tables_mut();
1615 let stop_start = tables.stops.len() as u32;
1616 let count = colors.len();
1617 let positions = stops.as_deref().filter(|values| values.len() == count);
1618 for (index, color) in colors.iter().enumerate() {
1619 let position = positions.map_or_else(
1620 || {
1621 if count <= 1 {
1622 0.0
1623 } else {
1624 index as f32 / (count - 1) as f32
1625 }
1626 },
1627 |values| values[index],
1628 );
1629 tables.stops.push(GradientStopRecord {
1630 color: [color.0, color.1, color.2, color.3],
1631 position: [position, 0.0, 0.0, 0.0],
1632 });
1633 }
1634 let (explicit_start, explicit_len) = match stops {
1635 Some(values) => {
1636 let start = tables.explicit_stops.len() as u32;
1637 tables.explicit_stops.extend_from_slice(values);
1638 (start, values.len() as u32)
1639 }
1640 None => (0, NO_EXPLICIT_STOPS),
1641 };
1642 let record = BrushRecord {
1643 kind,
1644 tile_mode: tile_mode as u32,
1645 stop_start,
1646 stop_count: count as u32,
1647 params,
1648 explicit_start,
1649 explicit_len,
1650 reserved: [0; 2],
1651 };
1652 tables.brushes.push(record);
1653 let handle = tables.brushes.len() as u32;
1654 let first = colors.first().copied().unwrap_or(Color(0.0, 0.0, 0.0, 0.0));
1655 (handle, [first.0, first.1, first.2, first.3])
1656 }
1657}
1658
1659#[derive(Clone, Debug)]
1664pub struct CommandRecording {
1665 shapes: Arc<ShapeRecorder>,
1666 content: RecordingContent,
1667 fingerprint: OnceCell<u64>,
1668}
1669
1670#[derive(Clone, Debug)]
1671struct RecordingContent {
1672 others: Vec<DrawPrimitive>,
1673 min: [f32; 2],
1674 max: [f32; 2],
1675 summary: RecordingSummary,
1676 content_markers: u32,
1677}
1678
1679impl Default for RecordingContent {
1680 fn default() -> Self {
1681 Self {
1682 others: Vec::new(),
1683 min: [f32::INFINITY; 2],
1684 max: [f32::NEG_INFINITY; 2],
1685 summary: RecordingSummary::default(),
1686 content_markers: 0,
1687 }
1688 }
1689}
1690
1691thread_local! {
1692 static NO_SHAPES: Arc<ShapeRecorder> = Arc::new(ShapeRecorder::default());
1695}
1696
1697impl Default for CommandRecording {
1698 fn default() -> Self {
1699 Self {
1700 shapes: NO_SHAPES.with(Arc::clone),
1701 content: RecordingContent::default(),
1702 fingerprint: OnceCell::new(),
1703 }
1704 }
1705}
1706
1707impl PartialEq for CommandRecording {
1708 fn eq(&self, other: &Self) -> bool {
1709 self.shapes == other.shapes
1710 && self.content.others == other.content.others
1711 && self.content.content_markers == other.content.content_markers
1712 }
1713}
1714
1715impl CommandRecording {
1716 pub fn try_take_reusable(&mut self) -> Option<Self> {
1746 Arc::get_mut(&mut self.shapes)?;
1747 Some(std::mem::take(self))
1748 }
1749
1750 pub fn into_recorder(self) -> CommandRecorder {
1753 let (shapes, vessel) = take_shapes(self.shapes, ShapeRecorder::clone);
1754 CommandRecorder {
1755 shapes,
1756 content: self.content,
1757 vessel,
1758 }
1759 }
1760
1761 pub fn from_primitives(primitives: impl IntoIterator<Item = DrawPrimitive>) -> Self {
1762 CommandRecorder::from_primitives(primitives).finish()
1763 }
1764
1765 pub fn shape_capacity(&self) -> usize {
1766 self.shapes.tables.shapes.capacity()
1767 }
1768
1769 pub fn pod_heap_bytes(&self) -> usize {
1771 self.shapes.tables.heap_bytes()
1772 }
1773
1774 pub fn tables(&self) -> &RecordTables {
1776 self.shapes.tables()
1777 }
1778
1779 pub fn shape_recorder(&self) -> &Arc<ShapeRecorder> {
1781 &self.shapes
1782 }
1783
1784 pub fn len_in(&self, segments: &Range<u32>) -> usize {
1786 self.segments_in(segments)
1787 .filter(|segment| segment.lane != RecordLane::Content)
1788 .map(|segment| segment.count as usize)
1789 .sum()
1790 }
1791
1792 pub fn shapes(&self) -> &ShapeRecords {
1794 &self.shapes.tables.shapes
1795 }
1796
1797 pub fn brushes(&self) -> &[BrushRecord] {
1798 &self.shapes.tables.brushes
1799 }
1800
1801 pub fn stops(&self) -> &[GradientStopRecord] {
1802 &self.shapes.tables.stops
1803 }
1804
1805 pub fn others(&self) -> &[DrawPrimitive] {
1806 &self.content.others
1807 }
1808
1809 pub fn segments(&self) -> &[RecordSegment] {
1810 &self.shapes.tables.segments
1811 }
1812
1813 pub fn all_segments(&self) -> Range<u32> {
1815 0..self.shapes.tables.segments.len() as u32
1816 }
1817
1818 pub fn bounds(&self) -> Option<Rect> {
1821 let others = (self.content.min[0] <= self.content.max[0]
1822 && self.content.min[1] <= self.content.max[1])
1823 .then(|| Rect {
1824 x: self.content.min[0],
1825 y: self.content.min[1],
1826 width: self.content.max[0] - self.content.min[0],
1827 height: self.content.max[1] - self.content.min[1],
1828 });
1829 match (self.shapes.bounds(), others) {
1830 (Some(shapes), Some(others)) => Some(union_rect(shapes, others)),
1831 (shapes, others) => shapes.or(others),
1832 }
1833 }
1834
1835 pub fn summary(&self) -> RecordingSummary {
1836 self.content.summary
1837 }
1838
1839 pub fn content_markers(&self) -> u32 {
1840 self.content.content_markers
1841 }
1842
1843 pub fn len(&self) -> usize {
1845 self.shapes.tables.shapes.len()
1846 + self.content.others.len()
1847 + self.content.content_markers as usize
1848 }
1849
1850 pub fn is_empty(&self) -> bool {
1851 self.len() == 0
1852 }
1853
1854 pub fn fingerprint(&self) -> u64 {
1859 *self.fingerprint.get_or_init(|| {
1860 let mut hasher = FxHasher::default();
1861 hasher.write_u64(self.shapes.fingerprint());
1862 for primitive in &self.content.others {
1863 hasher.write_u64(primitive.render_hash());
1864 }
1865 hasher.write_u32(self.content.content_markers);
1866 hasher.finish()
1867 })
1868 }
1869
1870 pub fn is_empty_in(&self, segments: &Range<u32>) -> bool {
1872 !self
1873 .segments_in(segments)
1874 .any(|segment| segment.lane != RecordLane::Content && segment.count > 0)
1875 }
1876
1877 pub fn segments_in(&self, segments: &Range<u32>) -> std::slice::Iter<'_, RecordSegment> {
1878 self.shapes.tables.segments[segments.start as usize..segments.end as usize].iter()
1879 }
1880
1881 pub fn summary_in(&self, segments: &Range<u32>) -> RecordingSummary {
1883 if *segments == self.all_segments() {
1884 return self.content.summary;
1885 }
1886 let mut summary = RecordingSummary::default();
1887 for segment in self.segments_in(segments) {
1888 match segment.lane {
1889 RecordLane::Shapes if segment.count > 0 => {
1890 summary.has_non_shadow = true;
1891 summary.has_shapes = true;
1892 }
1893 RecordLane::Others => {
1894 summary.has_others = true;
1895 for primitive in &self.content.others[segment.range()] {
1896 summary.note(primitive);
1897 }
1898 }
1899 RecordLane::Content => summary.has_content_markers = true,
1900 RecordLane::Shapes => {}
1901 }
1902 }
1903 summary
1904 }
1905
1906 pub fn content_split(&self, behind: bool) -> Range<u32> {
1909 let last_marker = self
1910 .shapes
1911 .tables
1912 .segments
1913 .iter()
1914 .rposition(|segment| segment.lane == RecordLane::Content);
1915 match (last_marker, behind) {
1916 (Some(index), true) => 0..index as u32,
1917 (Some(index), false) => index as u32 + 1..self.shapes.tables.segments.len() as u32,
1918 (None, true) => 0..0,
1919 (None, false) => self.all_segments(),
1920 }
1921 }
1922
1923 pub fn coverage_rects(&self, segments: Range<u32>) -> impl Iterator<Item = Rect> + '_ {
1925 self.segments_in(&segments).flat_map(move |segment| {
1926 segment.range().filter_map(move |index| match segment.lane {
1927 RecordLane::Shapes => self
1928 .shapes
1929 .tables
1930 .shapes
1931 .get(index)
1932 .map(|record| record.coverage_rect()),
1933 RecordLane::Others => primitive_coverage_rect(&self.content.others[index]),
1934 RecordLane::Content => None,
1935 })
1936 })
1937 }
1938
1939 pub fn primitives(&self, segments: Range<u32>) -> impl Iterator<Item = DrawPrimitive> + '_ {
1942 self.segments_in(&segments)
1943 .flat_map(|segment| self.segment_primitives(segment, false))
1944 }
1945
1946 pub fn primitives_with_markers(&self) -> impl Iterator<Item = DrawPrimitive> + '_ {
1948 self.shapes
1949 .tables
1950 .segments
1951 .iter()
1952 .flat_map(|segment| self.segment_primitives(segment, true))
1953 }
1954
1955 pub fn into_primitives_with_markers(self) -> Vec<DrawPrimitive> {
1956 self.primitives_with_markers().collect()
1957 }
1958
1959 fn segment_primitives<'a>(
1960 &'a self,
1961 segment: &RecordSegment,
1962 markers: bool,
1963 ) -> impl Iterator<Item = DrawPrimitive> + use<'a> {
1964 let lane = segment.lane;
1965 segment.range().filter_map(move |index| match lane {
1966 RecordLane::Shapes => Some(self.materialize_shape(index)),
1967 RecordLane::Others => Some(self.content.others[index].clone()),
1968 RecordLane::Content => markers.then_some(DrawPrimitive::Content),
1969 })
1970 }
1971
1972 pub fn materialize_shape(&self, index: usize) -> DrawPrimitive {
1974 let record = self
1975 .shapes
1976 .tables
1977 .shapes
1978 .get(index)
1979 .expect("recorded shape index");
1980 let rect = record.rect_value();
1981 let brush = self.brush_of(&record);
1982 let stroke = record.stroke();
1983 let primitive = match (record.kind(), record.trapezoid()) {
1984 (_, Some(trapezoid)) => DrawPrimitive::Trapezoid {
1985 rect: row_rect(record.radii),
1986 brush,
1987 trapezoid,
1988 },
1989 (RECORD_KIND_ROUND_RECT, _) => DrawPrimitive::RoundRect {
1990 rect,
1991 brush,
1992 radii: CornerRadii {
1993 top_left: record.radii[0],
1994 top_right: record.radii[1],
1995 bottom_right: record.radii[2],
1996 bottom_left: record.radii[3],
1997 },
1998 stroke,
1999 },
2000 (RECORD_KIND_LINE, _) => DrawPrimitive::Line {
2001 rect,
2002 brush,
2003 start: Point::new(record.arc[0], record.arc[1]),
2004 end: Point::new(record.arc_band[2], record.arc_band[3]),
2005 stroke: stroke.unwrap_or_default(),
2006 },
2007 (RECORD_KIND_ARC, _) => DrawPrimitive::Arc {
2008 rect,
2009 brush,
2010 center: Point::new(record.arc[0], record.arc[1]),
2011 radius: record.arc[2],
2012 start_angle: record.arc_band[0],
2013 sweep_angle: record.arc_band[1],
2014 stroke,
2015 inner_radius: record.arc[3],
2016 },
2017 _ => DrawPrimitive::Rect {
2018 rect,
2019 brush,
2020 stroke,
2021 },
2022 };
2023 let blend_mode = record.blend_mode();
2024 if blend_mode == BlendMode::SrcOver {
2025 primitive
2026 } else {
2027 DrawPrimitive::Blend {
2028 primitive: Box::new(primitive),
2029 blend_mode,
2030 }
2031 }
2032 }
2033
2034 pub fn brush_of(&self, record: &ShapeRecord) -> Brush {
2037 if record.brush == 0 {
2038 return Brush::Solid(Color(
2039 record.color[0],
2040 record.color[1],
2041 record.color[2],
2042 record.color[3],
2043 ));
2044 }
2045 let tables = &self.shapes.tables;
2046 let brush = &tables.brushes[record.brush as usize - 1];
2047 let colors = tables.stops
2048 [brush.stop_start as usize..(brush.stop_start + brush.stop_count) as usize]
2049 .iter()
2050 .map(|stop| Color(stop.color[0], stop.color[1], stop.color[2], stop.color[3]))
2051 .collect();
2052 let stops = (brush.explicit_len != NO_EXPLICIT_STOPS).then(|| {
2053 tables.explicit_stops[brush.explicit_start as usize
2054 ..(brush.explicit_start + brush.explicit_len) as usize]
2055 .to_vec()
2056 });
2057 let [a, b, c, d] = brush.params;
2058 let tile_mode = TILE_MODES[brush.tile_mode as usize];
2059 match brush.kind {
2060 BRUSH_KIND_RADIAL => Brush::RadialGradient {
2061 colors,
2062 stops,
2063 center: Point::new(a, b),
2064 radius: c,
2065 tile_mode,
2066 },
2067 BRUSH_KIND_SWEEP => Brush::SweepGradient {
2068 colors,
2069 stops,
2070 center: Point::new(a, b),
2071 },
2072 _ => Brush::LinearGradient {
2073 colors,
2074 stops,
2075 start: Point::new(a, b),
2076 end: Point::new(c, d),
2077 tile_mode,
2078 },
2079 }
2080 }
2081}
2082
2083#[derive(Debug, Default)]
2086pub struct CommandRecorder {
2087 shapes: ShapeRecorder,
2088 content: RecordingContent,
2089 vessel: Option<Arc<ShapeRecorder>>,
2092}
2093
2094impl Clone for CommandRecorder {
2095 fn clone(&self) -> Self {
2096 Self {
2097 shapes: self.shapes.clone(),
2098 content: self.content.clone(),
2099 vessel: None,
2100 }
2101 }
2102}
2103
2104fn take_shapes(
2107 mut shared: Arc<ShapeRecorder>,
2108 copy: impl FnOnce(&ShapeRecorder) -> ShapeRecorder,
2109) -> (ShapeRecorder, Option<Arc<ShapeRecorder>>) {
2110 match Arc::get_mut(&mut shared) {
2111 Some(shapes) => (std::mem::take(shapes), Some(shared)),
2112 None => (copy(&shared), None),
2113 }
2114}
2115
2116fn fill_vessel(vessel: Option<Arc<ShapeRecorder>>, shapes: ShapeRecorder) -> Arc<ShapeRecorder> {
2118 let Some(mut vessel) = vessel else {
2119 return Arc::new(shapes);
2120 };
2121 match Arc::get_mut(&mut vessel) {
2122 Some(slot) => {
2123 *slot = shapes;
2124 vessel
2125 }
2126 None => Arc::new(shapes),
2127 }
2128}
2129
2130impl CommandRecorder {
2131 pub fn from_primitives(primitives: impl IntoIterator<Item = DrawPrimitive>) -> Self {
2133 let mut recorder = Self::default();
2134 for primitive in primitives {
2135 recorder.push_primitive(primitive);
2136 }
2137 recorder
2138 }
2139
2140 pub fn reusing(recording: CommandRecording) -> Self {
2143 let (shapes, vessel) = take_shapes(recording.shapes, |shared| ShapeRecorder {
2144 tables: shared.tables.with_capacity_of(),
2145 ..ShapeRecorder::default()
2146 });
2147 let mut recorder = Self {
2148 shapes,
2149 content: recording.content,
2150 vessel,
2151 };
2152 recorder.clear();
2153 recorder
2154 }
2155
2156 pub fn finish(mut self) -> CommandRecording {
2160 if self.vessel.is_none() {
2161 self.shapes.tables.trim_growth();
2162 }
2163 CommandRecording {
2164 shapes: fill_vessel(self.vessel, self.shapes),
2165 content: self.content,
2166 fingerprint: OnceCell::new(),
2167 }
2168 }
2169
2170 pub fn len(&self) -> usize {
2172 self.shapes.tables.shapes.len()
2173 + self.content.others.len()
2174 + self.content.content_markers as usize
2175 }
2176
2177 pub fn is_empty(&self) -> bool {
2179 self.len() == 0
2180 }
2181
2182 pub fn content_markers(&self) -> u32 {
2184 self.content.content_markers
2185 }
2186
2187 pub fn clear(&mut self) {
2190 self.shapes.clear();
2191 self.content.others.clear();
2192 self.content.min = [f32::INFINITY; 2];
2193 self.content.max = [f32::NEG_INFINITY; 2];
2194 self.content.summary = RecordingSummary::default();
2195 self.content.content_markers = 0;
2196 }
2197
2198 pub fn reserve_shapes(&mut self, additional: usize) {
2200 self.shapes.tables_mut().shapes.reserve(additional);
2201 }
2202
2203 pub fn push_content(&mut self) {
2205 self.content.content_markers += 1;
2206 self.content.summary.has_content_markers = true;
2207 self.shapes.push_content_segment();
2208 }
2209
2210 pub fn push_primitive(&mut self, primitive: DrawPrimitive) {
2213 if matches!(primitive, DrawPrimitive::Content) {
2214 self.push_content();
2215 return;
2216 }
2217 match self.shapes.push_primitive(primitive) {
2218 Recorded::Shape(_) => self.note_shape(),
2219 Recorded::Other(other) => self.push_other(other),
2220 }
2221 }
2222
2223 #[inline]
2224 fn note_shape(&mut self) {
2225 self.content.summary.has_non_shadow = true;
2226 self.content.summary.has_shapes = true;
2227 }
2228
2229 fn include_bounds(&mut self, rect: Rect) {
2230 self.content.min[0] = self.content.min[0].min(rect.x);
2231 self.content.min[1] = self.content.min[1].min(rect.y);
2232 self.content.max[0] = self.content.max[0].max(rect.x + rect.width);
2233 self.content.max[1] = self.content.max[1].max(rect.y + rect.height);
2234 }
2235
2236 pub fn push_rect(
2238 &mut self,
2239 rect: Rect,
2240 brush: &Brush,
2241 stroke: Option<Stroke>,
2242 blend: BlendMode,
2243 ) {
2244 self.shapes.push_rect(rect, brush, stroke, blend);
2245 self.note_shape();
2246 }
2247
2248 pub fn push_round_rect(
2250 &mut self,
2251 rect: Rect,
2252 brush: &Brush,
2253 radii: CornerRadii,
2254 stroke: Option<Stroke>,
2255 blend: BlendMode,
2256 ) {
2257 self.shapes
2258 .push_round_rect(rect, brush, radii, stroke, blend);
2259 self.note_shape();
2260 }
2261
2262 #[inline]
2265 pub fn push_arc(&mut self, rect: Rect, args: &ArcRecordArgs<'_>) {
2266 self.shapes.push_arc(rect, args);
2267 self.note_shape();
2268 }
2269
2270 #[inline]
2274 pub fn push_scope_arc(&mut self, args: &ArcRecordArgs<'_>, geometry: &ArcGeometry) {
2275 self.shapes.push_scope_arc(args, geometry);
2276 self.note_shape();
2277 }
2278
2279 pub fn push_line(
2281 &mut self,
2282 line: &LineGeometry,
2283 brush: &Brush,
2284 stroke: Stroke,
2285 blend: BlendMode,
2286 ) {
2287 self.shapes.push_line(line, brush, stroke, blend);
2288 self.note_shape();
2289 }
2290
2291 pub fn push_trapezoids(
2294 &mut self,
2295 brush_rect: Rect,
2296 trapezoids: &[Trapezoid],
2297 brush: &Brush,
2298 blend: BlendMode,
2299 ) {
2300 if trapezoids.is_empty() {
2301 return;
2302 }
2303 self.shapes
2304 .push_trapezoids(brush_rect, trapezoids, brush, blend);
2305 self.note_shape();
2306 }
2307
2308 pub fn push_other(&mut self, primitive: DrawPrimitive) {
2310 self.content.summary.has_others = true;
2311 self.content.summary.note(&primitive);
2312 if let Some(rect) = primitive_coverage_rect(&primitive) {
2313 self.include_bounds(rect);
2314 }
2315 let index = self.content.others.len() as u32;
2316 self.content.others.push(primitive);
2317 self.shapes
2318 .extend_segment(RecordLane::Others, index, BlendMode::SrcOver, 0);
2319 }
2320
2321 pub fn merge_summary(&mut self, other: RecordingSummary) {
2324 self.content.summary.merge(other);
2325 }
2326}
2327
2328pub struct ArcRecordArgs<'a> {
2331 pub brush: &'a Brush,
2332 pub center: Point,
2333 pub radius: f32,
2334 pub start_angle: f32,
2335 pub sweep_angle: f32,
2336 pub stroke: Option<Stroke>,
2337 pub inner_radius: f32,
2338 pub blend_mode: BlendMode,
2339}
2340
2341fn stroked_circle_ring(
2344 rect: Rect,
2345 radii: CornerRadii,
2346 stroke: Option<Stroke>,
2347) -> Option<ArcGeometry> {
2348 const CIRCLE_TOLERANCE: f32 = 0.01;
2349 let stroke = stroke?;
2350 if rect.width.to_bits() != rect.height.to_bits() || !stroke.is_visible() {
2351 return None;
2352 }
2353 let half = rect.width * 0.5;
2354 let radius = radii.top_left;
2355 if !radius.is_finite()
2356 || radius <= 0.0
2357 || (radius - half).abs() > CIRCLE_TOLERANCE
2358 || radii.top_right.to_bits() != radius.to_bits()
2359 || radii.bottom_right.to_bits() != radius.to_bits()
2360 || radii.bottom_left.to_bits() != radius.to_bits()
2361 {
2362 return None;
2363 }
2364 let half_width = stroke.half_width();
2365 Some(ArcGeometry::new(
2366 Point::new(rect.x + half, rect.y + half),
2367 (half - half_width).max(0.0),
2368 half + half_width,
2369 0.0,
2370 TAU,
2371 StrokeCap::Round,
2372 ))
2373}
2374
2375fn union_rect(a: Rect, b: Rect) -> Rect {
2376 let x = a.x.min(b.x);
2377 let y = a.y.min(b.y);
2378 Rect {
2379 x,
2380 y,
2381 width: (a.x + a.width).max(b.x + b.width) - x,
2382 height: (a.y + a.height).max(b.y + b.height) - y,
2383 }
2384}
2385
2386fn rect_row(rect: Rect) -> [f32; 4] {
2387 [rect.x, rect.y, rect.width, rect.height]
2388}
2389
2390fn row_rect(row: [f32; 4]) -> Rect {
2391 Rect {
2392 x: row[0],
2393 y: row[1],
2394 width: row[2],
2395 height: row[3],
2396 }
2397}
2398
2399#[inline(always)]
2402pub fn normalized_band(args: &ArcRecordArgs<'_>) -> ArcGeometry {
2403 ArcGeometry::of_band(
2404 args.center,
2405 arc_band(args.radius, args.inner_radius, args.stroke),
2406 args.start_angle,
2407 args.sweep_angle,
2408 )
2409}
2410
2411fn band_disc(geometry: &ArcGeometry) -> Rect {
2414 let reach = geometry.outer_radius + geometry.half_thickness();
2415 Rect {
2416 x: geometry.center.x - reach,
2417 y: geometry.center.y - reach,
2418 width: reach + reach,
2419 height: reach + reach,
2420 }
2421}
2422
2423pub fn arc_trig(start_angle: f32, sweep_angle: f32) -> [f32; 4] {
2428 if sweep_angle >= TAU && start_angle == 0.0 {
2429 return [0.0, -1.0, 0.0, -1.0];
2430 }
2431 let half_sweep = within(sweep_angle, 0.0, TAU) * 0.5;
2432 let (mid_sin, mid_cos) = (start_angle + half_sweep).sin_cos();
2433 let (half_sin, half_cos) = half_sweep.sin_cos();
2434 [mid_sin, mid_cos, at_least(half_sin, 0.0), half_cos]
2435}
2436
2437#[cfg(test)]
2438#[path = "tests/record_tests.rs"]
2439mod tests;
2440
2441#[cfg(test)]
2442#[path = "tests/record_band_tests.rs"]
2443mod band_tests;