diff --git a/piet-gpu-types/src/annotated.rs b/piet-gpu-types/src/annotated.rs index 681a7ec..04f2111 100644 --- a/piet-gpu-types/src/annotated.rs +++ b/piet-gpu-types/src/annotated.rs @@ -18,12 +18,17 @@ piet_gpu! { // That's expected to be uncommon, so we could special-case it. linewidth: f32, } + struct AnnoClip { + bbox: [f32; 4], + } enum Annotated { Nop, Stroke(AnnoStroke), Fill(AnnoFill), FillMask(AnnoFillMask), FillMaskInv(AnnoFillMask), + BeginClip(AnnoClip), + EndClip(AnnoClip), } } } diff --git a/piet-gpu-types/src/ptcl.rs b/piet-gpu-types/src/ptcl.rs index 95dcdc6..d05218b 100644 --- a/piet-gpu-types/src/ptcl.rs +++ b/piet-gpu-types/src/ptcl.rs @@ -30,6 +30,14 @@ piet_gpu! { backdrop: i32, mask: f32, } + struct CmdBeginClip { + tile_ref: u32, + backdrop: i32, + } + struct CmdEndClip { + // This will be 1.0 for clips, but we can imagine blend groups. + alpha: f32, + } struct CmdSolid { rgba_color: u32, } @@ -46,6 +54,8 @@ piet_gpu! { Fill(CmdFill), FillMask(CmdFillMask), FillMaskInv(CmdFillMask), + BeginClip(CmdBeginClip), + EndClip(CmdEndClip), Stroke(CmdStroke), Solid(CmdSolid), SolidMask(CmdSolidMask), diff --git a/piet-gpu-types/src/scene.rs b/piet-gpu-types/src/scene.rs index 1359c1b..7e2fb43 100644 --- a/piet-gpu-types/src/scene.rs +++ b/piet-gpu-types/src/scene.rs @@ -1,6 +1,8 @@ use piet_gpu_derive::piet_gpu; -pub use self::scene::{CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform}; +pub use self::scene::{ + BeginClip, CubicSeg, Element, EndClip, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, +}; piet_gpu! { #[rust_encode] @@ -36,6 +38,15 @@ piet_gpu! { mat: [f32; 4], translate: [f32; 2], } + struct BeginClip { + bbox: [f32; 4], + // TODO: add alpha? + } + struct EndClip { + // The delta between the BeginClip and EndClip element indices. + // It is stored as a delta to facilitate binary string concatenation. + delta: u32, + } enum Element { Nop, // Another approach to encoding would be to use a single @@ -55,6 +66,8 @@ piet_gpu! { Transform(Transform), FillMask(FillMask), FillMaskInv(FillMask), + BeginClip(BeginClip), + EndClip(EndClip), } } } diff --git a/piet-gpu/shader/annotated.h b/piet-gpu/shader/annotated.h index 847ca06..986eff2 100644 --- a/piet-gpu/shader/annotated.h +++ b/piet-gpu/shader/annotated.h @@ -12,6 +12,10 @@ struct AnnoStrokeRef { uint offset; }; +struct AnnoClipRef { + uint offset; +}; + struct AnnotatedRef { uint offset; }; @@ -50,11 +54,23 @@ AnnoStrokeRef AnnoStroke_index(AnnoStrokeRef ref, uint index) { return AnnoStrokeRef(ref.offset + index * AnnoStroke_size); } +struct AnnoClip { + vec4 bbox; +}; + +#define AnnoClip_size 16 + +AnnoClipRef AnnoClip_index(AnnoClipRef ref, uint index) { + return AnnoClipRef(ref.offset + index * AnnoClip_size); +} + #define Annotated_Nop 0 #define Annotated_Stroke 1 #define Annotated_Fill 2 #define Annotated_FillMask 3 #define Annotated_FillMaskInv 4 +#define Annotated_BeginClip 5 +#define Annotated_EndClip 6 #define Annotated_size 28 AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { @@ -130,6 +146,25 @@ void AnnoStroke_write(AnnoStrokeRef ref, AnnoStroke s) { annotated[ix + 5] = floatBitsToUint(s.linewidth); } +AnnoClip AnnoClip_read(AnnoClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = annotated[ix + 0]; + uint raw1 = annotated[ix + 1]; + uint raw2 = annotated[ix + 2]; + uint raw3 = annotated[ix + 3]; + AnnoClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +void AnnoClip_write(AnnoClipRef ref, AnnoClip s) { + uint ix = ref.offset >> 2; + annotated[ix + 0] = floatBitsToUint(s.bbox.x); + annotated[ix + 1] = floatBitsToUint(s.bbox.y); + annotated[ix + 2] = floatBitsToUint(s.bbox.z); + annotated[ix + 3] = floatBitsToUint(s.bbox.w); +} + uint Annotated_tag(AnnotatedRef ref) { return annotated[ref.offset >> 2]; } @@ -150,6 +185,14 @@ AnnoFillMask Annotated_FillMaskInv_read(AnnotatedRef ref) { return AnnoFillMask_read(AnnoFillMaskRef(ref.offset + 4)); } +AnnoClip Annotated_BeginClip_read(AnnotatedRef ref) { + return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +} + +AnnoClip Annotated_EndClip_read(AnnotatedRef ref) { + return AnnoClip_read(AnnoClipRef(ref.offset + 4)); +} + void Annotated_Nop_write(AnnotatedRef ref) { annotated[ref.offset >> 2] = Annotated_Nop; } @@ -174,3 +217,13 @@ void Annotated_FillMaskInv_write(AnnotatedRef ref, AnnoFillMask s) { AnnoFillMask_write(AnnoFillMaskRef(ref.offset + 4), s); } +void Annotated_BeginClip_write(AnnotatedRef ref, AnnoClip s) { + annotated[ref.offset >> 2] = Annotated_BeginClip; + AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +} + +void Annotated_EndClip_write(AnnotatedRef ref, AnnoClip s) { + annotated[ref.offset >> 2] = Annotated_EndClip; + AnnoClip_write(AnnoClipRef(ref.offset + 4), s); +} + diff --git a/piet-gpu/shader/backdrop.spv b/piet-gpu/shader/backdrop.spv index e2093d9..7f0852d 100644 Binary files a/piet-gpu/shader/backdrop.spv and b/piet-gpu/shader/backdrop.spv differ diff --git a/piet-gpu/shader/binning.spv b/piet-gpu/shader/binning.spv index 50070a1..8f44c89 100644 Binary files a/piet-gpu/shader/binning.spv and b/piet-gpu/shader/binning.spv differ diff --git a/piet-gpu/shader/coarse.spv b/piet-gpu/shader/coarse.spv index 7758b96..17bae64 100644 Binary files a/piet-gpu/shader/coarse.spv and b/piet-gpu/shader/coarse.spv differ diff --git a/piet-gpu/shader/kernel4.comp b/piet-gpu/shader/kernel4.comp index d21aa87..919d120 100644 --- a/piet-gpu/shader/kernel4.comp +++ b/piet-gpu/shader/kernel4.comp @@ -27,6 +27,8 @@ layout(rgba8, set = 0, binding = 2) uniform writeonly image2D image; #include "ptcl.h" #include "tile.h" +#define BLEND_STACK_SIZE 4 + // Calculate coverage based on backdrop + coverage of each line segment float[CHUNK] computeArea(vec2 xy, int backdrop, uint tile_ref) { // Probably better to store as float, but conversion is no doubt cheap. @@ -69,6 +71,8 @@ void main() { vec2 xy = vec2(xy_uint); vec3 rgb[CHUNK]; float mask[CHUNK]; + uint blend_stack[BLEND_STACK_SIZE][CHUNK]; + uint blend_sp = 0; for (uint i = 0; i < CHUNK; i++) { rgb[i] = vec3(0.5); mask[i] = 1.0; @@ -137,6 +141,22 @@ void main() { mask[k] = mix(mask[k], fill_mask.mask, 1.0 - area[k]); } break; + case Cmd_BeginClip: + CmdBeginClip begin_clip = Cmd_BeginClip_read(cmd_ref); + area = computeArea(xy, begin_clip.backdrop, begin_clip.tile_ref); + for (uint k = 0; k < CHUNK; k++) { + blend_stack[blend_sp][k] = packUnorm4x8(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0))); + } + blend_sp++; + break; + case Cmd_EndClip: + CmdEndClip end_clip = Cmd_EndClip_read(cmd_ref); + blend_sp--; + for (uint k = 0; k < CHUNK; k++) { + vec4 rgba = unpackUnorm4x8(blend_stack[blend_sp][k]); + rgb[k] = mix(rgb[k], rgba.rgb, end_clip.alpha * rgba.a); + } + break; case Cmd_Solid: CmdSolid solid = Cmd_Solid_read(cmd_ref); fg_rgba = unpackUnorm4x8(solid.rgba_color).wzyx; diff --git a/piet-gpu/shader/kernel4.spv b/piet-gpu/shader/kernel4.spv index d159fe5..0d38581 100644 Binary files a/piet-gpu/shader/kernel4.spv and b/piet-gpu/shader/kernel4.spv differ diff --git a/piet-gpu/shader/path_coarse.spv b/piet-gpu/shader/path_coarse.spv index db5bc57..f82a031 100644 Binary files a/piet-gpu/shader/path_coarse.spv and b/piet-gpu/shader/path_coarse.spv differ diff --git a/piet-gpu/shader/ptcl.h b/piet-gpu/shader/ptcl.h index d1cc83d..33ab664 100644 --- a/piet-gpu/shader/ptcl.h +++ b/piet-gpu/shader/ptcl.h @@ -20,6 +20,14 @@ struct CmdFillMaskRef { uint offset; }; +struct CmdBeginClipRef { + uint offset; +}; + +struct CmdEndClipRef { + uint offset; +}; + struct CmdSolidRef { uint offset; }; @@ -103,6 +111,27 @@ CmdFillMaskRef CmdFillMask_index(CmdFillMaskRef ref, uint index) { return CmdFillMaskRef(ref.offset + index * CmdFillMask_size); } +struct CmdBeginClip { + uint tile_ref; + int backdrop; +}; + +#define CmdBeginClip_size 8 + +CmdBeginClipRef CmdBeginClip_index(CmdBeginClipRef ref, uint index) { + return CmdBeginClipRef(ref.offset + index * CmdBeginClip_size); +} + +struct CmdEndClip { + float alpha; +}; + +#define CmdEndClip_size 4 + +CmdEndClipRef CmdEndClip_index(CmdEndClipRef ref, uint index) { + return CmdEndClipRef(ref.offset + index * CmdEndClip_size); +} + struct CmdSolid { uint rgba_color; }; @@ -139,10 +168,12 @@ CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) { #define Cmd_Fill 3 #define Cmd_FillMask 4 #define Cmd_FillMaskInv 5 -#define Cmd_Stroke 6 -#define Cmd_Solid 7 -#define Cmd_SolidMask 8 -#define Cmd_Jump 9 +#define Cmd_BeginClip 6 +#define Cmd_EndClip 7 +#define Cmd_Stroke 8 +#define Cmd_Solid 9 +#define Cmd_SolidMask 10 +#define Cmd_Jump 11 #define Cmd_size 20 CmdRef Cmd_index(CmdRef ref, uint index) { @@ -271,6 +302,35 @@ void CmdFillMask_write(CmdFillMaskRef ref, CmdFillMask s) { ptcl[ix + 2] = floatBitsToUint(s.mask); } +CmdBeginClip CmdBeginClip_read(CmdBeginClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + uint raw1 = ptcl[ix + 1]; + CmdBeginClip s; + s.tile_ref = raw0; + s.backdrop = int(raw1); + return s; +} + +void CmdBeginClip_write(CmdBeginClipRef ref, CmdBeginClip s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = s.tile_ref; + ptcl[ix + 1] = uint(s.backdrop); +} + +CmdEndClip CmdEndClip_read(CmdEndClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = ptcl[ix + 0]; + CmdEndClip s; + s.alpha = uintBitsToFloat(raw0); + return s; +} + +void CmdEndClip_write(CmdEndClipRef ref, CmdEndClip s) { + uint ix = ref.offset >> 2; + ptcl[ix + 0] = floatBitsToUint(s.alpha); +} + CmdSolid CmdSolid_read(CmdSolidRef ref) { uint ix = ref.offset >> 2; uint raw0 = ptcl[ix + 0]; @@ -334,6 +394,14 @@ CmdFillMask Cmd_FillMaskInv_read(CmdRef ref) { return CmdFillMask_read(CmdFillMaskRef(ref.offset + 4)); } +CmdBeginClip Cmd_BeginClip_read(CmdRef ref) { + return CmdBeginClip_read(CmdBeginClipRef(ref.offset + 4)); +} + +CmdEndClip Cmd_EndClip_read(CmdRef ref) { + return CmdEndClip_read(CmdEndClipRef(ref.offset + 4)); +} + CmdStroke Cmd_Stroke_read(CmdRef ref) { return CmdStroke_read(CmdStrokeRef(ref.offset + 4)); } @@ -379,6 +447,16 @@ void Cmd_FillMaskInv_write(CmdRef ref, CmdFillMask s) { CmdFillMask_write(CmdFillMaskRef(ref.offset + 4), s); } +void Cmd_BeginClip_write(CmdRef ref, CmdBeginClip s) { + ptcl[ref.offset >> 2] = Cmd_BeginClip; + CmdBeginClip_write(CmdBeginClipRef(ref.offset + 4), s); +} + +void Cmd_EndClip_write(CmdRef ref, CmdEndClip s) { + ptcl[ref.offset >> 2] = Cmd_EndClip; + CmdEndClip_write(CmdEndClipRef(ref.offset + 4), s); +} + void Cmd_Stroke_write(CmdRef ref, CmdStroke s) { ptcl[ref.offset >> 2] = Cmd_Stroke; CmdStroke_write(CmdStrokeRef(ref.offset + 4), s); diff --git a/piet-gpu/shader/scene.h b/piet-gpu/shader/scene.h index 6823fe6..0a4a2ce 100644 --- a/piet-gpu/shader/scene.h +++ b/piet-gpu/shader/scene.h @@ -32,6 +32,14 @@ struct TransformRef { uint offset; }; +struct BeginClipRef { + uint offset; +}; + +struct EndClipRef { + uint offset; +}; + struct ElementRef { uint offset; }; @@ -123,6 +131,26 @@ TransformRef Transform_index(TransformRef ref, uint index) { return TransformRef(ref.offset + index * Transform_size); } +struct BeginClip { + vec4 bbox; +}; + +#define BeginClip_size 16 + +BeginClipRef BeginClip_index(BeginClipRef ref, uint index) { + return BeginClipRef(ref.offset + index * BeginClip_size); +} + +struct EndClip { + uint clip_size; +}; + +#define EndClip_size 4 + +EndClipRef EndClip_index(EndClipRef ref, uint index) { + return EndClipRef(ref.offset + index * EndClip_size); +} + #define Element_Nop 0 #define Element_StrokeLine 1 #define Element_FillLine 2 @@ -136,6 +164,8 @@ TransformRef Transform_index(TransformRef ref, uint index) { #define Element_Transform 10 #define Element_FillMask 11 #define Element_FillMaskInv 12 +#define Element_BeginClip 13 +#define Element_EndClip 14 #define Element_size 36 ElementRef Element_index(ElementRef ref, uint index) { @@ -233,6 +263,25 @@ Transform Transform_read(TransformRef ref) { return s; } +BeginClip BeginClip_read(BeginClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + BeginClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +EndClip EndClip_read(EndClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + EndClip s; + s.clip_size = raw0; + return s; +} + uint Element_tag(ElementRef ref) { return scene[ref.offset >> 2]; } @@ -285,3 +334,11 @@ FillMask Element_FillMaskInv_read(ElementRef ref) { return FillMask_read(FillMaskRef(ref.offset + 4)); } +BeginClip Element_BeginClip_read(ElementRef ref) { + return BeginClip_read(BeginClipRef(ref.offset + 4)); +} + +EndClip Element_EndClip_read(ElementRef ref) { + return EndClip_read(EndClipRef(ref.offset + 4)); +} + diff --git a/piet-gpu/shader/tile_alloc.spv b/piet-gpu/shader/tile_alloc.spv index af52665..449f4d6 100644 Binary files a/piet-gpu/shader/tile_alloc.spv and b/piet-gpu/shader/tile_alloc.spv differ diff --git a/piet-gpu/src/render_ctx.rs b/piet-gpu/src/render_ctx.rs index 9593704..b3aa895 100644 --- a/piet-gpu/src/render_ctx.rs +++ b/piet-gpu/src/render_ctx.rs @@ -1,12 +1,13 @@ -use std::{borrow::Cow, ops::RangeBounds}; +use std::{borrow::Cow, convert::TryInto, ops::RangeBounds}; use piet_gpu_types::encoder::{Encode, Encoder}; -use piet_gpu_types::scene::{CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke}; +use piet_gpu_types::scene::{ + BeginClip, CubicSeg, Element, EndClip, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, +}; use piet::{ - kurbo::Size, - kurbo::{Affine, PathEl, Point, Rect, Shape}, + kurbo::{Affine, Insets, PathEl, Point, Rect, Shape, Size}, HitTestPosition, TextAttribute, TextStorage, }; @@ -33,8 +34,14 @@ pub struct PietGpuRenderContext { stroke_width: f32, // We're tallying these cpu-side for expedience, but will probably // move this to some kind of readback from element processing. + /// The count of elements that make it through to coarse rasterization. path_count: usize, + /// The count of path segment elements. pathseg_count: usize, + + cur_transform: Affine, + state_stack: Vec, + clip_stack: Vec, } #[derive(Clone)] @@ -43,6 +50,21 @@ pub enum PietGpuBrush { Gradient, } +#[derive(Default)] +struct State { + /// The transform relative to the parent state. + transform: Affine, + n_clip: usize, +} + +struct ClipElement { + /// Index of BeginClip element in element vec, for bbox fixup. + begin_ix: usize, + bbox: Option, + /// The transform relative to the next clip element on the stack. + transform: Affine, +} + const TOLERANCE: f64 = 0.25; impl PietGpuRenderContext { @@ -58,6 +80,9 @@ impl PietGpuRenderContext { stroke_width, path_count: 0, pathseg_count: 0, + cur_transform: Affine::default(), + state_stack: Vec::new(), + clip_stack: Vec::new(), } } @@ -96,17 +121,19 @@ impl RenderContext for PietGpuRenderContext { fn clear(&mut self, _color: Color) {} fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush, width: f64) { - let width = width as f32; - if self.stroke_width != width { + let width_f32 = width as f32; + if self.stroke_width != width_f32 { self.elements - .push(Element::SetLineWidth(SetLineWidth { width })); - self.stroke_width = width; + .push(Element::SetLineWidth(SetLineWidth { width: width_f32 })); + self.stroke_width = width_f32; } let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, false); match brush { PietGpuBrush::Solid(rgba_color) => { + // Note: the bbox contribution of stroke becomes more complicated with miter joins. + self.accumulate_bbox(|| shape.bounding_box() + Insets::uniform(width * 0.5)); + let path = shape.path_elements(TOLERANCE); + self.encode_path(path, false); let stroke = Stroke { rgba_color }; self.elements.push(Element::Stroke(stroke)); self.path_count += 1; @@ -126,21 +153,34 @@ impl RenderContext for PietGpuRenderContext { fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush) { let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); - let path = shape.path_elements(TOLERANCE); - self.encode_path(path, true); - match brush { - PietGpuBrush::Solid(rgba_color) => { - let fill = Fill { rgba_color }; - self.elements.push(Element::Fill(fill)); - self.path_count += 1; - } - _ => (), + if let PietGpuBrush::Solid(rgba_color) = brush { + // Note: we might get a good speedup from using an approximate bounding box. + // Perhaps that should be added to kurbo. + self.accumulate_bbox(|| shape.bounding_box()); + let path = shape.path_elements(TOLERANCE); + self.encode_path(path, true); + let fill = Fill { rgba_color }; + self.elements.push(Element::Fill(fill)); + self.path_count += 1; } } fn fill_even_odd(&mut self, _shape: impl Shape, _brush: &impl IntoBrush) {} - fn clip(&mut self, _shape: impl Shape) {} + fn clip(&mut self, shape: impl Shape) { + let begin_ix = self.elements.len(); + let path = shape.path_elements(TOLERANCE); + self.encode_path(path, true); + self.elements.push(Element::BeginClip(BeginClip { + bbox: Default::default(), + })); + self.clip_stack.push(ClipElement { + bbox: None, + begin_ix, + transform: Affine::default(), + }); + self.path_count += 1; + } fn text(&mut self) -> &mut Self::Text { &mut self.inner_text @@ -149,15 +189,42 @@ impl RenderContext for PietGpuRenderContext { fn draw_text(&mut self, _layout: &Self::TextLayout, _pos: impl Into) {} fn save(&mut self) -> Result<(), Error> { + self.state_stack.push(Default::default()); Ok(()) } + fn restore(&mut self) -> Result<(), Error> { - Ok(()) + if let Some(state) = self.state_stack.pop() { + if state.transform != Affine::default() { + let a_inv = state.transform.inverse(); + self.elements + .push(Element::Transform(to_scene_transform(a_inv))); + self.cur_transform *= a_inv; + } + for _ in 0..state.n_clip { + self.pop_clip(); + } + Ok(()) + } else { + Err(Error::StackUnbalance) + } } + fn finish(&mut self) -> Result<(), Error> { Ok(()) } - fn transform(&mut self, _transform: Affine) {} + + fn transform(&mut self, transform: Affine) { + self.elements + .push(Element::Transform(to_scene_transform(transform))); + if let Some(tos) = self.state_stack.last_mut() { + tos.transform *= transform; + } + if let Some(tos) = self.clip_stack.last_mut() { + tos.transform *= transform; + } + self.cur_transform *= transform; + } fn make_image( &mut self, @@ -189,7 +256,13 @@ impl RenderContext for PietGpuRenderContext { fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush) {} fn current_transform(&self) -> Affine { - Default::default() + self.cur_transform + } + + fn with_save(&mut self, f: impl FnOnce(&mut Self) -> Result<(), Error>) -> Result<(), Error> { + self.save()?; + // Always try to restore the stack, even if `f` errored. + f(self).and(self.restore()) } } @@ -316,6 +389,33 @@ impl PietGpuRenderContext { } } } + + fn pop_clip(&mut self) { + let tos = self.clip_stack.pop().unwrap(); + let delta = (self.elements.len() - tos.begin_ix).try_into().unwrap(); + self.elements.push(Element::EndClip(EndClip { delta })); + self.path_count += 1; + if let Some(bbox) = tos.bbox { + if let Element::BeginClip(begin_clip) = &mut self.elements[tos.begin_ix] { + begin_clip.bbox = rect_to_f32_4(bbox); + } else { + unreachable!("expected BeginClip, not found"); + } + self.accumulate_bbox(|| bbox); + } + } + + fn accumulate_bbox(&mut self, f: impl FnOnce() -> Rect) { + if let Some(tos) = self.clip_stack.last_mut() { + let bbox = f(); + let bbox = tos.transform.transform_rect_bbox(bbox); + tos.bbox = if let Some(old_bbox) = tos.bbox { + Some(old_bbox.union(bbox)) + } else { + Some(bbox) + }; + } + } } impl Text for PietGpuText { @@ -410,3 +510,15 @@ impl IntoBrush for PietGpuBrush { fn to_f32_2(point: Point) -> [f32; 2] { [point.x as f32, point.y as f32] } + +fn rect_to_f32_4(rect: Rect) -> [f32; 4] { + [rect.x0 as f32, rect.y0 as f32, rect.x1 as f32, rect.y1 as f32] +} + +fn to_scene_transform(transform: Affine) -> Transform { + let c = transform.as_coeffs(); + Transform { + mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32], + translate: [c[4] as f32, c[5] as f32], + } +}