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