From 2ce9ad36af34e5001b9cdbb9472402dda5e1a2ba Mon Sep 17 00:00:00 2001 From: Pierre Curto Date: Sat, 15 Jan 2022 18:52:51 +0100 Subject: [PATCH] clip: check Path ops are not mixed with others Multiple operations Op, such as clip.Path, cannot be interleaved with other ops. This patch adds a mechanism to ensure that is the case by starting multi ops with ops.BeginMulti and ending them with ops.EndMulti while operations are written to op.Ops with ops.WriteMulti. This mechanism is applied to clip.Path. Fixes: https://todo.sr.ht/~eliasnaur/gio/336 Signed-off-by: Pierre Curto --- gpu/internal/rendertest/clip_test.go | 24 ++++++++++++++++++++++++ internal/ops/ops.go | 27 +++++++++++++++++++++++++++ op/clip/clip.go | 12 +++++++----- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index 0820639f..4275a7cf 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -227,3 +227,27 @@ func TestPathReuse(t *testing.T) { }, func(r result) { }) } + +func TestPathInterleave(t *testing.T) { + t.Run("interleave op in clip.Path", func(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Error("expected panic did not occur") + } + }() + ops := new(op.Ops) + var path clip.Path + path.Begin(ops) + path.LineTo(f32.Point{X: 123, Y: 456}) + paint.ColorOp{}.Add(ops) + path.End() + }) + t.Run("use ops after clip.Path", func(t *testing.T) { + ops := new(op.Ops) + var path clip.Path + path.Begin(ops) + path.LineTo(f32.Point{X: 123, Y: 456}) + path.End() + paint.ColorOp{}.Add(ops) + }) +} diff --git a/internal/ops/ops.go b/internal/ops/ops.go index fece6d6a..ce076947 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -22,6 +22,8 @@ type Ops struct { // nextStateID is the id allocated for the next // StateOp. nextStateID int + // multipOp indicates a multi-op such as clip.Path is being added. + multipOp bool macroStack stack stacks [5]stack @@ -192,6 +194,31 @@ func Reset(o *Ops) { } func Write(o *Ops, n int) []byte { + if o.multipOp { + panic("cannot mix multi ops with single ones") + } + o.data = append(o.data, make([]byte, n)...) + return o.data[len(o.data)-n:] +} + +func BeginMulti(o *Ops) { + if o.multipOp { + panic("cannot interleave multi ops") + } + o.multipOp = true +} + +func EndMulti(o *Ops) { + if !o.multipOp { + panic("cannot end non multi ops") + } + o.multipOp = false +} + +func WriteMulti(o *Ops, n int) []byte { + if !o.multipOp { + panic("cannot use multi ops in single ops") + } o.data = append(o.data, make([]byte, n)...) return o.data[len(o.data)-n:] } diff --git a/op/clip/clip.go b/op/clip/clip.go index 5b4dad09..c94e1a0b 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -126,7 +126,8 @@ func (p *Path) Begin(o *op.Ops) { contour: 1, } p.hash.SetSeed(pathSeed) - data := ops.Write(p.ops, ops.TypeAuxLen) + ops.BeginMulti(p.ops) + data := ops.WriteMulti(p.ops, ops.TypeAuxLen) data[0] = byte(ops.TypeAux) } @@ -134,6 +135,7 @@ func (p *Path) Begin(o *op.Ops) { func (p *Path) End() PathSpec { p.gap() c := p.macro.Stop() + ops.EndMulti(p.ops) return PathSpec{ spec: c, hasSegments: p.hasSegments, @@ -163,7 +165,7 @@ func (p *Path) gap() { if p.pen != p.start { // A closed contour starts and ends in the same point. // This move creates a gap in the contour, register it. - data := ops.Write(p.ops, scene.CommandSize+4) + data := ops.WriteMulti(p.ops, scene.CommandSize+4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) p.cmd(data[4:], scene.Gap(p.pen, p.start)) @@ -183,7 +185,7 @@ func (p *Path) Line(delta f32.Point) { // LineTo moves the pen to the absolute point specified, recording a line. func (p *Path) LineTo(to f32.Point) { - data := ops.Write(p.ops, scene.CommandSize+4) + data := ops.WriteMulti(p.ops, scene.CommandSize+4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) p.cmd(data[4:], scene.Line(p.pen, to)) @@ -251,7 +253,7 @@ func (p *Path) Quad(ctrl, to f32.Point) { // QuadTo records a quadratic Bézier from the pen to end // with the control point ctrl, with absolute coordinates. func (p *Path) QuadTo(ctrl, to f32.Point) { - data := ops.Write(p.ops, scene.CommandSize+4) + data := ops.WriteMulti(p.ops, scene.CommandSize+4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) p.cmd(data[4:], scene.Quad(p.pen, ctrl, to)) @@ -297,7 +299,7 @@ func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) { if ctrl0 == p.pen && ctrl1 == p.pen && to == p.pen { return } - data := ops.Write(p.ops, scene.CommandSize+4) + data := ops.WriteMulti(p.ops, scene.CommandSize+4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))