diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs index ab3ee1e..d66be10 100644 --- a/examples/scenes/src/test_scenes.rs +++ b/examples/scenes/src/test_scenes.rs @@ -51,6 +51,7 @@ pub fn test_scenes() -> SceneSet { scene!(conflation_artifacts), scene!(labyrinth), scene!(base_color_test: animated), + scene!(clip_test: animated), ]; SceneSet { scenes } @@ -824,6 +825,96 @@ fn base_color_test(sb: &mut SceneBuilder, params: &mut SceneParams) { ); } +fn clip_test(sb: &mut SceneBuilder, params: &mut SceneParams) { + let clip = { + const X0: f64 = 50.0; + const Y0: f64 = 0.0; + const X1: f64 = 200.0; + const Y1: f64 = 500.0; + [ + PathEl::MoveTo((X0, Y0).into()), + PathEl::LineTo((X1, Y0).into()), + PathEl::LineTo((X1, Y0 + (Y1 - Y0)).into()), + PathEl::LineTo((X1 + (X0 - X1), Y1).into()), + PathEl::LineTo((X0, Y1).into()), + PathEl::ClosePath, + ] + }; + sb.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &clip); + { + let text_size = 60.0 + 40.0 * (params.time as f32).sin(); + let s = "Some clipped text!"; + params.text.add( + sb, + None, + text_size, + None, + Affine::translate((110.0, 100.0)), + s, + ); + } + sb.pop_layer(); + + let large_background_rect = kurbo::Rect::new(-1000.0, -1000.0, 2000.0, 2000.0); + let inside_clip_rect = kurbo::Rect::new(11.0, 13.399999999999999, 59.0, 56.6); + let outside_clip_rect = kurbo::Rect::new( + 12.599999999999998, + 12.599999999999998, + 57.400000000000006, + 57.400000000000006, + ); + let clip_rect = kurbo::Rect::new(0.0, 0.0, 74.4, 339.20000000000005); + let scale = 2.0; + + sb.push_layer( + BlendMode { + mix: peniko::Mix::Normal, + compose: peniko::Compose::SrcOver, + }, + 1.0, + Affine::new([scale, 0.0, 0.0, scale, 27.07470703125, 176.40660533027858]), + &clip_rect, + ); + + sb.fill( + peniko::Fill::NonZero, + kurbo::Affine::new([scale, 0.0, 0.0, scale, 27.07470703125, 176.40660533027858]), + peniko::Color::rgb8(0, 0, 255), + None, + &large_background_rect, + ); + sb.fill( + peniko::Fill::NonZero, + kurbo::Affine::new([ + scale, + 0.0, + 0.0, + scale, + 29.027636718750003, + 182.9755506427786, + ]), + peniko::Color::rgb8(0, 255, 0), + None, + &inside_clip_rect, + ); + sb.fill( + peniko::Fill::NonZero, + kurbo::Affine::new([ + scale, + 0.0, + 0.0, + scale, + 29.027636718750003, + scale * 559.3583631427786, + ]), + peniko::Color::rgb8(255, 0, 0), + None, + &outside_clip_rect, + ); + + sb.pop_layer(); +} + fn around_center(xform: Affine, center: Point) -> Affine { Affine::translate(center.to_vec2()) * xform * Affine::translate(-center.to_vec2()) } diff --git a/shader/binning.wgsl b/shader/binning.wgsl index 2672bb8..c46026d 100644 --- a/shader/binning.wgsl +++ b/shader/binning.wgsl @@ -86,15 +86,20 @@ fn main( let path_bbox = path_bbox_buf[draw_monoid.path_ix]; let pb = vec4(vec4(path_bbox.x0, path_bbox.y0, path_bbox.x1, path_bbox.y1)); - let bbox_raw = bbox_intersect(clip_bbox, pb); - // TODO(naga): clunky expression a workaround for broken lhs swizzle - let bbox = vec4(bbox_raw.xy, max(bbox_raw.xy, bbox_raw.zw)); + let bbox = bbox_intersect(clip_bbox, pb); intersected_bbox[element_ix] = bbox; - x0 = i32(floor(bbox.x * SX)); - y0 = i32(floor(bbox.y * SY)); - x1 = i32(ceil(bbox.z * SX)); - y1 = i32(ceil(bbox.w * SY)); + + // `bbox_intersect` can result in a zero or negative area intersection if the path bbox lies + // outside the clip bbox. If that is the case, Don't round up the bottom-right corner of the + // and leave the coordinates at 0. This way the path will get clipped out and won't get + // assigned to a bin. + if bbox.x < bbox.z && bbox.y < bbox.w { + x0 = i32(floor(bbox.x * SX)); + y0 = i32(floor(bbox.y * SY)); + x1 = i32(ceil(bbox.z * SX)); + y1 = i32(ceil(bbox.w * SY)); + } } let width_in_bins = i32((config.width_in_tiles + N_TILE_X - 1u) / N_TILE_X); let height_in_bins = i32((config.height_in_tiles + N_TILE_Y - 1u) / N_TILE_Y); diff --git a/shader/tile_alloc.wgsl b/shader/tile_alloc.wgsl index 8ec9217..1f7a4bc 100644 --- a/shader/tile_alloc.wgsl +++ b/shader/tile_alloc.wgsl @@ -68,10 +68,15 @@ fn main( var y1 = 0; if drawtag != DRAWTAG_NOP && drawtag != DRAWTAG_END_CLIP { let bbox = draw_bboxes[drawobj_ix]; - x0 = i32(floor(bbox.x * SX)); - y0 = i32(floor(bbox.y * SY)); - x1 = i32(ceil(bbox.z * SX)); - y1 = i32(ceil(bbox.w * SY)); + + // Don't round up the bottom-right corner of the bbox if the area is zero and leave the + // coordinates at 0. This will make `tile_count` zero as the shape is clipped out. + if bbox.x < bbox.z && bbox.y < bbox.w { + x0 = i32(floor(bbox.x * SX)); + y0 = i32(floor(bbox.y * SY)); + x1 = i32(ceil(bbox.z * SX)); + y1 = i32(ceil(bbox.w * SY)); + } } let ux0 = u32(clamp(x0, 0, i32(config.width_in_tiles))); let uy0 = u32(clamp(y0, 0, i32(config.height_in_tiles)));