moved clipping to backend
This commit is contained in:
parent
cf788d55f3
commit
fe9d6e60e4
7 changed files with 107 additions and 133 deletions
|
@ -20,6 +20,9 @@ type Backend interface {
|
||||||
DrawImage(dimg Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64)
|
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
|
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
|
GetImageData(x, y, w, h int) *image.RGBA
|
||||||
PutImageData(img *image.RGBA, x, y int)
|
PutImageData(img *image.RGBA, x, y int)
|
||||||
}
|
}
|
||||||
|
|
62
backend/gogl/clip.go
Normal file
62
backend/gogl/clip.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -54,10 +54,17 @@ func (b *GoGLBackend) Clear(pts [4][2]float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *GoGLBackend) clearRect(x, y, w, h int) {
|
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.Scissor(int32(x), int32(b.h-y-h), int32(w), int32(h))
|
||||||
gl.ClearColor(0, 0, 0, 0)
|
gl.ClearColor(0, 0, 0, 0)
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
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) {
|
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) {
|
||||||
|
|
|
@ -205,7 +205,7 @@ func New(x, y, w, h int) (backendbase.Backend, error) {
|
||||||
gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
|
gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
|
||||||
gl.StencilFunc(gl.EQUAL, 0, 0xFF)
|
gl.StencilFunc(gl.EQUAL, 0, 0xFF)
|
||||||
|
|
||||||
gl.Enable(gl.SCISSOR_TEST)
|
gl.Disable(gl.SCISSOR_TEST)
|
||||||
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
16
canvas.go
16
canvas.go
|
@ -59,7 +59,6 @@ type drawState struct {
|
||||||
lineDashPoint int
|
lineDashPoint int
|
||||||
lineDashOffset float64
|
lineDashOffset float64
|
||||||
|
|
||||||
scissor scissor
|
|
||||||
clip Path2D
|
clip Path2D
|
||||||
|
|
||||||
shadowColor color.RGBA
|
shadowColor color.RGBA
|
||||||
|
@ -85,11 +84,6 @@ type drawStyle struct {
|
||||||
image *Image
|
image *Image
|
||||||
}
|
}
|
||||||
|
|
||||||
type scissor struct {
|
|
||||||
on bool
|
|
||||||
tl, br vec
|
|
||||||
}
|
|
||||||
|
|
||||||
type lineJoin uint8
|
type lineJoin uint8
|
||||||
type lineCap 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))
|
gli.Viewport(int32(cv.x), int32(cv.y), int32(cv.w), int32(cv.h))
|
||||||
cv.disableTextureRenderTarget()
|
cv.disableTextureRenderTarget()
|
||||||
}
|
}
|
||||||
cv.applyScissor()
|
|
||||||
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeCanvas *Canvas
|
var activeCanvas *Canvas
|
||||||
|
@ -535,18 +527,14 @@ func (cv *Canvas) Restore() {
|
||||||
if l <= 0 {
|
if l <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cv.state.scissor = scissor{}
|
cv.b.ClearClip()
|
||||||
cv.applyScissor()
|
|
||||||
gli.StencilMask(0xFF)
|
|
||||||
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
|
||||||
for _, st := range cv.stateStack {
|
for _, st := range cv.stateStack {
|
||||||
if len(st.clip.p) > 0 {
|
if len(st.clip.p) > 0 {
|
||||||
cv.clip(st.clip.p)
|
cv.clip(&st.clip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cv.state = cv.stateStack[l-1]
|
cv.state = cv.stateStack[l-1]
|
||||||
cv.stateStack = cv.stateStack[:l-1]
|
cv.stateStack = cv.stateStack[:l-1]
|
||||||
cv.applyScissor()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale updates the current transformation with a scaling by the given values
|
// Scale updates the current transformation with a scaling by the given values
|
||||||
|
|
144
paths.go
144
paths.go
|
@ -331,21 +331,7 @@ func (cv *Canvas) FillPath(path *Path2D) {
|
||||||
cv.activate()
|
cv.activate()
|
||||||
|
|
||||||
var triBuf [500][2]float64
|
var triBuf [500][2]float64
|
||||||
tris := triBuf[:0]
|
tris := buildFillTriangles(path, 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:])
|
|
||||||
}
|
|
||||||
if len(tris) == 0 {
|
if len(tris) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -356,7 +342,24 @@ func (cv *Canvas) FillPath(path *Path2D) {
|
||||||
cv.b.Fill(&stl, tris)
|
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]
|
last := path[len(path)-1]
|
||||||
if last.flags&pathIsConvex != 0 {
|
if last.flags&pathIsConvex != 0 {
|
||||||
p0, p1 := path[0].pos, path[1].pos
|
p0, p1 := path[0].pos, path[1].pos
|
||||||
|
@ -367,7 +370,7 @@ func (cv *Canvas) appendSubPathTriangles(tris [][2]float64, path []pathPoint) []
|
||||||
p1 = p2
|
p1 = p2
|
||||||
}
|
}
|
||||||
} else if last.flags&pathSelfIntersects != 0 {
|
} else if last.flags&pathSelfIntersects != 0 {
|
||||||
path = cv.cutIntersections(path)
|
path = cutIntersections(path)
|
||||||
tris = triangulatePath(path, tris)
|
tris = triangulatePath(path, tris)
|
||||||
} else {
|
} else {
|
||||||
tris = triangulatePath(path, tris)
|
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
|
// Clip uses the current path to clip any further drawing. Use Save/Restore to
|
||||||
// remove the clipping again
|
// remove the clipping again
|
||||||
func (cv *Canvas) Clip() {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path := cv.path.p
|
var triBuf [500][2]float64
|
||||||
for i := len(path) - 1; i >= 0; i-- {
|
tris := buildFillTriangles(path, triBuf[:0])
|
||||||
if path[i].flags&pathMove != 0 {
|
if len(tris) == 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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// cv.activate()
|
cv.b.Clip(tris)
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rect creates a closed rectangle path for stroking or filling
|
// Rect creates a closed rectangle path for stroking or filling
|
||||||
|
|
|
@ -102,7 +102,7 @@ func triangulatePath(path []pathPoint, target [][2]float64) [][2]float64 {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cv *Canvas) cutIntersections(path []pathPoint) []pathPoint {
|
func cutIntersections(path []pathPoint) []pathPoint {
|
||||||
type cut struct {
|
type cut struct {
|
||||||
from, to int
|
from, to int
|
||||||
j int
|
j int
|
||||||
|
|
Loading…
Reference in a new issue