Support even-odd fill rule

Add logic for handling the even-odd fill rule to `SceneBuilder` and the coarse and fine shaders.
This commit is contained in:
Chad Brokaw 2023-01-10 15:22:04 -05:00
parent c57db1d25b
commit c6ac5bf590
4 changed files with 66 additions and 21 deletions

View file

@ -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)]

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
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<u32>(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<f32>(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<f32>(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<f32>(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: {

View file

@ -97,7 +97,7 @@ var output: texture_storage_2d<r8, write>;
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>;
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<f32>) -> array<f32, PIXELS_PER_THREAD> {
}
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

View file

@ -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<BrushRef<'b>>,
brush_transform: Option<Affine>,
@ -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