From fe9d6e60e4878360e6d7eaf8026219f533fceb3f Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Fri, 22 Feb 2019 12:08:34 +0100 Subject: [PATCH] moved clipping to backend --- backend/backendbase/base.go | 3 + backend/gogl/clip.go | 62 ++++++++++++++++ backend/gogl/fill.go | 9 ++- backend/gogl/gogl.go | 2 +- canvas.go | 18 +---- paths.go | 144 ++++++++---------------------------- triangulation.go | 2 +- 7 files changed, 107 insertions(+), 133 deletions(-) create mode 100644 backend/gogl/clip.go diff --git a/backend/backendbase/base.go b/backend/backendbase/base.go index 0dccb25..f377333 100644 --- a/backend/backendbase/base.go +++ b/backend/backendbase/base.go @@ -20,6 +20,9 @@ type Backend interface { DrawImage(dimg Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64) FillImageMask(style *FillStyle, mask *image.Alpha, pts [][2]float64) // pts must have four points + ClearClip() + Clip(pts [][2]float64) + GetImageData(x, y, w, h int) *image.RGBA PutImageData(img *image.RGBA, x, y int) } diff --git a/backend/gogl/clip.go b/backend/gogl/clip.go new file mode 100644 index 0000000..61aa230 --- /dev/null +++ b/backend/gogl/clip.go @@ -0,0 +1,62 @@ +package goglbackend + +import ( + "unsafe" + + "github.com/go-gl/gl/v3.2-core/gl" +) + +func (b *GoGLBackend) ClearClip() { + gl.StencilMask(0xFF) + gl.Clear(gl.STENCIL_BUFFER_BIT) +} + +func (b *GoGLBackend) Clip(pts [][2]float64) { + b.ptsBuf = b.ptsBuf[:0] + b.ptsBuf = append(b.ptsBuf, + 0, 0, + 0, float32(b.fh), + float32(b.fw), float32(b.fh), + float32(b.fw), 0) + for _, pt := range pts { + b.ptsBuf = append(b.ptsBuf, float32(pt[0]), float32(pt[1])) + } + + mode := uint32(gl.TRIANGLES) + if len(pts) == 4 { + mode = gl.TRIANGLE_FAN + } + + gl.BindBuffer(gl.ARRAY_BUFFER, b.buf) + gl.BufferData(gl.ARRAY_BUFFER, len(b.ptsBuf)*4, unsafe.Pointer(&b.ptsBuf[0]), gl.STREAM_DRAW) + gl.VertexAttribPointer(b.sr.Vertex, 2, gl.FLOAT, false, 0, nil) + + gl.UseProgram(b.sr.ID) + gl.Uniform4f(b.sr.Color, 1, 1, 1, 1) + gl.Uniform2f(b.sr.CanvasSize, float32(b.fw), float32(b.fh)) + gl.EnableVertexAttribArray(b.sr.Vertex) + + gl.ColorMask(false, false, false, false) + + gl.StencilMask(0x04) + gl.StencilFunc(gl.ALWAYS, 4, 0x04) + gl.StencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE) + gl.DrawArrays(mode, 4, int32(len(pts))) + + gl.StencilMask(0x02) + gl.StencilFunc(gl.EQUAL, 0, 0x06) + gl.StencilOp(gl.KEEP, gl.INVERT, gl.INVERT) + gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) + + gl.StencilMask(0x04) + gl.StencilFunc(gl.ALWAYS, 0, 0x04) + gl.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) + gl.DrawArrays(gl.TRIANGLE_FAN, 0, 4) + + gl.DisableVertexAttribArray(b.sr.Vertex) + + gl.ColorMask(true, true, true, true) + gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) + gl.StencilMask(0xFF) + gl.StencilFunc(gl.EQUAL, 0, 0xFF) +} diff --git a/backend/gogl/fill.go b/backend/gogl/fill.go index f77ca01..02b117a 100644 --- a/backend/gogl/fill.go +++ b/backend/gogl/fill.go @@ -54,10 +54,17 @@ func (b *GoGLBackend) Clear(pts [4][2]float64) { } func (b *GoGLBackend) clearRect(x, y, w, h int) { + gl.Enable(gl.SCISSOR_TEST) + + var box [4]int32 + gl.GetIntegerv(gl.SCISSOR_BOX, &box[0]) + gl.Scissor(int32(x), int32(b.h-y-h), int32(w), int32(h)) gl.ClearColor(0, 0, 0, 0) gl.Clear(gl.COLOR_BUFFER_BIT) - // cv.applyScissor() + gl.Scissor(box[0], box[1], box[2], box[3]) + + gl.Disable(gl.SCISSOR_TEST) } func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { diff --git a/backend/gogl/gogl.go b/backend/gogl/gogl.go index 89a5c94..08c2516 100644 --- a/backend/gogl/gogl.go +++ b/backend/gogl/gogl.go @@ -205,7 +205,7 @@ func New(x, y, w, h int) (backendbase.Backend, error) { gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) gl.StencilFunc(gl.EQUAL, 0, 0xFF) - gl.Enable(gl.SCISSOR_TEST) + gl.Disable(gl.SCISSOR_TEST) return b, nil } diff --git a/canvas.go b/canvas.go index 8e96192..47bb830 100644 --- a/canvas.go +++ b/canvas.go @@ -59,8 +59,7 @@ type drawState struct { lineDashPoint int lineDashOffset float64 - scissor scissor - clip Path2D + clip Path2D shadowColor color.RGBA shadowOffsetX float64 @@ -85,11 +84,6 @@ type drawStyle struct { image *Image } -type scissor struct { - on bool - tl, br vec -} - type lineJoin uint8 type lineCap uint8 @@ -221,8 +215,6 @@ func (cv *Canvas) Activate() { gli.Viewport(int32(cv.x), int32(cv.y), int32(cv.w), int32(cv.h)) cv.disableTextureRenderTarget() } - cv.applyScissor() - gli.Clear(gl_STENCIL_BUFFER_BIT) } var activeCanvas *Canvas @@ -535,18 +527,14 @@ func (cv *Canvas) Restore() { if l <= 0 { return } - cv.state.scissor = scissor{} - cv.applyScissor() - gli.StencilMask(0xFF) - gli.Clear(gl_STENCIL_BUFFER_BIT) + cv.b.ClearClip() for _, st := range cv.stateStack { if len(st.clip.p) > 0 { - cv.clip(st.clip.p) + cv.clip(&st.clip) } } cv.state = cv.stateStack[l-1] cv.stateStack = cv.stateStack[:l-1] - cv.applyScissor() } // Scale updates the current transformation with a scaling by the given values diff --git a/paths.go b/paths.go index 75c9fa0..62eeb40 100644 --- a/paths.go +++ b/paths.go @@ -331,21 +331,7 @@ func (cv *Canvas) FillPath(path *Path2D) { cv.activate() var triBuf [500][2]float64 - tris := triBuf[:0] - - start := 0 - for i, p := range path.p { - if p.flags&pathMove == 0 { - continue - } - if i >= start+3 { - tris = cv.appendSubPathTriangles(tris, path.p[start:i]) - } - start = i - } - if len(path.p) >= start+3 { - tris = cv.appendSubPathTriangles(tris, path.p[start:]) - } + tris := buildFillTriangles(path, triBuf[:0]) if len(tris) == 0 { return } @@ -356,7 +342,24 @@ func (cv *Canvas) FillPath(path *Path2D) { cv.b.Fill(&stl, tris) } -func (cv *Canvas) appendSubPathTriangles(tris [][2]float64, path []pathPoint) [][2]float64 { +func buildFillTriangles(path *Path2D, tris [][2]float64) [][2]float64 { + start := 0 + for i, p := range path.p { + if p.flags&pathMove == 0 { + continue + } + if i >= start+3 { + tris = appendSubPathTriangles(tris, path.p[start:i]) + } + start = i + } + if len(path.p) >= start+3 { + tris = appendSubPathTriangles(tris, path.p[start:]) + } + return tris +} + +func appendSubPathTriangles(tris [][2]float64, path []pathPoint) [][2]float64 { last := path[len(path)-1] if last.flags&pathIsConvex != 0 { p0, p1 := path[0].pos, path[1].pos @@ -367,7 +370,7 @@ func (cv *Canvas) appendSubPathTriangles(tris [][2]float64, path []pathPoint) [] p1 = p2 } } else if last.flags&pathSelfIntersects != 0 { - path = cv.cutIntersections(path) + path = cutIntersections(path) tris = triangulatePath(path, tris) } else { tris = triangulatePath(path, tris) @@ -378,110 +381,21 @@ func (cv *Canvas) appendSubPathTriangles(tris [][2]float64, path []pathPoint) [] // Clip uses the current path to clip any further drawing. Use Save/Restore to // remove the clipping again func (cv *Canvas) Clip() { - if len(cv.path.p) < 3 { + cv.clip(&cv.path) +} + +func (cv *Canvas) clip(path *Path2D) { + if len(path.p) < 3 { return } - path := cv.path.p - for i := len(path) - 1; i >= 0; i-- { - if path[i].flags&pathMove != 0 { - path = path[i:] - break - } - } - - cv.clip(path) -} - -func (cv *Canvas) clip(path []pathPoint) { - if len(path) < 3 { - return - } - if path[len(path)-1].flags&pathIsRect != 0 { - cv.scissor(path) + var triBuf [500][2]float64 + tris := buildFillTriangles(path, triBuf[:0]) + if len(tris) == 0 { return } - // cv.activate() - - // var triBuf [1000]float32 - // tris := triBuf[:0] - // tris = append(tris, 0, 0, float32(cv.fw), 0, float32(cv.fw), float32(cv.fh), 0, 0, float32(cv.fw), float32(cv.fh), 0, float32(cv.fh)) - // baseLen := len(tris) - // tris = triangulatePath(path, tris) - // if len(tris) <= baseLen { - // return - // } - - // gli.BindBuffer(gl_ARRAY_BUFFER, buf) - // gli.BufferData(gl_ARRAY_BUFFER, len(tris)*4, unsafe.Pointer(&tris[0]), gl_STREAM_DRAW) - // gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, 0) - - // gli.UseProgram(sr.id) - // gli.Uniform4f(sr.color, 1, 1, 1, 1) - // gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh)) - // gli.EnableVertexAttribArray(sr.vertex) - - // gli.ColorMask(false, false, false, false) - - // gli.StencilMask(0x04) - // gli.StencilFunc(gl_ALWAYS, 4, 0x04) - // gli.StencilOp(gl_REPLACE, gl_REPLACE, gl_REPLACE) - // gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6)) - - // gli.StencilMask(0x02) - // gli.StencilFunc(gl_EQUAL, 0, 0x06) - // gli.StencilOp(gl_KEEP, gl_INVERT, gl_INVERT) - // gli.DrawArrays(gl_TRIANGLES, 0, 6) - - // gli.StencilMask(0x04) - // gli.StencilFunc(gl_ALWAYS, 0, 0x04) - // gli.StencilOp(gl_ZERO, gl_ZERO, gl_ZERO) - // gli.DrawArrays(gl_TRIANGLES, 0, 6) - - // gli.DisableVertexAttribArray(sr.vertex) - - // gli.ColorMask(true, true, true, true) - // gli.StencilOp(gl_KEEP, gl_KEEP, gl_KEEP) - // gli.StencilMask(0xFF) - // gli.StencilFunc(gl_EQUAL, 0, 0xFF) - - // cv.state.clip = cv.path - // cv.state.clip.p = make([]pathPoint, len(cv.path.p)) - // copy(cv.state.clip.p, cv.path.p) -} - -func (cv *Canvas) scissor(path []pathPoint) { - tl, br := vec{math.MaxFloat64, math.MaxFloat64}, vec{} - for _, p := range path { - tl[0] = math.Min(p.pos[0], tl[0]) - tl[1] = math.Min(p.pos[1], tl[1]) - br[0] = math.Max(p.pos[0], br[0]) - br[1] = math.Max(p.pos[1], br[1]) - } - - if cv.state.scissor.on { - tl[0] = math.Max(tl[0], cv.state.scissor.tl[0]) - tl[1] = math.Max(tl[1], cv.state.scissor.tl[1]) - br[0] = math.Min(br[0], cv.state.scissor.br[0]) - br[1] = math.Min(br[1], cv.state.scissor.br[1]) - } - - if tl[0] >= br[0] || tl[1] >= br[1] { - tl, br = vec{}, vec{} - } - - cv.state.scissor = scissor{tl: tl, br: br, on: true} - cv.applyScissor() -} - -func (cv *Canvas) applyScissor() { - s := &cv.state.scissor - if s.on { - gli.Scissor(int32(s.tl[0]+0.5), int32(cv.fh-s.br[1]+0.5), int32(s.br[0]-s.tl[0]+0.5), int32(s.br[1]-s.tl[1]+0.5)) - } else { - gli.Scissor(0, 0, int32(cv.w), int32(cv.h)) - } + cv.b.Clip(tris) } // Rect creates a closed rectangle path for stroking or filling diff --git a/triangulation.go b/triangulation.go index 2bb5da1..8d7c302 100644 --- a/triangulation.go +++ b/triangulation.go @@ -102,7 +102,7 @@ func triangulatePath(path []pathPoint, target [][2]float64) [][2]float64 { return target } -func (cv *Canvas) cutIntersections(path []pathPoint) []pathPoint { +func cutIntersections(path []pathPoint) []pathPoint { type cut struct { from, to int j int