op/clip: don't accept open Paths for Outline

Outline represents a clipping operations that clips all drawing outside
a closed path. Before this change, paths not closed we're patched up by
adding an implicit line from the endpoint to the beginning.

These fixups are inefficient for a rare case, but acceptable because the
old renderer post-processes all paths anyway. However, the new compute
renderer don't need post-processing in most cases, making fixups too
expensive.

Given that clipping to an open path is fundamentally undefined and that
implicit fixup with a closing line segment is merely a way to force the
clip to be well-defined, this change adds a panic to Outline for Paths
that are not closed.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-03-11 08:09:07 +01:00
parent 4b377aa896
commit 884e7d27e2
4 changed files with 39 additions and 28 deletions
+15 -6
View File
@@ -65,8 +65,11 @@ func (p Op) Add(o *op.Ops) {
}
type PathSpec struct {
spec op.CallOp
quads uint32 // quads is the number Bézier segments in that path.
spec op.CallOp
// open is true if any path contour is not closed. A closed contour starts
// and ends in the same point.
open bool
quads uint32 // quads is the number Bézier segments in the path.
}
// Path constructs a Op clip path described by lines and
@@ -78,6 +81,7 @@ type PathSpec struct {
// data is stored directly in the Ops list supplied to Begin.
type Path struct {
ops *op.Ops
open bool
contour int
pen f32.Point
macro op.MacroOp
@@ -102,6 +106,7 @@ func (p *Path) End() PathSpec {
c := p.macro.Stop()
return PathSpec{
spec: c,
open: p.open || p.pen != p.start,
quads: p.quads,
}
}
@@ -114,6 +119,7 @@ func (p *Path) Move(delta f32.Point) {
// MoveTo moves the pen to the specified absolute coordinate.
func (p *Path) MoveTo(to f32.Point) {
p.open = p.open || p.pen != p.start
p.end()
p.pen = to
p.start = to
@@ -121,9 +127,6 @@ func (p *Path) MoveTo(to f32.Point) {
// end completes the current contour.
func (p *Path) end() {
if p.pen != p.start {
p.LineTo(p.start)
}
p.contour++
}
@@ -369,8 +372,11 @@ func (p *Path) approxCubeTo(splits int, maxDist float32, ctrl0, ctrl1, to f32.Po
return splits
}
// Close closes the path.
// Close closes the path contour.
func (p *Path) Close() {
if p.pen != p.start {
p.LineTo(p.start)
}
p.end()
}
@@ -382,6 +388,9 @@ type Outline struct {
// Op returns a clip operation representing the outline.
func (o Outline) Op() Op {
if o.Path.open {
panic("not all path contours are closed")
}
return Op{
path: o.Path,
outline: true,
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"testing"
"gioui.org/f32"
"gioui.org/op"
)
func TestOpenPathOutlinePanic(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Error("Outline of an open path didn't panic")
}
}()
var p Path
p.Begin(new(op.Ops))
p.Line(f32.Pt(10, 10))
Outline{Path: p.End()}.Op()
}