use tag flags for fill vs stroke modes in scene elements

Encode stroke vs fill as tag flags, thereby reducing the number of scene
elements. Encoding change only, no functional changes.

The previous Stroke and Fill commands are merged to one command,
FillColor. The encoding to annotated element is divergent, which is
fixed when annotated elements move to tag flags.

Updates #70

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur 2021-03-17 11:08:28 +01:00
parent a5b6bda941
commit e9ff509ab9
6 changed files with 96 additions and 137 deletions

View file

@ -1,7 +1,7 @@
use piet_gpu_derive::piet_gpu; use piet_gpu_derive::piet_gpu;
pub use self::scene::{ pub use self::scene::{
Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform,
}; };
piet_gpu! { piet_gpu! {
@ -22,16 +22,13 @@ piet_gpu! {
p2: [f32; 2], p2: [f32; 2],
p3: [f32; 2], p3: [f32; 2],
} }
struct Fill { struct FillColor {
rgba_color: u32, rgba_color: u32,
} }
struct FillImage { struct FillImage {
index: u32, index: u32,
offset: [i16; 2], offset: [i16; 2],
} }
struct Stroke {
rgba_color: u32,
}
struct SetLineWidth { struct SetLineWidth {
width: f32, width: f32,
} }
@ -45,19 +42,12 @@ piet_gpu! {
} }
enum Element { enum Element {
Nop, Nop,
// Another approach to encoding would be to use a single
// variant but have a bool for fill/stroke. This could be
// packed into the tag, so the on-the-wire representation
// would be very similar to what's here.
StrokeLine(LineSeg),
FillLine(LineSeg),
StrokeQuad(QuadSeg), Line(TagFlags, LineSeg),
FillQuad(QuadSeg), Quad(TagFlags, QuadSeg),
StrokeCubic(CubicSeg), Cubic(TagFlags, CubicSeg),
FillCubic(CubicSeg),
Stroke(Stroke), FillColor(TagFlags, FillColor),
Fill(Fill),
SetLineWidth(SetLineWidth), SetLineWidth(SetLineWidth),
Transform(Transform), Transform(Transform),
BeginClip(Clip), BeginClip(Clip),

View file

@ -110,30 +110,26 @@ State map_element(ElementRef ref) {
c.pathseg_count = 0; c.pathseg_count = 0;
c.trans_count = 0; c.trans_count = 0;
switch (tag) { switch (tag) {
case Element_FillLine: case Element_Line:
case Element_StrokeLine: LineSeg line = Element_Line_read(ref);
LineSeg line = Element_FillLine_read(ref);
c.bbox.xy = min(line.p0, line.p1); c.bbox.xy = min(line.p0, line.p1);
c.bbox.zw = max(line.p0, line.p1); c.bbox.zw = max(line.p0, line.p1);
c.pathseg_count = 1; c.pathseg_count = 1;
break; break;
case Element_FillQuad: case Element_Quad:
case Element_StrokeQuad: QuadSeg quad = Element_Quad_read(ref);
QuadSeg quad = Element_FillQuad_read(ref);
c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2); c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2);
c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2); c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2);
c.pathseg_count = 1; c.pathseg_count = 1;
break; break;
case Element_FillCubic: case Element_Cubic:
case Element_StrokeCubic: CubicSeg cubic = Element_Cubic_read(ref);
CubicSeg cubic = Element_FillCubic_read(ref);
c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3)); c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3));
c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3)); c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3));
c.pathseg_count = 1; c.pathseg_count = 1;
break; break;
case Element_Fill: case Element_FillColor:
case Element_FillImage: case Element_FillImage:
case Element_Stroke:
case Element_BeginClip: case Element_BeginClip:
c.flags = FLAG_RESET_BBOX; c.flags = FLAG_RESET_BBOX;
c.path_count = 1; c.path_count = 1;
@ -291,11 +287,11 @@ void main() {
// gains to be had from stashing in shared memory or possibly // gains to be had from stashing in shared memory or possibly
// registers (though register pressure is an issue). // registers (though register pressure is an issue).
ElementRef this_ref = Element_index(ref, i); ElementRef this_ref = Element_index(ref, i);
uint tag = Element_tag(this_ref).tag; ElementTag tag = Element_tag(this_ref);
switch (tag) { bool is_stroke = fill_mode_from_flags(tag.flags) == MODE_STROKE;
case Element_FillLine: switch (tag.tag) {
case Element_StrokeLine: case Element_Line:
LineSeg line = Element_StrokeLine_read(this_ref); LineSeg line = Element_Line_read(this_ref);
PathStrokeCubic path_cubic; PathStrokeCubic path_cubic;
path_cubic.p0 = line.p0; path_cubic.p0 = line.p0;
path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0); path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0);
@ -303,7 +299,7 @@ void main() {
path_cubic.p3 = line.p1; path_cubic.p3 = line.p1;
path_cubic.path_ix = st.path_count; path_cubic.path_ix = st.path_count;
path_cubic.trans_ix = st.trans_count; path_cubic.trans_ix = st.trans_count;
if (tag == Element_StrokeLine) { if (is_stroke) {
path_cubic.stroke = get_linewidth(st); path_cubic.stroke = get_linewidth(st);
} else { } else {
path_cubic.stroke = vec2(0.0); path_cubic.stroke = vec2(0.0);
@ -311,20 +307,19 @@ void main() {
// We do encoding a bit by hand to minimize divergence. Another approach // We do encoding a bit by hand to minimize divergence. Another approach
// would be to have a fill/stroke bool. // would be to have a fill/stroke bool.
PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
uint out_tag = tag == Element_FillLine ? PathSeg_FillCubic : PathSeg_StrokeCubic; uint out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic;
write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag);
PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic);
break; break;
case Element_FillQuad: case Element_Quad:
case Element_StrokeQuad: QuadSeg quad = Element_Quad_read(this_ref);
QuadSeg quad = Element_StrokeQuad_read(this_ref);
path_cubic.p0 = quad.p0; path_cubic.p0 = quad.p0;
path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0); path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0);
path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0); path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0);
path_cubic.p3 = quad.p2; path_cubic.p3 = quad.p2;
path_cubic.path_ix = st.path_count; path_cubic.path_ix = st.path_count;
path_cubic.trans_ix = st.trans_count; path_cubic.trans_ix = st.trans_count;
if (tag == Element_StrokeQuad) { if (is_stroke) {
path_cubic.stroke = get_linewidth(st); path_cubic.stroke = get_linewidth(st);
} else { } else {
path_cubic.stroke = vec2(0.0); path_cubic.stroke = vec2(0.0);
@ -332,20 +327,19 @@ void main() {
// We do encoding a bit by hand to minimize divergence. Another approach // We do encoding a bit by hand to minimize divergence. Another approach
// would be to have a fill/stroke bool. // would be to have a fill/stroke bool.
path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
out_tag = tag == Element_FillQuad ? PathSeg_FillCubic : PathSeg_StrokeCubic; out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic;
write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag);
PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic);
break; break;
case Element_FillCubic: case Element_Cubic:
case Element_StrokeCubic: CubicSeg cubic = Element_Cubic_read(this_ref);
CubicSeg cubic = Element_StrokeCubic_read(this_ref);
path_cubic.p0 = cubic.p0; path_cubic.p0 = cubic.p0;
path_cubic.p1 = cubic.p1; path_cubic.p1 = cubic.p1;
path_cubic.p2 = cubic.p2; path_cubic.p2 = cubic.p2;
path_cubic.p3 = cubic.p3; path_cubic.p3 = cubic.p3;
path_cubic.path_ix = st.path_count; path_cubic.path_ix = st.path_count;
path_cubic.trans_ix = st.trans_count; path_cubic.trans_ix = st.trans_count;
if (tag == Element_StrokeCubic) { if (is_stroke) {
path_cubic.stroke = get_linewidth(st); path_cubic.stroke = get_linewidth(st);
} else { } else {
path_cubic.stroke = vec2(0.0); path_cubic.stroke = vec2(0.0);
@ -353,27 +347,28 @@ void main() {
// We do encoding a bit by hand to minimize divergence. Another approach // We do encoding a bit by hand to minimize divergence. Another approach
// would be to have a fill/stroke bool. // would be to have a fill/stroke bool.
path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size);
out_tag = tag == Element_FillCubic ? PathSeg_FillCubic : PathSeg_StrokeCubic; out_tag = !is_stroke ? PathSeg_FillCubic : PathSeg_StrokeCubic;
write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag); write_mem(conf.pathseg_alloc, path_out_ref.offset >> 2, out_tag);
PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic); PathStrokeCubic_write(conf.pathseg_alloc, PathStrokeCubicRef(path_out_ref.offset + 4), path_cubic);
break; break;
case Element_Stroke: case Element_FillColor:
Stroke stroke = Element_Stroke_read(this_ref); FillColor fill = Element_FillColor_read(this_ref);
// TODO: merge paths when annotations use tag flags.
if (is_stroke) {
AnnoStroke anno_stroke; AnnoStroke anno_stroke;
anno_stroke.rgba_color = stroke.rgba_color; anno_stroke.rgba_color = fill.rgba_color;
vec2 lw = get_linewidth(st); vec2 lw = get_linewidth(st);
anno_stroke.bbox = st.bbox + vec4(-lw, lw); anno_stroke.bbox = st.bbox + vec4(-lw, lw);
anno_stroke.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); anno_stroke.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
Annotated_Stroke_write(conf.anno_alloc, out_ref, anno_stroke); Annotated_Stroke_write(conf.anno_alloc, out_ref, anno_stroke);
break; } else {
case Element_Fill:
Fill fill = Element_Fill_read(this_ref);
AnnoFill anno_fill; AnnoFill anno_fill;
anno_fill.rgba_color = fill.rgba_color; anno_fill.rgba_color = fill.rgba_color;
anno_fill.bbox = st.bbox; anno_fill.bbox = st.bbox;
out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
Annotated_Fill_write(conf.anno_alloc, out_ref, anno_fill); Annotated_Fill_write(conf.anno_alloc, out_ref, anno_fill);
}
break; break;
case Element_FillImage: case Element_FillImage:
FillImage fill_img = Element_FillImage_read(this_ref); FillImage fill_img = Element_FillImage_read(this_ref);
@ -381,7 +376,7 @@ void main() {
anno_fill_img.index = fill_img.index; anno_fill_img.index = fill_img.index;
anno_fill_img.offset = fill_img.offset; anno_fill_img.offset = fill_img.offset;
anno_fill_img.bbox = st.bbox; anno_fill_img.bbox = st.bbox;
out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
Annotated_FillImage_write(conf.anno_alloc, out_ref, anno_fill_img); Annotated_FillImage_write(conf.anno_alloc, out_ref, anno_fill_img);
break; break;
case Element_BeginClip: case Element_BeginClip:

Binary file not shown.

View file

@ -14,7 +14,7 @@ struct CubicSegRef {
uint offset; uint offset;
}; };
struct FillRef { struct FillColorRef {
uint offset; uint offset;
}; };
@ -22,10 +22,6 @@ struct FillImageRef {
uint offset; uint offset;
}; };
struct StrokeRef {
uint offset;
};
struct SetLineWidthRef { struct SetLineWidthRef {
uint offset; uint offset;
}; };
@ -78,14 +74,14 @@ CubicSegRef CubicSeg_index(CubicSegRef ref, uint index) {
return CubicSegRef(ref.offset + index * CubicSeg_size); return CubicSegRef(ref.offset + index * CubicSeg_size);
} }
struct Fill { struct FillColor {
uint rgba_color; uint rgba_color;
}; };
#define Fill_size 4 #define FillColor_size 4
FillRef Fill_index(FillRef ref, uint index) { FillColorRef FillColor_index(FillColorRef ref, uint index) {
return FillRef(ref.offset + index * Fill_size); return FillColorRef(ref.offset + index * FillColor_size);
} }
struct FillImage { struct FillImage {
@ -99,16 +95,6 @@ FillImageRef FillImage_index(FillImageRef ref, uint index) {
return FillImageRef(ref.offset + index * FillImage_size); return FillImageRef(ref.offset + index * FillImage_size);
} }
struct Stroke {
uint rgba_color;
};
#define Stroke_size 4
StrokeRef Stroke_index(StrokeRef ref, uint index) {
return StrokeRef(ref.offset + index * Stroke_size);
}
struct SetLineWidth { struct SetLineWidth {
float width; float width;
}; };
@ -141,19 +127,15 @@ ClipRef Clip_index(ClipRef ref, uint index) {
} }
#define Element_Nop 0 #define Element_Nop 0
#define Element_StrokeLine 1 #define Element_Line 1
#define Element_FillLine 2 #define Element_Quad 2
#define Element_StrokeQuad 3 #define Element_Cubic 3
#define Element_FillQuad 4 #define Element_FillColor 4
#define Element_StrokeCubic 5 #define Element_SetLineWidth 5
#define Element_FillCubic 6 #define Element_Transform 6
#define Element_Stroke 7 #define Element_BeginClip 7
#define Element_Fill 8 #define Element_EndClip 8
#define Element_SetLineWidth 9 #define Element_FillImage 9
#define Element_Transform 10
#define Element_BeginClip 11
#define Element_EndClip 12
#define Element_FillImage 13
#define Element_size 36 #define Element_size 36
ElementRef Element_index(ElementRef ref, uint index) { ElementRef Element_index(ElementRef ref, uint index) {
@ -210,10 +192,10 @@ CubicSeg CubicSeg_read(CubicSegRef ref) {
return s; return s;
} }
Fill Fill_read(FillRef ref) { FillColor FillColor_read(FillColorRef ref) {
uint ix = ref.offset >> 2; uint ix = ref.offset >> 2;
uint raw0 = scene[ix + 0]; uint raw0 = scene[ix + 0];
Fill s; FillColor s;
s.rgba_color = raw0; s.rgba_color = raw0;
return s; return s;
} }
@ -228,14 +210,6 @@ FillImage FillImage_read(FillImageRef ref) {
return s; return s;
} }
Stroke Stroke_read(StrokeRef ref) {
uint ix = ref.offset >> 2;
uint raw0 = scene[ix + 0];
Stroke s;
s.rgba_color = raw0;
return s;
}
SetLineWidth SetLineWidth_read(SetLineWidthRef ref) { SetLineWidth SetLineWidth_read(SetLineWidthRef ref) {
uint ix = ref.offset >> 2; uint ix = ref.offset >> 2;
uint raw0 = scene[ix + 0]; uint raw0 = scene[ix + 0];
@ -274,36 +248,20 @@ ElementTag Element_tag(ElementRef ref) {
return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16); return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16);
} }
LineSeg Element_StrokeLine_read(ElementRef ref) { LineSeg Element_Line_read(ElementRef ref) {
return LineSeg_read(LineSegRef(ref.offset + 4)); return LineSeg_read(LineSegRef(ref.offset + 4));
} }
LineSeg Element_FillLine_read(ElementRef ref) { QuadSeg Element_Quad_read(ElementRef ref) {
return LineSeg_read(LineSegRef(ref.offset + 4));
}
QuadSeg Element_StrokeQuad_read(ElementRef ref) {
return QuadSeg_read(QuadSegRef(ref.offset + 4)); return QuadSeg_read(QuadSegRef(ref.offset + 4));
} }
QuadSeg Element_FillQuad_read(ElementRef ref) { CubicSeg Element_Cubic_read(ElementRef ref) {
return QuadSeg_read(QuadSegRef(ref.offset + 4));
}
CubicSeg Element_StrokeCubic_read(ElementRef ref) {
return CubicSeg_read(CubicSegRef(ref.offset + 4)); return CubicSeg_read(CubicSegRef(ref.offset + 4));
} }
CubicSeg Element_FillCubic_read(ElementRef ref) { FillColor Element_FillColor_read(ElementRef ref) {
return CubicSeg_read(CubicSegRef(ref.offset + 4)); return FillColor_read(FillColorRef(ref.offset + 4));
}
Stroke Element_Stroke_read(ElementRef ref) {
return Stroke_read(StrokeRef(ref.offset + 4));
}
Fill Element_Fill_read(ElementRef ref) {
return Fill_read(FillRef(ref.offset + 4));
} }
SetLineWidth Element_SetLineWidth_read(ElementRef ref) { SetLineWidth Element_SetLineWidth_read(ElementRef ref) {

View file

@ -39,3 +39,12 @@ struct Config {
Alloc anno_alloc; Alloc anno_alloc;
Alloc trans_alloc; Alloc trans_alloc;
}; };
// Fill modes.
#define MODE_NONZERO 0
#define MODE_STROKE 1
// fill_mode_from_flags extracts the fill mode from tag flags.
uint fill_mode_from_flags(uint flags) {
return flags & 0x1;
}

View file

@ -3,7 +3,7 @@ use std::{borrow::Cow, ops::RangeBounds};
use piet_gpu_types::encoder::{Encode, Encoder}; use piet_gpu_types::encoder::{Encode, Encoder};
use piet_gpu_types::scene::{ use piet_gpu_types::scene::{
Clip, CubicSeg, Element, Fill, LineSeg, QuadSeg, SetLineWidth, Stroke, Transform, Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetLineWidth, Transform,
}; };
use piet::{ use piet::{
@ -69,6 +69,13 @@ struct ClipElement {
bbox: Option<Rect>, bbox: Option<Rect>,
} }
enum FillMode {
// Fill path according to the non-zero winding rule.
Nonzero = 0,
// Fill stroked path.
Stroke = 1,
}
const TOLERANCE: f64 = 0.25; const TOLERANCE: f64 = 0.25;
impl PietGpuRenderContext { impl PietGpuRenderContext {
@ -143,8 +150,8 @@ impl RenderContext for PietGpuRenderContext {
self.accumulate_bbox(|| shape.bounding_box() + Insets::uniform(width * 0.5)); self.accumulate_bbox(|| shape.bounding_box() + Insets::uniform(width * 0.5));
let path = shape.path_elements(TOLERANCE); let path = shape.path_elements(TOLERANCE);
self.encode_path(path, false); self.encode_path(path, false);
let stroke = Stroke { rgba_color }; let stroke = FillColor { rgba_color };
self.elements.push(Element::Stroke(stroke)); self.elements.push(Element::FillColor(FillMode::Stroke as u16, stroke));
self.path_count += 1; self.path_count += 1;
} }
_ => (), _ => (),
@ -168,8 +175,8 @@ impl RenderContext for PietGpuRenderContext {
self.accumulate_bbox(|| shape.bounding_box()); self.accumulate_bbox(|| shape.bounding_box());
let path = shape.path_elements(TOLERANCE); let path = shape.path_elements(TOLERANCE);
self.encode_path(path, true); self.encode_path(path, true);
let fill = Fill { rgba_color }; let fill = FillColor { rgba_color };
self.elements.push(Element::Fill(fill)); self.elements.push(Element::FillColor(FillMode::Nonzero as u16, fill));
self.path_count += 1; self.path_count += 1;
} }
} }
@ -286,27 +293,27 @@ impl RenderContext for PietGpuRenderContext {
impl PietGpuRenderContext { impl PietGpuRenderContext {
fn encode_line_seg(&mut self, seg: LineSeg, is_fill: bool) { fn encode_line_seg(&mut self, seg: LineSeg, is_fill: bool) {
if is_fill { if is_fill {
self.elements.push(Element::FillLine(seg)); self.elements.push(Element::Line(FillMode::Nonzero as u16, seg));
} else { } else {
self.elements.push(Element::StrokeLine(seg)); self.elements.push(Element::Line(FillMode::Stroke as u16, seg));
} }
self.pathseg_count += 1; self.pathseg_count += 1;
} }
fn encode_quad_seg(&mut self, seg: QuadSeg, is_fill: bool) { fn encode_quad_seg(&mut self, seg: QuadSeg, is_fill: bool) {
if is_fill { if is_fill {
self.elements.push(Element::FillQuad(seg)); self.elements.push(Element::Quad(FillMode::Nonzero as u16, seg));
} else { } else {
self.elements.push(Element::StrokeQuad(seg)); self.elements.push(Element::Quad(FillMode::Stroke as u16, seg));
} }
self.pathseg_count += 1; self.pathseg_count += 1;
} }
fn encode_cubic_seg(&mut self, seg: CubicSeg, is_fill: bool) { fn encode_cubic_seg(&mut self, seg: CubicSeg, is_fill: bool) {
if is_fill { if is_fill {
self.elements.push(Element::FillCubic(seg)); self.elements.push(Element::Cubic(FillMode::Nonzero as u16, seg));
} else { } else {
self.elements.push(Element::StrokeCubic(seg)); self.elements.push(Element::Cubic(FillMode::Stroke as u16, seg));
} }
self.pathseg_count += 1; self.pathseg_count += 1;
} }