From aac53ad0b548b818490c67b1dc964c01a8f0a392 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 20 Oct 2021 09:59:07 +0200 Subject: [PATCH] gpu: don't alias stroked paths with outline paths The default renderer caches pre-processed paths for efficient re-use, but failed to distinguish outline paths from stroke paths. Fixes gio#294 Signed-off-by: Elias Naur --- gpu/gpu.go | 15 ++++++------ gpu/internal/rendertest/clip_test.go | 22 ++++++++++++++++++ .../rendertest/refs/TestPathReuse.png | Bin 0 -> 1278 bytes 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 gpu/internal/rendertest/refs/TestPathReuse.png diff --git a/gpu/gpu.go b/gpu/gpu.go index 0ca6b689..a01eb1ff 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -149,6 +149,8 @@ type quadsOp struct { } type opKey struct { + outline bool + strokeWidth float32 sx, hx, sy, hy float32 ops.Key } @@ -904,9 +906,8 @@ func (k opKey) SetTransform(t f32.Affine2D) opKey { func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) { var ( - quads quadsOp - strWidth float32 - state drawState + quads quadsOp + state drawState ) reset := func() { state = drawState{ @@ -931,7 +932,7 @@ loop: d.transStack = d.transStack[:n-1] case ops.TypeStroke: - strWidth = decodeStrokeOp(encOp.Data) + quads.key.strokeWidth = decodeStrokeOp(encOp.Data) case ops.TypePath: encOp, ok = r.Decode() @@ -939,11 +940,12 @@ loop: break loop } quads.aux = encOp.Data[ops.TypeAuxLen:] - quads.key = opKey{Key: encOp.Key} + quads.key.Key = encOp.Key case ops.TypeClip: var op clipOp op.decode(encOp.Data) + quads.key.outline = op.outline bounds := op.bounds trans, off := splitTransform(state.t) if len(quads.aux) > 0 { @@ -957,7 +959,7 @@ loop: op.bounds = v.bounds } else { pathData, bounds := d.buildVerts( - quads.aux, trans, op.outline, strWidth, + quads.aux, trans, quads.key.outline, quads.key.strokeWidth, ) op.bounds = bounds quads.aux = pathData @@ -971,7 +973,6 @@ loop: } d.addClipPath(&state, quads.aux, quads.key, op.bounds, off, op.push) quads = quadsOp{} - strWidth = 0 case ops.TypePopClip: for { push := state.cpath.push diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index dbee98fc..22703617 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -4,6 +4,7 @@ package rendertest import ( "image" + "image/color" "math" "testing" @@ -183,3 +184,24 @@ func TestStrokedPathZeroWidth(t *testing.T) { r.expect(65, 50, transparent) }) } + +func TestPathReuse(t *testing.T) { + run(t, func(o *op.Ops) { + var path clip.Path + path.Begin(o) + path.MoveTo(f32.Pt(60, 10)) + path.LineTo(f32.Pt(110, 75)) + path.LineTo(f32.Pt(10, 75)) + path.Close() + spec := path.End() + + outline := clip.Outline{Path: spec}.Op().Push(o) + paint.Fill(o, color.NRGBA{R: 0xFF, A: 0xFF}) + outline.Pop() + + stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o) + paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF}) + stroke.Pop() + }, func(r result) { + }) +} diff --git a/gpu/internal/rendertest/refs/TestPathReuse.png b/gpu/internal/rendertest/refs/TestPathReuse.png new file mode 100644 index 0000000000000000000000000000000000000000..7a178e546633c6c327a1840e19faeb96c2e368e6 GIT binary patch literal 1278 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU^(jP;uumf=gqAB-7%pOZTIhT zF4=U6BkY=<*`n6g1oe%AuXS6t_CJbO_$}VydMiW9Q+e8?1pR1Er5hbbo6I7)eIst1 zeYfY+-I=rR+?_jjr}6jWKhOQV$Idr%Z}Iz?);0XbH*IdVarClq^kE=B1xCN^jPK4~ z%UtkX_gR6w!t-qVuXj5FAHF^I;dQC?*VxWLi+b4y7nvCQ($04_S?=Tfut4tD?TTaN zAJ%9dJorW?P-9Ot|8L$oANAjapO!OM*)jW<-cf~u?LQ~}H?C7KTsh5l-5q#+*&~z_&1yqQj*Vm!DNI7iH*jtJ|UXiAS43wAW^X&L9u*x>EOE9FjKnnm}o91Rpmd~o7%Jj1a*H)r>GHY^EeGECf_oOEwUxR|l##N^2g3m09Cn5eGL zD4{IXD_QwTxFOYj@kV9;c}yG5WK2=|^hDbs@uEiPlat{L(}13KowtWILGq$T>XVc4 z49{eHgWTsCv79iGs_d{yk+ESIBHmRHkY!(HIR?fR-KEv$O6!im5 z8DFjhntT$g^b$Ex3ZhSmG}tY^_+|62v_CHMidY{U%1ANxXu5tPcpJkP!Co8pd7oG% zjHGhc+AZZ0wrBdW!0wZ5-9g2KVoc;cvF? zY3~#@vH!N3f8U;c%>EiiNP1{ literal 0 HcmV?d00001