Merge pull request #242 from linebender/evenodd

Support even-odd fill rule
This commit is contained in:
Chad Brokaw 2023-01-11 14:13:24 -05:00 committed by GitHub
commit a9aa3f9cab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 21 deletions

View file

@ -271,7 +271,16 @@ fn blend_square(blend: BlendMode) -> SceneFragment {
#[allow(unused)] #[allow(unused)]
pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) { 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 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( sb.fill(
Fill::NonZero, Fill::NonZero,
Affine::IDENTITY, Affine::IDENTITY,
@ -333,6 +342,20 @@ pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize)
&rect, &rect,
); );
sb.pop_layer(); 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)] #[allow(unused)]

View file

@ -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 // TODO: take flags
alloc_cmd(3u); alloc_cmd(3u);
if linewidth < 0.0 { if linewidth < 0.0 {
let even_odd = linewidth < -1.0;
if tile.segments != 0u { if tile.segments != 0u {
let fill = CmdFill(tile.segments, tile.backdrop); let fill = CmdFill(tile.segments, tile.backdrop);
ptcl[cmd_offset] = CMD_FILL; 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); ptcl[cmd_offset + 2u] = u32(fill.backdrop);
cmd_offset += 3u; cmd_offset += 3u;
} else { } else {
if even_odd && (abs(tile.backdrop) & 1) == 0 {
return false;
}
ptcl[cmd_offset] = CMD_SOLID; ptcl[cmd_offset] = CMD_SOLID;
cmd_offset += 1u; cmd_offset += 1u;
} }
@ -100,6 +105,7 @@ fn write_path(tile: Tile, linewidth: f32) {
ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width); ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width);
cmd_offset += 3u; cmd_offset += 3u;
} }
return true;
} }
fn write_color(color: CmdColor) { fn write_color(color: CmdColor) {
@ -323,26 +329,29 @@ fn main(
// DRAWTAG_FILL_COLOR // DRAWTAG_FILL_COLOR
case 0x44u: { case 0x44u: {
let linewidth = bitcast<f32>(info_bin_data[di]); let linewidth = bitcast<f32>(info_bin_data[di]);
write_path(tile, linewidth); if write_path(tile, linewidth) {
let rgba_color = scene[dd]; let rgba_color = scene[dd];
write_color(CmdColor(rgba_color)); write_color(CmdColor(rgba_color));
} }
}
// DRAWTAG_FILL_LIN_GRADIENT // DRAWTAG_FILL_LIN_GRADIENT
case 0x114u: { case 0x114u: {
let linewidth = bitcast<f32>(info_bin_data[di]); let linewidth = bitcast<f32>(info_bin_data[di]);
write_path(tile, linewidth); if write_path(tile, linewidth) {
let index = scene[dd]; let index = scene[dd];
let info_offset = di + 1u; let info_offset = di + 1u;
write_grad(CMD_LIN_GRAD, index, info_offset); write_grad(CMD_LIN_GRAD, index, info_offset);
} }
}
// DRAWTAG_FILL_RAD_GRADIENT // DRAWTAG_FILL_RAD_GRADIENT
case 0x2dcu: { case 0x2dcu: {
let linewidth = bitcast<f32>(info_bin_data[di]); let linewidth = bitcast<f32>(info_bin_data[di]);
write_path(tile, linewidth); if write_path(tile, linewidth) {
let index = scene[dd]; let index = scene[dd];
let info_offset = di + 1u; let info_offset = di + 1u;
write_grad(CMD_RAD_GRAD, index, info_offset); write_grad(CMD_RAD_GRAD, index, info_offset);
} }
}
// DRAWTAG_BEGIN_CLIP // DRAWTAG_BEGIN_CLIP
case 0x9u: { case 0x9u: {
if tile.segments == 0u && tile.backdrop == 0 { if tile.segments == 0u && tile.backdrop == 0 {

View file

@ -97,7 +97,7 @@ var output: texture_storage_2d<r8, write>;
let PIXELS_PER_THREAD = 4u; let PIXELS_PER_THREAD = 4u;
fn fill_path(tile: Tile, xy: vec2<f32>) -> array<f32, PIXELS_PER_THREAD> { fn fill_path(tile: Tile, xy: vec2<f32>, even_odd: bool) -> array<f32, PIXELS_PER_THREAD> {
var area: array<f32, PIXELS_PER_THREAD>; var area: array<f32, PIXELS_PER_THREAD>;
let backdrop_f = f32(tile.backdrop); let backdrop_f = f32(tile.backdrop);
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) { for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
@ -136,10 +136,18 @@ fn fill_path(tile: Tile, xy: vec2<f32>) -> array<f32, PIXELS_PER_THREAD> {
} }
segment_ix = segment.next; segment_ix = segment.next;
} }
// nonzero winding rule 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) { for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
area[i] = min(abs(area[i]), 1.0); area[i] = min(abs(area[i]), 1.0);
} }
}
return area; return area;
} }
@ -195,8 +203,10 @@ fn main(
// CMD_FILL // CMD_FILL
case 1u: { case 1u: {
let fill = read_fill(cmd_ix); let fill = read_fill(cmd_ix);
let tile = Tile(fill.backdrop, fill.tile); let segments = fill.tile >> 1u;
area = fill_path(tile, xy); let even_odd = (fill.tile & 1u) != 0u;
let tile = Tile(fill.backdrop, segments);
area = fill_path(tile, xy, even_odd);
cmd_ix += 3u; cmd_ix += 3u;
} }
// CMD_STROKE // CMD_STROKE

View file

@ -126,7 +126,7 @@ impl<'a> SceneBuilder<'a> {
/// Fills a shape using the specified style and brush. /// Fills a shape using the specified style and brush.
pub fn fill<'b>( pub fn fill<'b>(
&mut self, &mut self,
_style: Fill, style: Fill,
transform: Affine, transform: Affine,
brush: impl Into<BrushRef<'b>>, brush: impl Into<BrushRef<'b>>,
brush_transform: Option<Affine>, brush_transform: Option<Affine>,
@ -134,7 +134,10 @@ impl<'a> SceneBuilder<'a> {
) { ) {
self.scene self.scene
.encode_transform(Transform::from_kurbo(&transform)); .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 self.scene.encode_shape(shape, true) {
if let Some(brush_transform) = brush_transform { if let Some(brush_transform) = brush_transform {
self.scene self.scene