diff --git a/examples/with_winit/src/test_scene.rs b/examples/with_winit/src/test_scene.rs index c901af1..310f59b 100644 --- a/examples/with_winit/src/test_scene.rs +++ b/examples/with_winit/src/test_scene.rs @@ -271,7 +271,16 @@ fn blend_square(blend: BlendMode) -> SceneFragment { #[allow(unused)] pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { + use PathEl::*; let rect = Rect::from_origin_size(Point::new(0.0, 0.0), (1000.0, 1000.0)); + let star = [ + MoveTo((50.0, 0.0).into()), + LineTo((21.0, 90.0).into()), + LineTo((98.0, 35.0).into()), + LineTo((2.0, 35.0).into()), + LineTo((79.0, 90.0).into()), + ClosePath, + ]; sb.fill( Fill::NonZero, Affine::IDENTITY, @@ -333,6 +342,20 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) &rect, ); sb.pop_layer(); + sb.fill( + Fill::NonZero, + Affine::translate((400.0, 100.0)), + Color::PURPLE, + None, + &star, + ); + sb.fill( + Fill::EvenOdd, + Affine::translate((500.0, 100.0)), + Color::PURPLE, + None, + &star, + ); } #[allow(unused)] diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl index 46e23be..dec040e 100644 --- a/shader/coarse.wgsl +++ b/shader/coarse.wgsl @@ -79,17 +79,22 @@ fn alloc_cmd(size: u32) { } } -fn write_path(tile: Tile, linewidth: f32) { +fn write_path(tile: Tile, linewidth: f32) -> bool { // TODO: take flags alloc_cmd(3u); if linewidth < 0.0 { + let even_odd = linewidth < -1.0; if tile.segments != 0u { let fill = CmdFill(tile.segments, tile.backdrop); ptcl[cmd_offset] = CMD_FILL; - ptcl[cmd_offset + 1u] = fill.tile; + let segments_and_rule = select(fill.tile << 1u, (fill.tile << 1u) | 1u, even_odd); + ptcl[cmd_offset + 1u] = segments_and_rule; ptcl[cmd_offset + 2u] = u32(fill.backdrop); cmd_offset += 3u; } else { + if even_odd && (abs(tile.backdrop) & 1) == 0 { + return false; + } ptcl[cmd_offset] = CMD_SOLID; cmd_offset += 1u; } @@ -100,6 +105,7 @@ fn write_path(tile: Tile, linewidth: f32) { ptcl[cmd_offset + 2u] = bitcast(stroke.half_width); cmd_offset += 3u; } + return true; } fn write_color(color: CmdColor) { @@ -323,25 +329,28 @@ fn main( // DRAWTAG_FILL_COLOR case 0x44u: { let linewidth = bitcast(info_bin_data[di]); - write_path(tile, linewidth); - let rgba_color = scene[dd]; - write_color(CmdColor(rgba_color)); + if write_path(tile, linewidth) { + let rgba_color = scene[dd]; + write_color(CmdColor(rgba_color)); + } } // DRAWTAG_FILL_LIN_GRADIENT case 0x114u: { let linewidth = bitcast(info_bin_data[di]); - write_path(tile, linewidth); - let index = scene[dd]; - let info_offset = di + 1u; - write_grad(CMD_LIN_GRAD, index, info_offset); + if write_path(tile, linewidth) { + let index = scene[dd]; + let info_offset = di + 1u; + write_grad(CMD_LIN_GRAD, index, info_offset); + } } // DRAWTAG_FILL_RAD_GRADIENT case 0x2dcu: { let linewidth = bitcast(info_bin_data[di]); - write_path(tile, linewidth); - let index = scene[dd]; - let info_offset = di + 1u; - write_grad(CMD_RAD_GRAD, index, info_offset); + if write_path(tile, linewidth) { + let index = scene[dd]; + let info_offset = di + 1u; + write_grad(CMD_RAD_GRAD, index, info_offset); + } } // DRAWTAG_BEGIN_CLIP case 0x9u: { diff --git a/shader/fine.wgsl b/shader/fine.wgsl index 8bef8ea..cd40e18 100644 --- a/shader/fine.wgsl +++ b/shader/fine.wgsl @@ -97,7 +97,7 @@ var output: texture_storage_2d; let PIXELS_PER_THREAD = 4u; -fn fill_path(tile: Tile, xy: vec2) -> array { +fn fill_path(tile: Tile, xy: vec2, even_odd: bool) -> array { var area: array; let backdrop_f = f32(tile.backdrop); for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { @@ -136,9 +136,17 @@ fn fill_path(tile: Tile, xy: vec2) -> array { } segment_ix = segment.next; } - // nonzero winding rule - for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { - area[i] = min(abs(area[i]), 1.0); + if even_odd { + // even-odd winding rule + for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { + let a = abs(area[i]); + area[i] = select(a, 2.0 - min(a, 2.0), a > 1.0); + } + } else { + // non-zero winding rule + for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { + area[i] = min(abs(area[i]), 1.0); + } } return area; } @@ -195,8 +203,10 @@ fn main( // CMD_FILL case 1u: { let fill = read_fill(cmd_ix); - let tile = Tile(fill.backdrop, fill.tile); - area = fill_path(tile, xy); + let segments = fill.tile >> 1u; + let even_odd = (fill.tile & 1u) != 0u; + let tile = Tile(fill.backdrop, segments); + area = fill_path(tile, xy, even_odd); cmd_ix += 3u; } // CMD_STROKE diff --git a/src/scene.rs b/src/scene.rs index 74cc61c..3fb7a96 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -126,7 +126,7 @@ impl<'a> SceneBuilder<'a> { /// Fills a shape using the specified style and brush. pub fn fill<'b>( &mut self, - _style: Fill, + style: Fill, transform: Affine, brush: impl Into>, brush_transform: Option, @@ -134,7 +134,10 @@ impl<'a> SceneBuilder<'a> { ) { self.scene .encode_transform(Transform::from_kurbo(&transform)); - self.scene.encode_linewidth(-1.0); + self.scene.encode_linewidth(match style { + Fill::NonZero => -1.0, + Fill::EvenOdd => -2.0, + }); if self.scene.encode_shape(shape, true) { if let Some(brush_transform) = brush_transform { self.scene