add unscaled strokes
diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs index 23ea373..d3aa9d4 100644 --- a/examples/scenes/src/test_scenes.rs +++ b/examples/scenes/src/test_scenes.rs
@@ -82,6 +82,13 @@ None, &only_movetos, ); + sb.fill( + Fill::NonZero, + Affine::translate((100.0, 100.0)), + Color::rgb8(0, 0, 255), + None, + &missing_movetos, + ); sb.stroke( &Stroke::new(8.0), Affine::translate((100.0, 100.0)), @@ -89,6 +96,20 @@ None, &missing_movetos, ); + sb.fill( + Fill::NonZero, + Affine::translate((300.0, 100.0)), + Color::rgb8(0, 0, 255), + None, + &missing_movetos, + ); + sb.stroke( + &Stroke::new(8.0).with_scale(false), + Affine::translate((300.0, 100.0)), + Color::rgb8(0, 255, 255), + None, + &missing_movetos, + ); } fn cardioid_and_friends(sb: &mut SceneBuilder, _: &mut SceneParams) {
diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl index 58fe409..c5e61b8 100644 --- a/shader/coarse.wgsl +++ b/shader/coarse.wgsl
@@ -82,11 +82,22 @@ } } -fn write_path(tile: Tile, linewidth: f32) -> bool { +fn is_stroke(linewidth: u32) -> bool { + return (linewidth & 0x7f800000u) != 0x7f800000u; +} + +fn write_path(tile: Tile, linewidth: u32) -> bool { // TODO: take flags alloc_cmd(3u); - if linewidth < 0.0 { - let even_odd = linewidth < -1.0; + let linewidth_f32 = bitcast<f32>(linewidth); + if is_stroke(linewidth) { + let stroke = CmdStroke(tile.segments, 0.5 * linewidth_f32); + ptcl[cmd_offset] = CMD_STROKE; + ptcl[cmd_offset + 1u] = stroke.tile; + ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width); + cmd_offset += 3u; + } else { + let even_odd = linewidth == 0x7f800000u; if tile.segments != 0u { let fill = CmdFill(tile.segments, tile.backdrop); ptcl[cmd_offset] = CMD_FILL; @@ -101,12 +112,6 @@ ptcl[cmd_offset] = CMD_SOLID; cmd_offset += 1u; } - } else { - let stroke = CmdStroke(tile.segments, 0.5 * linewidth); - ptcl[cmd_offset] = CMD_STROKE; - ptcl[cmd_offset + 1u] = stroke.tile; - ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width); - cmd_offset += 3u; } return true; } @@ -360,7 +365,7 @@ switch drawtag { // DRAWTAG_FILL_COLOR case 0x44u: { - let linewidth = bitcast<f32>(info_bin_data[di]); + let linewidth = info_bin_data[di]; if write_path(tile, linewidth) { let rgba_color = scene[dd]; write_color(CmdColor(rgba_color)); @@ -368,7 +373,7 @@ } // DRAWTAG_FILL_LIN_GRADIENT case 0x114u: { - let linewidth = bitcast<f32>(info_bin_data[di]); + let linewidth = info_bin_data[di]; if write_path(tile, linewidth) { let index = scene[dd]; let info_offset = di + 1u; @@ -377,7 +382,7 @@ } // DRAWTAG_FILL_RAD_GRADIENT case 0x2dcu: { - let linewidth = bitcast<f32>(info_bin_data[di]); + let linewidth = info_bin_data[di]; if write_path(tile, linewidth) { let index = scene[dd]; let info_offset = di + 1u; @@ -386,7 +391,7 @@ } // DRAWTAG_FILL_IMAGE case 0x248u: { - let linewidth = bitcast<f32>(info_bin_data[di]); + let linewidth = info_bin_data[di]; if write_path(tile, linewidth) { write_image(di + 1u); } @@ -405,7 +410,7 @@ // DRAWTAG_END_CLIP case 0x21u: { clip_depth -= 1u; - write_path(tile, -1.0); + write_path(tile, 0x7fc00000u); let blend = scene[dd]; let alpha = bitcast<f32>(scene[dd + 1u]); write_end_clip(CmdEndClip(blend, alpha));
diff --git a/shader/draw_leaf.wgsl b/shader/draw_leaf.wgsl index b149d27..5c7aa15 100644 --- a/shader/draw_leaf.wgsl +++ b/shader/draw_leaf.wgsl
@@ -49,6 +49,10 @@ return Transform(matrx, translate); } +fn is_stroke(linewidth: u32) -> bool { + return (linewidth & 0x7f800000u) != 0x7f800000u; +} + var<workgroup> sh_scratch: array<DrawMonoid, WG_SIZE>; @compute @workgroup_size(256) @@ -109,20 +113,26 @@ // let x1 = f32(bbox.x1); // let y1 = f32(bbox.y1); // let bbox_f = vec4(x0, y0, x1, y1); - let fill_mode = u32(bbox.linewidth >= 0.0); + // let fill_mode = u32(bbox.linewidth >= 0.0); var matrx: vec4<f32>; var translate: vec2<f32>; - var linewidth = bbox.linewidth; - if linewidth >= 0.0 || tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT || + let linewidth_u32 = bbox.linewidth; + var linewidth = bitcast<f32>(linewidth_u32); + let stroked = is_stroke(linewidth_u32); + if stroked || tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT || tag_word == DRAWTAG_FILL_IMAGE { let transform = read_transform(config.transform_base, bbox.trans_ix); matrx = transform.matrx; translate = transform.translate; - } - if linewidth >= 0.0 { - // Note: doesn't deal with anisotropic case - linewidth *= sqrt(abs(matrx.x * matrx.w - matrx.y * matrx.z)); + if stroked { + if linewidth >= 0.0 { + // Note: doesn't deal with anisotropic case + linewidth *= sqrt(abs(matrx.x * matrx.w - matrx.y * matrx.z)); + } else { + linewidth = -linewidth; + } + } } switch tag_word { // DRAWTAG_FILL_COLOR
diff --git a/shader/pathseg.wgsl b/shader/pathseg.wgsl index ec059ab..2ca59d5 100644 --- a/shader/pathseg.wgsl +++ b/shader/pathseg.wgsl
@@ -29,7 +29,7 @@ y0: atomic<i32>, x1: atomic<i32>, y1: atomic<i32>, - linewidth: f32, + linewidth: u32, trans_ix: u32, } @@ -115,6 +115,10 @@ return i32(ceil(x)); } +fn is_stroke(linewidth: u32) -> bool { + return (linewidth & 0x7f800000u) != 0x7f800000u; +} + @compute @workgroup_size(256) fn main( @builtin(global_invocation_id) global_id: vec3<u32>, @@ -129,7 +133,7 @@ var tag_byte = (tag_word >> shift) & 0xffu; let out = &path_bboxes[tm.path_ix]; - let linewidth = bitcast<f32>(scene[config.linewidth_base + tm.linewidth_ix]); + let linewidth = scene[config.linewidth_base + tm.linewidth_ix]; if (tag_byte & PATH_TAG_PATH) != 0u { (*out).linewidth = linewidth; (*out).trans_ix = tm.trans_ix; @@ -182,14 +186,20 @@ } } var stroke = vec2(0.0, 0.0); - if linewidth >= 0.0 { - // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm - // This is the correct bounding box, but we're not handling rendering - // in the isotropic case, so it may mismatch. - stroke = 0.5 * linewidth * vec2(length(transform.matrx.xz), length(transform.matrx.yw)); + var flags = 0u; + if is_stroke(linewidth) { + let linewidth_f32 = bitcast<f32>(linewidth); + if linewidth_f32 >= 0.0 { + // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm + // This is the correct bounding box, but we're not handling rendering + // in the isotropic case, so it may mismatch. + stroke = 0.5 * linewidth_f32 * vec2(length(transform.matrx.xz), length(transform.matrx.yw)); + } else { + stroke = 0.5 * linewidth_f32 * vec2(-1.0); + } bbox += vec4(-stroke, stroke); + flags = 1u; } - let flags = u32(linewidth >= 0.0); cubics[global_id.x] = Cubic(p0, p1, p2, p3, stroke, tm.path_ix, flags); // Update bounding box using atomics only. Computing a monoid is a // potential future optimization.
diff --git a/shader/shared/bbox.wgsl b/shader/shared/bbox.wgsl index 714602d..52142b9 100644 --- a/shader/shared/bbox.wgsl +++ b/shader/shared/bbox.wgsl
@@ -9,7 +9,7 @@ y0: i32, x1: i32, y1: i32, - linewidth: f32, + linewidth: u32, trans_ix: u32, }
diff --git a/src/encoding/encoding.rs b/src/encoding/encoding.rs index 03ca730..7d662da 100644 --- a/src/encoding/encoding.rs +++ b/src/encoding/encoding.rs
@@ -89,7 +89,7 @@ self.color_stops.clear(); if !is_fragment { self.transforms.push(Transform::IDENTITY); - self.linewidths.push(-1.0); + self.linewidths.push(f32::INFINITY); } } @@ -181,6 +181,22 @@ } } + pub fn encode_fill(&mut self, even_odd: bool) { + if even_odd { + self.encode_linewidth(f32::from_bits(0x7f800000)); + } else { + self.encode_linewidth(f32::from_bits(0x7fc00000)); + } + } + + pub fn encode_stroke(&mut self, width: f32, scaled: bool) { + if scaled { + self.encode_linewidth(width); + } else { + self.encode_linewidth(-width); + } + } + /// Encodes a transform. /// /// If the given transform is different from the current one, encodes it and
diff --git a/src/encoding/glyph_cache.rs b/src/encoding/glyph_cache.rs index f919afb..feb124b 100644 --- a/src/encoding/glyph_cache.rs +++ b/src/encoding/glyph_cache.rs
@@ -55,9 +55,9 @@ let mut encode_glyph = || { let start = encoding_cache.stream_offsets(); match style { - Style::Fill(Fill::NonZero) => encoding_cache.encode_linewidth(-1.0), - Style::Fill(Fill::EvenOdd) => encoding_cache.encode_linewidth(-2.0), - Style::Stroke(stroke) => encoding_cache.encode_linewidth(stroke.width), + Style::Fill(Fill::NonZero) => encoding_cache.encode_fill(false), + Style::Fill(Fill::EvenOdd) => encoding_cache.encode_fill(true), + Style::Stroke(stroke) => encoding_cache.encode_stroke(stroke.width, stroke.scale), } let mut path = crate::glyph::PathEncoderPen(encoding_cache.encode_path(is_fill)); scaler
diff --git a/src/glyph.rs b/src/glyph.rs index b398e6f..016e7cd 100644 --- a/src/glyph.rs +++ b/src/glyph.rs
@@ -101,9 +101,9 @@ pub fn encode_glyph(&mut self, gid: u16, style: &Style, encoding: &mut Encoding) -> Option<()> { match style { - Style::Fill(Fill::NonZero) => encoding.encode_linewidth(-1.0), - Style::Fill(Fill::EvenOdd) => encoding.encode_linewidth(-2.0), - Style::Stroke(stroke) => encoding.encode_linewidth(stroke.width), + Style::Fill(Fill::NonZero) => encoding.encode_fill(false), + Style::Fill(Fill::EvenOdd) => encoding.encode_fill(true), + Style::Stroke(stroke) => encoding.encode_stroke(stroke.width, stroke.scale), } let mut path = PathEncoderPen(encoding.encode_path(matches!(style, Style::Fill(_)))); self.scaler.outline(GlyphId::new(gid), &mut path).ok()?;
diff --git a/src/scene.rs b/src/scene.rs index ce777b8..eef9fc7 100644 --- a/src/scene.rs +++ b/src/scene.rs
@@ -101,7 +101,7 @@ let blend = blend.into(); self.scene .encode_transform(Transform::from_kurbo(&transform)); - self.scene.encode_linewidth(-1.0); + self.scene.encode_fill(false); if !self.scene.encode_shape(shape, true) { // If the layer shape is invalid, encode a valid empty path. This suppresses // all drawing until the layer is popped. @@ -127,10 +127,7 @@ ) { self.scene .encode_transform(Transform::from_kurbo(&transform)); - self.scene.encode_linewidth(match style { - Fill::NonZero => -1.0, - Fill::EvenOdd => -2.0, - }); + self.scene.encode_fill(style == Fill::EvenOdd); if self.scene.encode_shape(shape, true) { if let Some(brush_transform) = brush_transform { if self @@ -155,7 +152,7 @@ ) { self.scene .encode_transform(Transform::from_kurbo(&transform)); - self.scene.encode_linewidth(style.width); + self.scene.encode_stroke(style.width, style.scale); if self.scene.encode_shape(shape, false) { if let Some(brush_transform) = brush_transform { if self