From 5ce888f8b94fd591348eb092934bd3d0c8c98d6b Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Thu, 23 May 2019 16:32:03 +0200 Subject: [PATCH] small performance improvements --- backend/backendbase/base.go | 2 +- backend/goglbackend/clip.go | 7 +++++-- backend/goglbackend/fill.go | 26 +++++++++++++++++++------- backend/softwarebackend/fill.go | 2 +- backend/xmobilebackend/clip.go | 7 +++++-- backend/xmobilebackend/fill.go | 26 +++++++++++++++++++------- images.go | 2 +- paths.go | 29 +++++++++++++++++++++-------- shadows.go | 4 ++-- text.go | 2 +- 10 files changed, 75 insertions(+), 32 deletions(-) diff --git a/backend/backendbase/base.go b/backend/backendbase/base.go index 3cb678c..9c83cb5 100644 --- a/backend/backendbase/base.go +++ b/backend/backendbase/base.go @@ -18,7 +18,7 @@ type Backend interface { LoadRadialGradient(data Gradient) RadialGradient Clear(pts [4][2]float64) - Fill(style *FillStyle, pts [][2]float64) + Fill(style *FillStyle, pts [][2]float64, canOverlap bool) DrawImage(dimg Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) FillImageMask(style *FillStyle, mask *image.Alpha, pts [4][2]float64) // pts must have four points diff --git a/backend/goglbackend/clip.go b/backend/goglbackend/clip.go index fd95a82..b38b65e 100644 --- a/backend/goglbackend/clip.go +++ b/backend/goglbackend/clip.go @@ -43,18 +43,21 @@ func (b *GoGLBackend) Clip(pts [][2]float64) { gl.ColorMask(false, false, false, false) + // set bit 2 in the stencil buffer in the given shape gl.StencilMask(0x04) - gl.StencilFunc(gl.ALWAYS, 4, 0x04) + gl.StencilFunc(gl.ALWAYS, 4, 0) gl.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE) gl.DrawArrays(mode, 4, int32(len(pts))) + // on entire screen, where neither bit 1 or 2 are set, invert bit 1 gl.StencilMask(0x02) gl.StencilFunc(gl.EQUAL, 0, 0x06) gl.StencilOp(gl.KEEP, gl.INVERT, gl.INVERT) gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) + // on entire screen, clear bit 2 gl.StencilMask(0x04) - gl.StencilFunc(gl.ALWAYS, 0, 0x04) + gl.StencilFunc(gl.ALWAYS, 0, 0) gl.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) diff --git a/backend/goglbackend/fill.go b/backend/goglbackend/fill.go index 2baba74..01c531a 100644 --- a/backend/goglbackend/fill.go +++ b/backend/goglbackend/fill.go @@ -69,7 +69,19 @@ func (b *GoGLBackend) clearRect(x, y, w, h int) { gl.Disable(gl.SCISSOR_TEST) } -func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { +func extent(pts [][2]float64) (min, max vec) { + min[0] = math.MaxFloat64 + min[1] = math.MaxFloat64 + for _, v := range pts { + min[0] = math.Min(min[0], v[0]) + min[1] = math.Min(min[1], v[1]) + max[0] = math.Max(max[0], v[0]) + max[1] = math.Max(max[1], v[1]) + } + return +} + +func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64, canOverlap bool) { b.activate() if style.Blur > 0 { @@ -80,11 +92,12 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { } b.ptsBuf = b.ptsBuf[:0] + min, max := extent(pts) b.ptsBuf = append(b.ptsBuf, - 0, 0, - 0, float32(b.fh), - float32(b.fw), float32(b.fh), - float32(b.fw), 0) + float32(min[0]), float32(min[1]), + float32(min[0]), float32(max[1]), + float32(max[0]), float32(max[1]), + float32(max[0]), float32(min[1])) for _, pt := range pts { b.ptsBuf = append(b.ptsBuf, float32(pt[0]), float32(pt[1])) } @@ -97,7 +110,7 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { gl.BindBuffer(gl.ARRAY_BUFFER, b.buf) gl.BufferData(gl.ARRAY_BUFFER, len(b.ptsBuf)*4, unsafe.Pointer(&b.ptsBuf[0]), gl.STREAM_DRAW) - if style.Color.A >= 255 { + if !canOverlap || style.Color.A >= 255 { vertex := b.useShader(style) gl.StencilFunc(gl.EQUAL, 0, 0xFF) @@ -130,7 +143,6 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { gl.EnableVertexAttribArray(vertex) gl.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, nil) - b.ptsBuf = append(b.ptsBuf[:0], 0, 0, float32(b.fw), 0, float32(b.fw), float32(b.fh), 0, float32(b.fh)) gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) gl.DisableVertexAttribArray(vertex) diff --git a/backend/softwarebackend/fill.go b/backend/softwarebackend/fill.go index fd05dd5..132de7a 100644 --- a/backend/softwarebackend/fill.go +++ b/backend/softwarebackend/fill.go @@ -19,7 +19,7 @@ func (b *SoftwareBackend) Clear(pts [4][2]float64) { }) } -func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { +func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts [][2]float64, canOverlap bool) { ffn := fillFunc(style) if style.Blur > 0 { diff --git a/backend/xmobilebackend/clip.go b/backend/xmobilebackend/clip.go index 67979eb..fbf1034 100755 --- a/backend/xmobilebackend/clip.go +++ b/backend/xmobilebackend/clip.go @@ -43,18 +43,21 @@ func (b *XMobileBackend) Clip(pts [][2]float64) { b.glctx.ColorMask(false, false, false, false) + // set bit 2 in the stencil buffer in the given shape b.glctx.StencilMask(0x04) - b.glctx.StencilFunc(gl.ALWAYS, 4, 0x04) + b.glctx.StencilFunc(gl.ALWAYS, 4, 0) b.glctx.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE) b.glctx.DrawArrays(mode, 4, len(pts)) + // on entire screen, where neither bit 1 or 2 are set, invert bit 1 b.glctx.StencilMask(0x02) b.glctx.StencilFunc(gl.EQUAL, 0, 0x06) b.glctx.StencilOp(gl.KEEP, gl.INVERT, gl.INVERT) b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) + // on entire screen, clear bit 2 b.glctx.StencilMask(0x04) - b.glctx.StencilFunc(gl.ALWAYS, 0, 0x04) + b.glctx.StencilFunc(gl.ALWAYS, 0, 0) b.glctx.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) diff --git a/backend/xmobilebackend/fill.go b/backend/xmobilebackend/fill.go index 8f27cba..1bb02f1 100755 --- a/backend/xmobilebackend/fill.go +++ b/backend/xmobilebackend/fill.go @@ -69,7 +69,19 @@ func (b *XMobileBackend) clearRect(x, y, w, h int) { b.glctx.Disable(gl.SCISSOR_TEST) } -func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { +func extent(pts [][2]float64) (min, max vec) { + min[0] = math.MaxFloat64 + min[1] = math.MaxFloat64 + for _, v := range pts { + min[0] = math.Min(min[0], v[0]) + min[1] = math.Min(min[1], v[1]) + max[0] = math.Max(max[0], v[0]) + max[1] = math.Max(max[1], v[1]) + } + return +} + +func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts [][2]float64, canOverlap bool) { b.activate() if style.Blur > 0 { @@ -80,11 +92,12 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { } b.ptsBuf = b.ptsBuf[:0] + min, max := extent(pts) b.ptsBuf = append(b.ptsBuf, - 0, 0, - 0, float32(b.fh), - float32(b.fw), float32(b.fh), - float32(b.fw), 0) + float32(min[0]), float32(min[1]), + float32(min[0]), float32(max[1]), + float32(max[0]), float32(max[1]), + float32(max[0]), float32(min[1])) for _, pt := range pts { b.ptsBuf = append(b.ptsBuf, float32(pt[0]), float32(pt[1])) } @@ -97,7 +110,7 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf) b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&b.ptsBuf[0]), len(b.ptsBuf)*4), gl.STREAM_DRAW) - if style.Color.A >= 255 { + if !canOverlap || style.Color.A >= 255 { vertex := b.useShader(style) b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF) @@ -130,7 +143,6 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { b.glctx.EnableVertexAttribArray(vertex) b.glctx.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0) - b.ptsBuf = append(b.ptsBuf[:0], 0, 0, float32(b.fw), 0, float32(b.fw), float32(b.fh), 0, float32(b.fh)) b.glctx.DrawArrays(gl.TRIANGLE_FAN, 0, 4) b.glctx.DisableVertexAttribArray(vertex) diff --git a/images.go b/images.go index 8c06bc6..d8e8e60 100644 --- a/images.go +++ b/images.go @@ -169,7 +169,7 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) { data[2] = cv.tf(vec{dx + dw, dy + dh}) data[3] = cv.tf(vec{dx + dw, dy}) - cv.drawShadow(data[:], nil) + cv.drawShadow(data[:], nil, false) cv.b.DrawImage(img.img, sx, sy, sw, sh, data, cv.state.globalAlpha) } diff --git a/paths.go b/paths.go index 18d4b13..d123f3d 100644 --- a/paths.go +++ b/paths.go @@ -95,10 +95,10 @@ func (cv *Canvas) strokePath(path *Path2D, inv mat, doInv bool) { var triBuf [500][2]float64 tris := cv.strokeTris(path, inv, doInv, triBuf[:0]) - cv.drawShadow(tris, nil) + cv.drawShadow(tris, nil, true) stl := cv.backendFillStyle(&cv.state.stroke, 1) - cv.b.Fill(&stl, tris) + cv.b.Fill(&stl, tris, true) } func (cv *Canvas) strokeTris(path *Path2D, inv mat, doInv bool, target [][2]float64) [][2]float64 { @@ -371,10 +371,10 @@ func (cv *Canvas) fillPath(path *Path2D, tf mat) { return } - cv.drawShadow(tris, nil) + cv.drawShadow(tris, nil, false) stl := cv.backendFillStyle(&cv.state.fill, 1) - cv.b.Fill(&stl, tris) + cv.b.Fill(&stl, tris, false) } func appendSubPathTriangles(tris [][2]float64, mat mat, path []pathPoint) [][2]float64 { @@ -412,8 +412,21 @@ func (cv *Canvas) clip(path *Path2D, tf mat) { return } - var triBuf [500][2]float64 - tris := triBuf[:0] + var buf [500][2]float64 + + if path.p[len(path.p)-1].flags&pathIsRect != 0 { + cv.state.clip.p = make([]pathPoint, len(path.p)) + copy(cv.state.clip.p, path.p) + + quad := buf[:4] + for i := range quad { + quad[i] = path.p[i].pos + } + cv.b.Clip(quad) + return + } + + tris := buf[:0] runSubPaths(path.p, true, func(sp []pathPoint) bool { tris = appendSubPathTriangles(tris, tf, sp) return false @@ -467,10 +480,10 @@ func (cv *Canvas) FillRect(x, y, w, h float64) { data := [4][2]float64{{p0[0], p0[1]}, {p1[0], p1[1]}, {p2[0], p2[1]}, {p3[0], p3[1]}} - cv.drawShadow(data[:], nil) + cv.drawShadow(data[:], nil, false) stl := cv.backendFillStyle(&cv.state.fill, 1) - cv.b.Fill(&stl, data[:]) + cv.b.Fill(&stl, data[:], false) } // ClearRect sets the color of the rectangle to transparent black diff --git a/shadows.go b/shadows.go index 3944335..5f2f7c5 100644 --- a/shadows.go +++ b/shadows.go @@ -7,7 +7,7 @@ import ( "github.com/tfriedel6/canvas/backend/backendbase" ) -func (cv *Canvas) drawShadow(pts [][2]float64, mask *image.Alpha) { +func (cv *Canvas) drawShadow(pts [][2]float64, mask *image.Alpha, canOverlap bool) { if cv.state.shadowColor.A == 0 { return } @@ -38,6 +38,6 @@ func (cv *Canvas) drawShadow(pts [][2]float64, mask *image.Alpha) { copy(quad[:], cv.shadowBuf) cv.b.FillImageMask(&style, mask, quad) } else { - cv.b.Fill(&style, cv.shadowBuf) + cv.b.Fill(&style, cv.shadowBuf, canOverlap) } } diff --git a/text.go b/text.go index adaa673..cf6f69b 100644 --- a/text.go +++ b/text.go @@ -215,7 +215,7 @@ func (cv *Canvas) FillText(str string, x, y float64) { mask := textImage.SubImage(image.Rect(0, 0, strWidth, strHeight)).(*image.Alpha) - cv.drawShadow(pts[:], mask) + cv.drawShadow(pts[:], mask, false) stl := cv.backendFillStyle(&cv.state.fill, 1) cv.b.FillImageMask(&stl, mask, pts)