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 with_capacity_of(&self) -> Self {
745 Self {
746 shapes: ShapeRecords::with_capacity(self.shapes.capacity()),
747 brushes: Vec::with_capacity(self.brushes.capacity()),
748 stops: Vec::with_capacity(self.stops.capacity()),
749 explicit_stops: Vec::with_capacity(self.explicit_stops.capacity()),
750 segments: Vec::with_capacity(self.segments.capacity()),
751 fingerprint: OnceLock::new(),
752 }
753 }
754
755 pub fn fingerprint(&self) -> u64 {
758 *self.fingerprint.get_or_init(|| {
759 let mut hasher = FxHasher::default();
760 hasher.write(bytemuck::cast_slice(self.shapes.bodies()));
761 hasher.write(bytemuck::cast_slice(self.shapes.curves()));
762 hasher.write(self.shapes.source_bytes());
763 hasher.write(bytemuck::cast_slice(&self.brushes));
764 hasher.write(bytemuck::cast_slice(&self.stops));
765 hasher.write(bytemuck::cast_slice(&self.explicit_stops));
766 self.segments.hash(&mut hasher);
767 hasher.finish()
768 })
769 }
770
771 pub fn heap_bytes(&self) -> usize {
773 self.shapes.heap_bytes()
774 + self.brushes.capacity() * std::mem::size_of::<BrushRecord>()
775 + self.stops.capacity() * std::mem::size_of::<GradientStopRecord>()
776 + self.explicit_stops.capacity() * std::mem::size_of::<f32>()
777 + self.segments.capacity() * std::mem::size_of::<RecordSegment>()
778 }
779}
780
781#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
784pub struct RecordingSummary {
785 pub has_text: bool,
787 pub has_shadow: bool,
789 pub has_non_shadow: bool,
791 pub has_pixel_sensitive: bool,
794 pub has_shapes: bool,
796 pub has_others: bool,
798 pub has_content_markers: bool,
800}
801
802impl RecordingSummary {
803 fn note(&mut self, primitive: &DrawPrimitive) {
804 let primitive = unwrap_blend(primitive);
805 if matches!(primitive, DrawPrimitive::Shadow(_)) {
806 self.has_shadow = true;
807 return;
808 }
809 if matches!(primitive, DrawPrimitive::Content) {
810 return;
811 }
812 self.has_non_shadow = true;
813 match primitive {
814 DrawPrimitive::Text(_) => {
815 self.has_text = true;
816 self.has_pixel_sensitive = true;
817 }
818 DrawPrimitive::Image { .. } => self.has_pixel_sensitive = true,
819 _ => {}
820 }
821 }
822
823 fn merge(&mut self, other: Self) {
824 self.has_text |= other.has_text;
825 self.has_shadow |= other.has_shadow;
826 self.has_non_shadow |= other.has_non_shadow;
827 self.has_pixel_sensitive |= other.has_pixel_sensitive;
828 self.has_shapes |= other.has_shapes;
829 self.has_others |= other.has_others;
830 self.has_content_markers |= other.has_content_markers;
831 }
832
833 pub fn shapes_only(&self) -> bool {
836 self.has_shapes && !self.has_others && !self.has_content_markers
837 }
838}
839
840fn unwrap_blend(mut primitive: &DrawPrimitive) -> &DrawPrimitive {
841 while let DrawPrimitive::Blend {
842 primitive: inner, ..
843 } = primitive
844 {
845 primitive = inner;
846 }
847 primitive
848}
849
850pub fn expand_rect(rect: Rect, margin: f32) -> Rect {
852 Rect {
853 x: rect.x - margin,
854 y: rect.y - margin,
855 width: rect.width + margin * 2.0,
856 height: rect.height + margin * 2.0,
857 }
858}
859
860pub fn primitive_coverage_rect(primitive: &DrawPrimitive) -> Option<Rect> {
863 match primitive {
864 DrawPrimitive::Blend { primitive, .. } => primitive_coverage_rect(primitive),
865 DrawPrimitive::Rect { rect, stroke, .. }
866 | DrawPrimitive::RoundRect { rect, stroke, .. }
867 | DrawPrimitive::Arc { rect, stroke, .. } => {
868 let half_stroke = stroke.as_ref().map_or(0.0, |stroke| stroke.width * 0.5);
869 Some(expand_rect(*rect, half_stroke))
870 }
871 DrawPrimitive::Line {
872 start, end, stroke, ..
873 } => Some(LineGeometry::new(*start, *end, *stroke).bounds()),
874 DrawPrimitive::Trapezoid { trapezoid, .. } => Some(trapezoid.bounds()),
875 DrawPrimitive::Image { rect, .. } => Some(*rect),
876 DrawPrimitive::Text(text) => Some(text.rect),
877 DrawPrimitive::Content | DrawPrimitive::Shadow(_) => None,
878 }
879}
880
881#[derive(Clone, Debug)]
886pub struct ShapeRecorder {
887 tables: RecordTables,
888 last_segment_key: u32,
889 segment_waste: u32,
890 min: [f32; 2],
891 max: [f32; 2],
892}
893
894impl Default for ShapeRecorder {
895 fn default() -> Self {
896 Self {
897 tables: RecordTables::default(),
898 last_segment_key: NO_SEGMENT_KEY,
899 segment_waste: 0,
900 min: [f32::INFINITY; 2],
901 max: [f32::NEG_INFINITY; 2],
902 }
903 }
904}
905
906impl PartialEq for ShapeRecorder {
907 fn eq(&self, other: &Self) -> bool {
908 self.tables == other.tables
909 }
910}
911
912pub enum Recorded {
914 Shape(Rect),
916 Other(DrawPrimitive),
918}
919
920#[inline]
921fn extend_segment_in(tables: &mut RecordTables, extend: bool, opened: RecordSegment) {
922 if extend {
923 let last = tables.segments.last_mut().expect("a keyed segment exists");
924 last.count += 1;
925 last.brushes |= opened.brushes;
926 last.kinds |= opened.kinds;
927 last.band_class = last.band_class.max(opened.band_class);
928 last.interiors |= opened.interiors;
929 last.occluders |= opened.occluders;
930 last.bare_interiors |= opened.bare_interiors;
931 return;
932 }
933 tables.segments.push(opened);
934}
935
936impl ShapeRecorder {
937 pub fn tables(&self) -> &RecordTables {
939 &self.tables
940 }
941
942 fn tables_mut(&mut self) -> &mut RecordTables {
943 let tables = &mut self.tables;
944 tables.fingerprint.take();
945 tables
946 }
947
948 pub fn is_empty(&self) -> bool {
949 self.tables.shapes.is_empty()
950 }
951
952 pub fn all_segments(&self) -> Range<u32> {
954 0..self.tables.segments.len() as u32
955 }
956
957 pub fn bounds(&self) -> Option<Rect> {
960 (self.min[0] <= self.max[0] && self.min[1] <= self.max[1]).then(|| Rect {
961 x: self.min[0],
962 y: self.min[1],
963 width: self.max[0] - self.min[0],
964 height: self.max[1] - self.min[1],
965 })
966 }
967
968 pub fn fingerprint(&self) -> u64 {
970 self.tables.fingerprint()
971 }
972
973 pub fn clear(&mut self) {
975 self.tables.clear();
976 self.last_segment_key = NO_SEGMENT_KEY;
977 self.segment_waste = 0;
978 self.min = [f32::INFINITY; 2];
979 self.max = [f32::NEG_INFINITY; 2];
980 }
981
982 pub fn push_primitive(&mut self, primitive: DrawPrimitive) -> Recorded {
985 match primitive {
986 DrawPrimitive::Blend {
987 primitive,
988 blend_mode,
989 } => match self.push_shape_primitive(*primitive, blend_mode) {
990 Recorded::Other(inner) => Recorded::Other(DrawPrimitive::Blend {
991 primitive: Box::new(inner),
992 blend_mode,
993 }),
994 recorded => recorded,
995 },
996 other => self.push_shape_primitive(other, BlendMode::SrcOver),
997 }
998 }
999
1000 pub fn push_shape_primitive(
1003 &mut self,
1004 primitive: DrawPrimitive,
1005 blend_mode: BlendMode,
1006 ) -> Recorded {
1007 Recorded::Shape(match primitive {
1008 DrawPrimitive::Rect {
1009 rect,
1010 brush,
1011 stroke,
1012 } => self.push_rect(rect, &brush, stroke, blend_mode),
1013 DrawPrimitive::RoundRect {
1014 rect,
1015 brush,
1016 radii,
1017 stroke,
1018 } => self.push_round_rect(rect, &brush, radii, stroke, blend_mode),
1019 DrawPrimitive::Arc {
1020 rect,
1021 brush,
1022 center,
1023 radius,
1024 start_angle,
1025 sweep_angle,
1026 stroke,
1027 inner_radius,
1028 } => self.push_arc(
1029 rect,
1030 &ArcRecordArgs {
1031 brush: &brush,
1032 center,
1033 radius,
1034 start_angle,
1035 sweep_angle,
1036 stroke,
1037 inner_radius,
1038 blend_mode,
1039 },
1040 ),
1041 DrawPrimitive::Line {
1042 brush,
1043 start,
1044 end,
1045 stroke,
1046 ..
1047 } => self.push_line(
1048 &LineGeometry::new(start, end, stroke),
1049 &brush,
1050 stroke,
1051 blend_mode,
1052 ),
1053 DrawPrimitive::Trapezoid {
1054 rect,
1055 brush,
1056 trapezoid,
1057 } => self.push_trapezoids(rect, &[trapezoid], &brush, blend_mode),
1058 other => return Recorded::Other(other),
1059 })
1060 }
1061
1062 fn push_content_segment(&mut self) {
1063 self.last_segment_key = NO_SEGMENT_KEY;
1064 self.tables_mut().segments.push(RecordSegment {
1065 lane: RecordLane::Content,
1066 start: 0,
1067 count: 1,
1068 blend: BlendMode::SrcOver,
1069 gradient: false,
1070 vertex_gradient: false,
1071 brushes: 0,
1072 kinds: 0,
1073 band_class: 0,
1074 interiors: false,
1075 occluders: false,
1076 bare_interiors: false,
1077 });
1078 }
1079
1080 pub fn push_line(
1086 &mut self,
1087 line: &LineGeometry,
1088 brush: &Brush,
1089 stroke: Stroke,
1090 blend: BlendMode,
1091 ) -> Rect {
1092 let (handle, color) = self.intern_brush(brush);
1093 let mut flags = pack_flags(RECORD_KIND_LINE, Some(stroke), blend, StrokeCap::Butt);
1094 if line.is_degenerate() {
1095 flags |= ARC_DEGENERATE_BIT;
1096 }
1097 let frame = line.frame();
1098 let ends = [line.start.x, line.start.y, line.end.x, line.end.y];
1099 let bounds = line.bounds();
1102 self.include_bounds(bounds);
1103 self.push_shape(
1104 ShapeRecordBody {
1105 rect: rect_row(line.end_bounds()),
1106 color,
1107 stroke_width: stroke.width,
1108 flags,
1109 brush: handle,
1110 placement: 0,
1111 arc_geometry: ends,
1112 },
1113 ShapeRecordCurve {
1114 radii: [0.0; 4],
1115 arc_normalized: [frame.direction.x, frame.direction.y, frame.half_length, 0.0],
1116 },
1117 ends,
1118 blend,
1119 None,
1120 );
1121 bounds
1122 }
1123
1124 pub fn push_trapezoids(
1131 &mut self,
1132 brush_rect: Rect,
1133 trapezoids: &[Trapezoid],
1134 brush: &Brush,
1135 blend: BlendMode,
1136 ) -> Rect {
1137 let (handle, color) = self.intern_brush(brush);
1138 let flags = pack_flags(RECORD_KIND_LINE, None, blend, StrokeCap::Butt)
1139 | (TRAPEZOID_CAP << CAP_SHIFT);
1140 let radii = rect_row(brush_rect);
1141 let shading = self.brush_shading(
1144 &ShapeRecordBody {
1145 brush: handle,
1146 flags,
1147 ..ShapeRecordBody::zeroed()
1148 },
1149 &ShapeRecordCurve {
1150 radii,
1151 arc_normalized: [0.0; 4],
1152 },
1153 );
1154 let mut reach: Option<Rect> = None;
1155 for trapezoid in trapezoids {
1156 let bounds = trapezoid.bounds();
1157 let open = (u32::from(trapezoid.open_left) * OPEN_LEFT_BIT)
1158 | (u32::from(trapezoid.open_right) * OPEN_RIGHT_BIT);
1159 self.push_shaded(
1160 ShapeRecordBody {
1161 rect: rect_row(bounds),
1162 color,
1163 stroke_width: 0.0,
1164 flags: flags | open,
1165 brush: handle,
1166 placement: 0,
1167 arc_geometry: [
1168 trapezoid.top[0],
1169 trapezoid.top[1],
1170 trapezoid.bottom[0],
1171 trapezoid.bottom[1],
1172 ],
1173 },
1174 ShapeRecordCurve {
1175 radii,
1176 arc_normalized: [
1177 trapezoid.left,
1178 trapezoid.right,
1179 trapezoid.right - trapezoid.left,
1180 0.0,
1181 ],
1182 },
1183 [0.0; 4],
1184 (blend, shading),
1185 None,
1186 );
1187 reach = Some(reach.map_or(bounds, |reach| reach.union(bounds)));
1188 }
1189 reach.unwrap_or(Rect::EMPTY)
1190 }
1191
1192 #[inline]
1193 pub fn push_rect(
1194 &mut self,
1195 rect: Rect,
1196 brush: &Brush,
1197 stroke: Option<Stroke>,
1198 blend: BlendMode,
1199 ) -> Rect {
1200 let (handle, color) = self.intern_brush(brush);
1201 self.push_shape(
1202 ShapeRecordBody {
1203 rect: rect_row(rect),
1204 color,
1205 stroke_width: stroke.map_or(0.0, |stroke| stroke.width),
1206 flags: pack_flags(RECORD_KIND_RECT, stroke, blend, StrokeCap::Butt),
1207 brush: handle,
1208 placement: 0,
1209 arc_geometry: [0.0; 4],
1210 },
1211 ShapeRecordCurve {
1212 radii: [0.0; 4],
1213 arc_normalized: [0.0; 4],
1214 },
1215 [0.0; 4],
1216 blend,
1217 None,
1218 )
1219 }
1220
1221 pub fn push_round_rect(
1222 &mut self,
1223 rect: Rect,
1224 brush: &Brush,
1225 radii: CornerRadii,
1226 stroke: Option<Stroke>,
1227 blend: BlendMode,
1228 ) -> Rect {
1229 let (handle, color) = self.intern_brush(brush);
1230 let mut flags = pack_flags(RECORD_KIND_ROUND_RECT, stroke, blend, StrokeCap::Butt);
1231 let mut arc_geometry = [0.0; 4];
1232 let mut source = [0.0; 4];
1233 let mut arc_normalized = [0.0; 4];
1234 let mut bucket = None;
1235 if let Some(ring) = stroked_circle_ring(rect, radii, stroke)
1236 && let band = BandRing::of_geometry(&ring)
1237 && let Some(ring_bucket) = band_bucket_for(
1238 &ring,
1239 ring.is_degenerate(),
1240 &band,
1241 expand_rect(rect, ring.half_thickness()),
1242 )
1243 {
1244 flags |= ARC_BANDED_BIT;
1245 arc_geometry = [
1246 ring.center.x,
1247 ring.center.y,
1248 ring.inner_radius,
1249 ring.outer_radius,
1250 ];
1251 source = [ring.mid_radius(), ring.inner_radius, 0.0, TAU];
1252 arc_normalized = [0.0, TAU, 0.0, 0.0];
1253 bucket = Some(ring_bucket);
1254 }
1255 self.push_shape(
1256 ShapeRecordBody {
1257 rect: rect_row(rect),
1258 color,
1259 stroke_width: stroke.map_or(0.0, |stroke| stroke.width),
1260 flags,
1261 brush: handle,
1262 placement: 0,
1263 arc_geometry,
1264 },
1265 ShapeRecordCurve {
1266 radii: [
1267 radii.top_left,
1268 radii.top_right,
1269 radii.bottom_right,
1270 radii.bottom_left,
1271 ],
1272 arc_normalized,
1273 },
1274 source,
1275 blend,
1276 bucket,
1277 )
1278 }
1279
1280 pub fn push_arc(&mut self, rect: Rect, args: &ArcRecordArgs<'_>) -> Rect {
1283 let geometry = normalized_band(args);
1284 self.push_arc_band(args, &geometry, Some(rect), geometry.is_degenerate())
1285 }
1286
1287 #[inline]
1294 pub fn push_scope_arc(&mut self, args: &ArcRecordArgs<'_>, geometry: &ArcGeometry) -> Rect {
1295 debug_assert!(
1296 !geometry.is_degenerate(),
1297 "the scope skips degenerate bands"
1298 );
1299 self.push_arc_band(args, geometry, None, false)
1300 }
1301
1302 #[inline]
1303 fn push_arc_band(
1304 &mut self,
1305 args: &ArcRecordArgs<'_>,
1306 geometry: &ArcGeometry,
1307 rect: Option<Rect>,
1308 degenerate: bool,
1309 ) -> Rect {
1310 let (handle, color) = self.intern_brush(args.brush);
1311 let mut flags = pack_flags(RECORD_KIND_ARC, args.stroke, args.blend_mode, geometry.cap);
1312 if degenerate {
1313 flags |= ARC_DEGENERATE_BIT;
1314 }
1315 let rect = rect.unwrap_or_else(|| {
1316 flags |= ARC_RECT_LOOSE_BIT;
1317 band_disc(geometry)
1318 });
1319 let ring = BandRing::of_geometry(geometry);
1320 let bucket = band_bucket_for(geometry, degenerate, &ring, rect);
1321 if bucket.is_some() {
1322 flags |= ARC_BANDED_BIT;
1323 }
1324 self.push_shape(
1325 ShapeRecordBody {
1326 rect: rect_row(rect),
1327 color,
1328 stroke_width: args.stroke.map_or(0.0, |stroke| stroke.width),
1329 flags,
1330 brush: handle,
1331 placement: 0,
1332 arc_geometry: [
1333 args.center.x,
1334 args.center.y,
1335 geometry.inner_radius,
1336 geometry.outer_radius,
1337 ],
1338 },
1339 ShapeRecordCurve {
1340 radii: [0.0; 4],
1341 arc_normalized: [geometry.start_angle, geometry.sweep_angle, 0.0, 0.0],
1342 },
1343 [
1344 args.radius,
1345 args.inner_radius,
1346 args.start_angle,
1347 args.sweep_angle,
1348 ],
1349 args.blend_mode,
1350 bucket,
1351 )
1352 }
1353
1354 #[inline(always)]
1355 fn push_shape(
1356 &mut self,
1357 body: ShapeRecordBody,
1358 curve: ShapeRecordCurve,
1359 source: [f32; 4],
1360 blend: BlendMode,
1361 band_bucket: Option<usize>,
1362 ) -> Rect {
1363 let shading = self.brush_shading(&body, &curve);
1364 self.push_shaded(body, curve, source, (blend, shading), band_bucket)
1365 }
1366
1367 fn push_shaded(
1369 &mut self,
1370 mut body: ShapeRecordBody,
1371 curve: ShapeRecordCurve,
1372 source: [f32; 4],
1373 (blend, shading): (BlendMode, BrushShading),
1374 band_bucket: Option<usize>,
1375 ) -> Rect {
1376 let half_stroke = if body.flags & STROKED_BIT != 0 {
1377 body.stroke_width * 0.5
1378 } else {
1379 0.0
1380 };
1381 let coverage = expand_rect(row_rect(body.rect), half_stroke);
1382 self.include_bounds(coverage);
1383 let index = self.tables.shapes.len() as u32;
1384 body.flags |= shading.flag();
1385 let brush_bit = 1u8
1386 << match body.brush {
1387 0 => 0,
1388 index => self.tables.brushes[index as usize - 1].kind,
1389 };
1390 let kind_bit = 1u8 << fragment_kind(body.flags);
1391 let band_class = band_bucket.unwrap_or(0) as u8;
1392 body.flags |= u32::from(band_class) << BAND_CLASS_SHIFT;
1393 let extend =
1394 self.note_segment_key(RecordLane::Shapes, blend, shading, is_trapezoid(body.flags))
1395 && self.segment_takes_class(band_class);
1396 if !extend {
1397 self.segment_waste = 0;
1398 }
1399 let joined = extend.then(|| self.tables.segments.last()).flatten();
1404 let translucent = body.brush != 0 || body.color[3] < 1.0;
1405 let interiors = !joined
1406 .is_some_and(|segment| segment.interiors && (segment.bare_interiors || !translucent))
1407 && interior_repays(&body, &curve);
1408 let occluders =
1409 !joined.is_some_and(|segment| segment.occluders) && interior_occludes(&body, &curve);
1410 let bare_interiors = interiors && translucent;
1411 let tables = self.tables_mut();
1412 tables.shapes.push(body, curve, source);
1413 extend_segment_in(
1414 tables,
1415 extend,
1416 RecordSegment {
1417 lane: RecordLane::Shapes,
1418 start: index,
1419 count: 1,
1420 blend,
1421 gradient: shading == BrushShading::Fragment,
1422 vertex_gradient: shading == BrushShading::Vertex,
1423 brushes: brush_bit,
1424 kinds: kind_bit,
1425 band_class,
1426 interiors,
1427 occluders,
1428 bare_interiors,
1429 },
1430 );
1431 coverage
1432 }
1433
1434 fn extend_segment(&mut self, lane: RecordLane, index: u32, blend: BlendMode, kind_bit: u8) {
1435 let extend = self.note_segment_key(lane, blend, BrushShading::Solid, false);
1436 if !extend {
1437 self.segment_waste = 0;
1438 }
1439 extend_segment_in(
1440 self.tables_mut(),
1441 extend,
1442 RecordSegment {
1443 lane,
1444 start: index,
1445 count: 1,
1446 blend,
1447 gradient: false,
1448 vertex_gradient: false,
1449 brushes: 0,
1450 kinds: kind_bit,
1451 band_class: 0,
1452 interiors: false,
1453 occluders: false,
1454 bare_interiors: false,
1455 },
1456 );
1457 }
1458
1459 #[inline]
1464 fn segment_takes_class(&mut self, band_class: u8) -> bool {
1465 let last = self.tables.segments.last().expect("a keyed segment exists");
1466 let held = ARC_BUCKET_SEGMENTS[last.band_class as usize];
1467 let wanted = ARC_BUCKET_SEGMENTS[band_class as usize];
1468 let waste = if wanted > held {
1469 last.count * (wanted - held)
1470 } else {
1471 held - wanted
1472 };
1473 if self.segment_waste + waste > SEGMENT_WASTE_QUADS {
1474 return false;
1475 }
1476 self.segment_waste += waste;
1477 true
1478 }
1479
1480 #[inline]
1485 fn note_segment_key(
1486 &mut self,
1487 lane: RecordLane,
1488 blend: BlendMode,
1489 shading: BrushShading,
1490 slice: bool,
1491 ) -> bool {
1492 let key = ((lane as u32) << 16)
1493 | (u32::from(slice) << 15)
1494 | ((blend as u32) << 2)
1495 | shading as u32;
1496 let extend = key == self.last_segment_key;
1497 self.last_segment_key = key;
1498 extend
1499 }
1500
1501 #[inline]
1504 fn include_bounds(&mut self, rect: Rect) {
1505 let right = rect.x + rect.width;
1506 let bottom = rect.y + rect.height;
1507 if rect.x < self.min[0] {
1508 self.min[0] = rect.x;
1509 }
1510 if rect.y < self.min[1] {
1511 self.min[1] = rect.y;
1512 }
1513 if right > self.max[0] {
1514 self.max[0] = right;
1515 }
1516 if bottom > self.max[1] {
1517 self.max[1] = bottom;
1518 }
1519 }
1520
1521 #[inline]
1522 fn brush_shading(&self, body: &ShapeRecordBody, curve: &ShapeRecordCurve) -> BrushShading {
1528 if body.brush == 0 {
1529 return BrushShading::Solid;
1530 }
1531 let tables = &self.tables;
1532 let Some(brush) = tables.brushes.get(body.brush as usize - 1) else {
1533 return BrushShading::Fragment;
1534 };
1535 let vertex = if is_trapezoid(body.flags) {
1536 vertex_gradient::clamps_between_two_stops(brush, &tables.stops, curve.radii)
1537 } else {
1538 body.flags & RECT_FILL_MASK == 0
1539 && vertex_gradient::spans_quad(brush, &tables.stops, body.rect)
1540 };
1541 if vertex {
1542 BrushShading::Vertex
1543 } else {
1544 BrushShading::Fragment
1545 }
1546 }
1547
1548 #[inline]
1552 fn intern_brush(&mut self, brush: &Brush) -> (u32, [f32; 4]) {
1553 match brush {
1554 Brush::Solid(color) => (0, [color.0, color.1, color.2, color.3]),
1555 gradient => self.intern_gradient(gradient),
1556 }
1557 }
1558
1559 #[cold]
1560 #[inline(never)]
1561 fn intern_gradient(&mut self, brush: &Brush) -> (u32, [f32; 4]) {
1562 let (kind, tile_mode, params, colors, stops) = match brush {
1563 Brush::Solid(color) => return (0, [color.0, color.1, color.2, color.3]),
1564 Brush::LinearGradient {
1565 colors,
1566 stops,
1567 start,
1568 end,
1569 tile_mode,
1570 } => (
1571 BRUSH_KIND_LINEAR,
1572 *tile_mode,
1573 [start.x, start.y, end.x, end.y],
1574 colors,
1575 stops,
1576 ),
1577 Brush::RadialGradient {
1578 colors,
1579 stops,
1580 center,
1581 radius,
1582 tile_mode,
1583 } => (
1584 BRUSH_KIND_RADIAL,
1585 *tile_mode,
1586 [center.x, center.y, *radius, 0.0],
1587 colors,
1588 stops,
1589 ),
1590 Brush::SweepGradient {
1591 colors,
1592 stops,
1593 center,
1594 } => (
1595 BRUSH_KIND_SWEEP,
1596 TileMode::Clamp,
1597 [center.x, center.y, 0.0, 0.0],
1598 colors,
1599 stops,
1600 ),
1601 };
1602 let tables = self.tables_mut();
1603 let stop_start = tables.stops.len() as u32;
1604 let count = colors.len();
1605 let positions = stops.as_deref().filter(|values| values.len() == count);
1606 for (index, color) in colors.iter().enumerate() {
1607 let position = positions.map_or_else(
1608 || {
1609 if count <= 1 {
1610 0.0
1611 } else {
1612 index as f32 / (count - 1) as f32
1613 }
1614 },
1615 |values| values[index],
1616 );
1617 tables.stops.push(GradientStopRecord {
1618 color: [color.0, color.1, color.2, color.3],
1619 position: [position, 0.0, 0.0, 0.0],
1620 });
1621 }
1622 let (explicit_start, explicit_len) = match stops {
1623 Some(values) => {
1624 let start = tables.explicit_stops.len() as u32;
1625 tables.explicit_stops.extend_from_slice(values);
1626 (start, values.len() as u32)
1627 }
1628 None => (0, NO_EXPLICIT_STOPS),
1629 };
1630 let record = BrushRecord {
1631 kind,
1632 tile_mode: tile_mode as u32,
1633 stop_start,
1634 stop_count: count as u32,
1635 params,
1636 explicit_start,
1637 explicit_len,
1638 reserved: [0; 2],
1639 };
1640 tables.brushes.push(record);
1641 let handle = tables.brushes.len() as u32;
1642 let first = colors.first().copied().unwrap_or(Color(0.0, 0.0, 0.0, 0.0));
1643 (handle, [first.0, first.1, first.2, first.3])
1644 }
1645}
1646
1647#[derive(Clone, Debug)]
1652pub struct CommandRecording {
1653 shapes: Arc<ShapeRecorder>,
1654 content: RecordingContent,
1655 fingerprint: OnceCell<u64>,
1656}
1657
1658#[derive(Clone, Debug)]
1659struct RecordingContent {
1660 others: Vec<DrawPrimitive>,
1661 min: [f32; 2],
1662 max: [f32; 2],
1663 summary: RecordingSummary,
1664 content_markers: u32,
1665}
1666
1667impl Default for RecordingContent {
1668 fn default() -> Self {
1669 Self {
1670 others: Vec::new(),
1671 min: [f32::INFINITY; 2],
1672 max: [f32::NEG_INFINITY; 2],
1673 summary: RecordingSummary::default(),
1674 content_markers: 0,
1675 }
1676 }
1677}
1678
1679thread_local! {
1680 static NO_SHAPES: Arc<ShapeRecorder> = Arc::new(ShapeRecorder::default());
1683}
1684
1685impl Default for CommandRecording {
1686 fn default() -> Self {
1687 Self {
1688 shapes: NO_SHAPES.with(Arc::clone),
1689 content: RecordingContent::default(),
1690 fingerprint: OnceCell::new(),
1691 }
1692 }
1693}
1694
1695impl PartialEq for CommandRecording {
1696 fn eq(&self, other: &Self) -> bool {
1697 self.shapes == other.shapes
1698 && self.content.others == other.content.others
1699 && self.content.content_markers == other.content.content_markers
1700 }
1701}
1702
1703impl CommandRecording {
1704 pub fn try_take_reusable(&mut self) -> Option<Self> {
1734 Arc::get_mut(&mut self.shapes)?;
1735 Some(std::mem::take(self))
1736 }
1737
1738 pub fn into_recorder(self) -> CommandRecorder {
1741 let (shapes, vessel) = take_shapes(self.shapes, ShapeRecorder::clone);
1742 CommandRecorder {
1743 shapes,
1744 content: self.content,
1745 vessel,
1746 }
1747 }
1748
1749 pub fn from_primitives(primitives: impl IntoIterator<Item = DrawPrimitive>) -> Self {
1750 CommandRecorder::from_primitives(primitives).finish()
1751 }
1752
1753 pub fn shape_capacity(&self) -> usize {
1754 self.shapes.tables.shapes.capacity()
1755 }
1756
1757 pub fn pod_heap_bytes(&self) -> usize {
1759 self.shapes.tables.heap_bytes()
1760 }
1761
1762 pub fn tables(&self) -> &RecordTables {
1764 self.shapes.tables()
1765 }
1766
1767 pub fn shape_recorder(&self) -> &Arc<ShapeRecorder> {
1769 &self.shapes
1770 }
1771
1772 pub fn len_in(&self, segments: &Range<u32>) -> usize {
1774 self.segments_in(segments)
1775 .filter(|segment| segment.lane != RecordLane::Content)
1776 .map(|segment| segment.count as usize)
1777 .sum()
1778 }
1779
1780 pub fn shapes(&self) -> &ShapeRecords {
1782 &self.shapes.tables.shapes
1783 }
1784
1785 pub fn brushes(&self) -> &[BrushRecord] {
1786 &self.shapes.tables.brushes
1787 }
1788
1789 pub fn stops(&self) -> &[GradientStopRecord] {
1790 &self.shapes.tables.stops
1791 }
1792
1793 pub fn others(&self) -> &[DrawPrimitive] {
1794 &self.content.others
1795 }
1796
1797 pub fn segments(&self) -> &[RecordSegment] {
1798 &self.shapes.tables.segments
1799 }
1800
1801 pub fn all_segments(&self) -> Range<u32> {
1803 0..self.shapes.tables.segments.len() as u32
1804 }
1805
1806 pub fn bounds(&self) -> Option<Rect> {
1809 let others = (self.content.min[0] <= self.content.max[0]
1810 && self.content.min[1] <= self.content.max[1])
1811 .then(|| Rect {
1812 x: self.content.min[0],
1813 y: self.content.min[1],
1814 width: self.content.max[0] - self.content.min[0],
1815 height: self.content.max[1] - self.content.min[1],
1816 });
1817 match (self.shapes.bounds(), others) {
1818 (Some(shapes), Some(others)) => Some(union_rect(shapes, others)),
1819 (shapes, others) => shapes.or(others),
1820 }
1821 }
1822
1823 pub fn summary(&self) -> RecordingSummary {
1824 self.content.summary
1825 }
1826
1827 pub fn content_markers(&self) -> u32 {
1828 self.content.content_markers
1829 }
1830
1831 pub fn len(&self) -> usize {
1833 self.shapes.tables.shapes.len()
1834 + self.content.others.len()
1835 + self.content.content_markers as usize
1836 }
1837
1838 pub fn is_empty(&self) -> bool {
1839 self.len() == 0
1840 }
1841
1842 pub fn fingerprint(&self) -> u64 {
1847 *self.fingerprint.get_or_init(|| {
1848 let mut hasher = FxHasher::default();
1849 hasher.write_u64(self.shapes.fingerprint());
1850 for primitive in &self.content.others {
1851 hasher.write_u64(primitive.render_hash());
1852 }
1853 hasher.write_u32(self.content.content_markers);
1854 hasher.finish()
1855 })
1856 }
1857
1858 pub fn is_empty_in(&self, segments: &Range<u32>) -> bool {
1860 !self
1861 .segments_in(segments)
1862 .any(|segment| segment.lane != RecordLane::Content && segment.count > 0)
1863 }
1864
1865 pub fn segments_in(&self, segments: &Range<u32>) -> std::slice::Iter<'_, RecordSegment> {
1866 self.shapes.tables.segments[segments.start as usize..segments.end as usize].iter()
1867 }
1868
1869 pub fn summary_in(&self, segments: &Range<u32>) -> RecordingSummary {
1871 if *segments == self.all_segments() {
1872 return self.content.summary;
1873 }
1874 let mut summary = RecordingSummary::default();
1875 for segment in self.segments_in(segments) {
1876 match segment.lane {
1877 RecordLane::Shapes if segment.count > 0 => {
1878 summary.has_non_shadow = true;
1879 summary.has_shapes = true;
1880 }
1881 RecordLane::Others => {
1882 summary.has_others = true;
1883 for primitive in &self.content.others[segment.range()] {
1884 summary.note(primitive);
1885 }
1886 }
1887 RecordLane::Content => summary.has_content_markers = true,
1888 RecordLane::Shapes => {}
1889 }
1890 }
1891 summary
1892 }
1893
1894 pub fn content_split(&self, behind: bool) -> Range<u32> {
1897 let last_marker = self
1898 .shapes
1899 .tables
1900 .segments
1901 .iter()
1902 .rposition(|segment| segment.lane == RecordLane::Content);
1903 match (last_marker, behind) {
1904 (Some(index), true) => 0..index as u32,
1905 (Some(index), false) => index as u32 + 1..self.shapes.tables.segments.len() as u32,
1906 (None, true) => 0..0,
1907 (None, false) => self.all_segments(),
1908 }
1909 }
1910
1911 pub fn coverage_rects(&self, segments: Range<u32>) -> impl Iterator<Item = Rect> + '_ {
1913 self.segments_in(&segments).flat_map(move |segment| {
1914 segment.range().filter_map(move |index| match segment.lane {
1915 RecordLane::Shapes => self
1916 .shapes
1917 .tables
1918 .shapes
1919 .get(index)
1920 .map(|record| record.coverage_rect()),
1921 RecordLane::Others => primitive_coverage_rect(&self.content.others[index]),
1922 RecordLane::Content => None,
1923 })
1924 })
1925 }
1926
1927 pub fn primitives(&self, segments: Range<u32>) -> impl Iterator<Item = DrawPrimitive> + '_ {
1930 self.segments_in(&segments)
1931 .flat_map(|segment| self.segment_primitives(segment, false))
1932 }
1933
1934 pub fn primitives_with_markers(&self) -> impl Iterator<Item = DrawPrimitive> + '_ {
1936 self.shapes
1937 .tables
1938 .segments
1939 .iter()
1940 .flat_map(|segment| self.segment_primitives(segment, true))
1941 }
1942
1943 pub fn into_primitives_with_markers(self) -> Vec<DrawPrimitive> {
1944 self.primitives_with_markers().collect()
1945 }
1946
1947 fn segment_primitives<'a>(
1948 &'a self,
1949 segment: &RecordSegment,
1950 markers: bool,
1951 ) -> impl Iterator<Item = DrawPrimitive> + use<'a> {
1952 let lane = segment.lane;
1953 segment.range().filter_map(move |index| match lane {
1954 RecordLane::Shapes => Some(self.materialize_shape(index)),
1955 RecordLane::Others => Some(self.content.others[index].clone()),
1956 RecordLane::Content => markers.then_some(DrawPrimitive::Content),
1957 })
1958 }
1959
1960 pub fn materialize_shape(&self, index: usize) -> DrawPrimitive {
1962 let record = self
1963 .shapes
1964 .tables
1965 .shapes
1966 .get(index)
1967 .expect("recorded shape index");
1968 let rect = record.rect_value();
1969 let brush = self.brush_of(&record);
1970 let stroke = record.stroke();
1971 let primitive = match (record.kind(), record.trapezoid()) {
1972 (_, Some(trapezoid)) => DrawPrimitive::Trapezoid {
1973 rect: row_rect(record.radii),
1974 brush,
1975 trapezoid,
1976 },
1977 (RECORD_KIND_ROUND_RECT, _) => DrawPrimitive::RoundRect {
1978 rect,
1979 brush,
1980 radii: CornerRadii {
1981 top_left: record.radii[0],
1982 top_right: record.radii[1],
1983 bottom_right: record.radii[2],
1984 bottom_left: record.radii[3],
1985 },
1986 stroke,
1987 },
1988 (RECORD_KIND_LINE, _) => DrawPrimitive::Line {
1989 rect,
1990 brush,
1991 start: Point::new(record.arc[0], record.arc[1]),
1992 end: Point::new(record.arc_band[2], record.arc_band[3]),
1993 stroke: stroke.unwrap_or_default(),
1994 },
1995 (RECORD_KIND_ARC, _) => DrawPrimitive::Arc {
1996 rect,
1997 brush,
1998 center: Point::new(record.arc[0], record.arc[1]),
1999 radius: record.arc[2],
2000 start_angle: record.arc_band[0],
2001 sweep_angle: record.arc_band[1],
2002 stroke,
2003 inner_radius: record.arc[3],
2004 },
2005 _ => DrawPrimitive::Rect {
2006 rect,
2007 brush,
2008 stroke,
2009 },
2010 };
2011 let blend_mode = record.blend_mode();
2012 if blend_mode == BlendMode::SrcOver {
2013 primitive
2014 } else {
2015 DrawPrimitive::Blend {
2016 primitive: Box::new(primitive),
2017 blend_mode,
2018 }
2019 }
2020 }
2021
2022 pub fn brush_of(&self, record: &ShapeRecord) -> Brush {
2025 if record.brush == 0 {
2026 return Brush::Solid(Color(
2027 record.color[0],
2028 record.color[1],
2029 record.color[2],
2030 record.color[3],
2031 ));
2032 }
2033 let tables = &self.shapes.tables;
2034 let brush = &tables.brushes[record.brush as usize - 1];
2035 let colors = tables.stops
2036 [brush.stop_start as usize..(brush.stop_start + brush.stop_count) as usize]
2037 .iter()
2038 .map(|stop| Color(stop.color[0], stop.color[1], stop.color[2], stop.color[3]))
2039 .collect();
2040 let stops = (brush.explicit_len != NO_EXPLICIT_STOPS).then(|| {
2041 tables.explicit_stops[brush.explicit_start as usize
2042 ..(brush.explicit_start + brush.explicit_len) as usize]
2043 .to_vec()
2044 });
2045 let [a, b, c, d] = brush.params;
2046 let tile_mode = TILE_MODES[brush.tile_mode as usize];
2047 match brush.kind {
2048 BRUSH_KIND_RADIAL => Brush::RadialGradient {
2049 colors,
2050 stops,
2051 center: Point::new(a, b),
2052 radius: c,
2053 tile_mode,
2054 },
2055 BRUSH_KIND_SWEEP => Brush::SweepGradient {
2056 colors,
2057 stops,
2058 center: Point::new(a, b),
2059 },
2060 _ => Brush::LinearGradient {
2061 colors,
2062 stops,
2063 start: Point::new(a, b),
2064 end: Point::new(c, d),
2065 tile_mode,
2066 },
2067 }
2068 }
2069}
2070
2071#[derive(Debug, Default)]
2074pub struct CommandRecorder {
2075 shapes: ShapeRecorder,
2076 content: RecordingContent,
2077 vessel: Option<Arc<ShapeRecorder>>,
2080}
2081
2082impl Clone for CommandRecorder {
2083 fn clone(&self) -> Self {
2084 Self {
2085 shapes: self.shapes.clone(),
2086 content: self.content.clone(),
2087 vessel: None,
2088 }
2089 }
2090}
2091
2092fn take_shapes(
2095 mut shared: Arc<ShapeRecorder>,
2096 copy: impl FnOnce(&ShapeRecorder) -> ShapeRecorder,
2097) -> (ShapeRecorder, Option<Arc<ShapeRecorder>>) {
2098 match Arc::get_mut(&mut shared) {
2099 Some(shapes) => (std::mem::take(shapes), Some(shared)),
2100 None => (copy(&shared), None),
2101 }
2102}
2103
2104fn fill_vessel(vessel: Option<Arc<ShapeRecorder>>, shapes: ShapeRecorder) -> Arc<ShapeRecorder> {
2106 let Some(mut vessel) = vessel else {
2107 return Arc::new(shapes);
2108 };
2109 match Arc::get_mut(&mut vessel) {
2110 Some(slot) => {
2111 *slot = shapes;
2112 vessel
2113 }
2114 None => Arc::new(shapes),
2115 }
2116}
2117
2118impl CommandRecorder {
2119 pub fn from_primitives(primitives: impl IntoIterator<Item = DrawPrimitive>) -> Self {
2121 let mut recorder = Self::default();
2122 for primitive in primitives {
2123 recorder.push_primitive(primitive);
2124 }
2125 recorder
2126 }
2127
2128 pub fn reusing(recording: CommandRecording) -> Self {
2131 let (shapes, vessel) = take_shapes(recording.shapes, |shared| ShapeRecorder {
2132 tables: shared.tables.with_capacity_of(),
2133 ..ShapeRecorder::default()
2134 });
2135 let mut recorder = Self {
2136 shapes,
2137 content: recording.content,
2138 vessel,
2139 };
2140 recorder.clear();
2141 recorder
2142 }
2143
2144 pub fn finish(self) -> CommandRecording {
2146 CommandRecording {
2147 shapes: fill_vessel(self.vessel, self.shapes),
2148 content: self.content,
2149 fingerprint: OnceCell::new(),
2150 }
2151 }
2152
2153 pub fn len(&self) -> usize {
2155 self.shapes.tables.shapes.len()
2156 + self.content.others.len()
2157 + self.content.content_markers as usize
2158 }
2159
2160 pub fn is_empty(&self) -> bool {
2162 self.len() == 0
2163 }
2164
2165 pub fn content_markers(&self) -> u32 {
2167 self.content.content_markers
2168 }
2169
2170 pub fn clear(&mut self) {
2173 self.shapes.clear();
2174 self.content.others.clear();
2175 self.content.min = [f32::INFINITY; 2];
2176 self.content.max = [f32::NEG_INFINITY; 2];
2177 self.content.summary = RecordingSummary::default();
2178 self.content.content_markers = 0;
2179 }
2180
2181 pub fn reserve_shapes(&mut self, additional: usize) {
2183 self.shapes.tables_mut().shapes.reserve(additional);
2184 }
2185
2186 pub fn push_content(&mut self) {
2188 self.content.content_markers += 1;
2189 self.content.summary.has_content_markers = true;
2190 self.shapes.push_content_segment();
2191 }
2192
2193 pub fn push_primitive(&mut self, primitive: DrawPrimitive) {
2196 if matches!(primitive, DrawPrimitive::Content) {
2197 self.push_content();
2198 return;
2199 }
2200 match self.shapes.push_primitive(primitive) {
2201 Recorded::Shape(_) => self.note_shape(),
2202 Recorded::Other(other) => self.push_other(other),
2203 }
2204 }
2205
2206 #[inline]
2207 fn note_shape(&mut self) {
2208 self.content.summary.has_non_shadow = true;
2209 self.content.summary.has_shapes = true;
2210 }
2211
2212 fn include_bounds(&mut self, rect: Rect) {
2213 self.content.min[0] = self.content.min[0].min(rect.x);
2214 self.content.min[1] = self.content.min[1].min(rect.y);
2215 self.content.max[0] = self.content.max[0].max(rect.x + rect.width);
2216 self.content.max[1] = self.content.max[1].max(rect.y + rect.height);
2217 }
2218
2219 pub fn push_rect(
2221 &mut self,
2222 rect: Rect,
2223 brush: &Brush,
2224 stroke: Option<Stroke>,
2225 blend: BlendMode,
2226 ) {
2227 self.shapes.push_rect(rect, brush, stroke, blend);
2228 self.note_shape();
2229 }
2230
2231 pub fn push_round_rect(
2233 &mut self,
2234 rect: Rect,
2235 brush: &Brush,
2236 radii: CornerRadii,
2237 stroke: Option<Stroke>,
2238 blend: BlendMode,
2239 ) {
2240 self.shapes
2241 .push_round_rect(rect, brush, radii, stroke, blend);
2242 self.note_shape();
2243 }
2244
2245 #[inline]
2248 pub fn push_arc(&mut self, rect: Rect, args: &ArcRecordArgs<'_>) {
2249 self.shapes.push_arc(rect, args);
2250 self.note_shape();
2251 }
2252
2253 #[inline]
2257 pub fn push_scope_arc(&mut self, args: &ArcRecordArgs<'_>, geometry: &ArcGeometry) {
2258 self.shapes.push_scope_arc(args, geometry);
2259 self.note_shape();
2260 }
2261
2262 pub fn push_line(
2264 &mut self,
2265 line: &LineGeometry,
2266 brush: &Brush,
2267 stroke: Stroke,
2268 blend: BlendMode,
2269 ) {
2270 self.shapes.push_line(line, brush, stroke, blend);
2271 self.note_shape();
2272 }
2273
2274 pub fn push_trapezoids(
2277 &mut self,
2278 brush_rect: Rect,
2279 trapezoids: &[Trapezoid],
2280 brush: &Brush,
2281 blend: BlendMode,
2282 ) {
2283 if trapezoids.is_empty() {
2284 return;
2285 }
2286 self.shapes
2287 .push_trapezoids(brush_rect, trapezoids, brush, blend);
2288 self.note_shape();
2289 }
2290
2291 pub fn push_other(&mut self, primitive: DrawPrimitive) {
2293 self.content.summary.has_others = true;
2294 self.content.summary.note(&primitive);
2295 if let Some(rect) = primitive_coverage_rect(&primitive) {
2296 self.include_bounds(rect);
2297 }
2298 let index = self.content.others.len() as u32;
2299 self.content.others.push(primitive);
2300 self.shapes
2301 .extend_segment(RecordLane::Others, index, BlendMode::SrcOver, 0);
2302 }
2303
2304 pub fn merge_summary(&mut self, other: RecordingSummary) {
2307 self.content.summary.merge(other);
2308 }
2309}
2310
2311pub struct ArcRecordArgs<'a> {
2314 pub brush: &'a Brush,
2315 pub center: Point,
2316 pub radius: f32,
2317 pub start_angle: f32,
2318 pub sweep_angle: f32,
2319 pub stroke: Option<Stroke>,
2320 pub inner_radius: f32,
2321 pub blend_mode: BlendMode,
2322}
2323
2324fn stroked_circle_ring(
2327 rect: Rect,
2328 radii: CornerRadii,
2329 stroke: Option<Stroke>,
2330) -> Option<ArcGeometry> {
2331 const CIRCLE_TOLERANCE: f32 = 0.01;
2332 let stroke = stroke?;
2333 if rect.width.to_bits() != rect.height.to_bits() || !stroke.is_visible() {
2334 return None;
2335 }
2336 let half = rect.width * 0.5;
2337 let radius = radii.top_left;
2338 if !radius.is_finite()
2339 || radius <= 0.0
2340 || (radius - half).abs() > CIRCLE_TOLERANCE
2341 || radii.top_right.to_bits() != radius.to_bits()
2342 || radii.bottom_right.to_bits() != radius.to_bits()
2343 || radii.bottom_left.to_bits() != radius.to_bits()
2344 {
2345 return None;
2346 }
2347 let half_width = stroke.half_width();
2348 Some(ArcGeometry::new(
2349 Point::new(rect.x + half, rect.y + half),
2350 (half - half_width).max(0.0),
2351 half + half_width,
2352 0.0,
2353 TAU,
2354 StrokeCap::Round,
2355 ))
2356}
2357
2358fn union_rect(a: Rect, b: Rect) -> Rect {
2359 let x = a.x.min(b.x);
2360 let y = a.y.min(b.y);
2361 Rect {
2362 x,
2363 y,
2364 width: (a.x + a.width).max(b.x + b.width) - x,
2365 height: (a.y + a.height).max(b.y + b.height) - y,
2366 }
2367}
2368
2369fn rect_row(rect: Rect) -> [f32; 4] {
2370 [rect.x, rect.y, rect.width, rect.height]
2371}
2372
2373fn row_rect(row: [f32; 4]) -> Rect {
2374 Rect {
2375 x: row[0],
2376 y: row[1],
2377 width: row[2],
2378 height: row[3],
2379 }
2380}
2381
2382#[inline(always)]
2385pub fn normalized_band(args: &ArcRecordArgs<'_>) -> ArcGeometry {
2386 ArcGeometry::of_band(
2387 args.center,
2388 arc_band(args.radius, args.inner_radius, args.stroke),
2389 args.start_angle,
2390 args.sweep_angle,
2391 )
2392}
2393
2394fn band_disc(geometry: &ArcGeometry) -> Rect {
2397 let reach = geometry.outer_radius + geometry.half_thickness();
2398 Rect {
2399 x: geometry.center.x - reach,
2400 y: geometry.center.y - reach,
2401 width: reach + reach,
2402 height: reach + reach,
2403 }
2404}
2405
2406pub fn arc_trig(start_angle: f32, sweep_angle: f32) -> [f32; 4] {
2411 if sweep_angle >= TAU && start_angle == 0.0 {
2412 return [0.0, -1.0, 0.0, -1.0];
2413 }
2414 let half_sweep = within(sweep_angle, 0.0, TAU) * 0.5;
2415 let (mid_sin, mid_cos) = (start_angle + half_sweep).sin_cos();
2416 let (half_sin, half_cos) = half_sweep.sin_cos();
2417 [mid_sin, mid_cos, at_least(half_sin, 0.0), half_cos]
2418}
2419
2420#[cfg(test)]
2421#[path = "tests/record_tests.rs"]
2422mod tests;
2423
2424#[cfg(test)]
2425#[path = "tests/record_band_tests.rs"]
2426mod band_tests;