Path2D now caches the triangulation for filling

This commit is contained in:
Thomas Friedel 2020-03-22 10:51:25 +01:00
parent 804a9c2774
commit 34087abece
17 changed files with 116 additions and 28 deletions

View file

@ -18,7 +18,7 @@ type Backend interface {
LoadRadialGradient(data Gradient) RadialGradient
Clear(pts [4]Vec)
Fill(style *FillStyle, pts []Vec, canOverlap bool)
Fill(style *FillStyle, pts []Vec, tf Mat, canOverlap bool)
DrawImage(dimg Image, sx, sy, sw, sh float64, pts [4]Vec, alpha float64)
FillImageMask(style *FillStyle, mask *image.Alpha, pts [4]Vec) // pts must have four points

View file

@ -39,6 +39,7 @@ func (b *GoGLBackend) Clip(pts []backendbase.Vec) {
gl.UseProgram(b.shd.ID)
gl.Uniform4f(b.shd.Color, 1, 1, 1, 1)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &mat3identity[0])
gl.Uniform1f(b.shd.GlobalAlpha, 1)
gl.Uniform1i(b.shd.UseAlphaTex, 0)
gl.Uniform1i(b.shd.Func, shdFuncSolid)

View file

@ -35,6 +35,7 @@ func (b *GoGLBackend) Clear(pts [4]backendbase.Vec) {
gl.UseProgram(b.shd.ID)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &mat3identity[0])
gl.Uniform4f(b.shd.Color, 0, 0, 0, 0)
gl.Uniform1f(b.shd.GlobalAlpha, 1)
gl.Uniform1i(b.shd.UseAlphaTex, 0)
@ -85,7 +86,7 @@ func extent(pts []backendbase.Vec) (min, max backendbase.Vec) {
return
}
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, canOverlap bool) {
func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, tf backendbase.Mat, canOverlap bool) {
b.activate()
if style.Blur > 0 {
@ -115,7 +116,7 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec,
gl.BufferData(gl.ARRAY_BUFFER, len(b.ptsBuf)*4, unsafe.Pointer(&b.ptsBuf[0]), gl.STREAM_DRAW)
if !canOverlap || style.Color.A >= 255 {
vertex, _ := b.useShader(style, false, 0)
vertex, _ := b.useShader(style, mat3(tf), false, 0)
gl.StencilFunc(gl.EQUAL, 0, 0xFF)
gl.EnableVertexAttribArray(vertex)
@ -132,6 +133,8 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec,
gl.UseProgram(b.shd.ID)
gl.Uniform4f(b.shd.Color, 0, 0, 0, 0)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
m3 := mat3(tf)
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &m3[0])
gl.Uniform1f(b.shd.GlobalAlpha, 1)
gl.Uniform1i(b.shd.UseAlphaTex, 0)
gl.Uniform1i(b.shd.Func, shdFuncSolid)
@ -145,7 +148,7 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec,
gl.StencilFunc(gl.EQUAL, 1, 0xFF)
vertex, _ := b.useShader(style, false, 0)
vertex, _ := b.useShader(style, mat3identity, false, 0)
gl.EnableVertexAttribArray(vertex)
gl.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, nil)
@ -190,7 +193,7 @@ func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Al
gl.BindBuffer(gl.ARRAY_BUFFER, b.buf)
vertex, alphaTexCoord := b.useShader(style, true, 1)
vertex, alphaTexCoord := b.useShader(style, mat3identity, true, 1)
gl.EnableVertexAttribArray(vertex)
gl.EnableVertexAttribArray(alphaTexCoord)
@ -267,6 +270,7 @@ func (b *GoGLBackend) drawBlurred(size float64, min, max backendbase.Vec) {
gl.UseProgram(b.shd.ID)
gl.Uniform1i(b.shd.Image, 0)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &mat3identity[0])
gl.Uniform1i(b.shd.UseAlphaTex, 0)
gl.Uniform1i(b.shd.Func, shdFuncBoxBlur)

View file

@ -258,9 +258,10 @@ func (b *GoGLBackendOffscreen) AsImage() backendbase.Image {
return &b.offscrImg
}
func (b *GoGLBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) {
func (b *GoGLBackend) useShader(style *backendbase.FillStyle, tf [9]float32, useAlpha bool, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) {
gl.UseProgram(b.shd.ID)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &tf[0])
if useAlpha {
gl.Uniform1i(b.shd.UseAlphaTex, 1)
gl.Uniform1i(b.shd.AlphaTex, alphaTexSlot)
@ -382,3 +383,18 @@ func (b *GoGLBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
}
func mat3(m backendbase.Mat) (m3 [9]float32) {
m3[0] = float32(m[0])
m3[1] = float32(m[1])
m3[2] = 0
m3[3] = float32(m[2])
m3[4] = float32(m[3])
m3[5] = 0
m3[6] = float32(m[4])
m3[7] = float32(m[5])
m3[8] = 1
return
}
var mat3identity = [9]float32{1, 0, 0, 0, 1, 0, 0, 0, 1}

View file

@ -88,6 +88,7 @@ func (b *GoGLBackend) PutImageData(img *image.RGBA, x, y int) {
gl.UseProgram(b.shd.ID)
gl.Uniform1i(b.shd.Image, 0)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &mat3identity[0])
gl.Uniform1f(b.shd.GlobalAlpha, 1)
gl.Uniform1i(b.shd.UseAlphaTex, 0)
gl.Uniform1i(b.shd.Func, shdFuncImage)

View file

@ -190,6 +190,7 @@ func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64,
gl.UseProgram(b.shd.ID)
gl.Uniform1i(b.shd.Image, 0)
gl.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
gl.UniformMatrix3fv(b.shd.Matrix, 1, false, &mat3identity[0])
gl.Uniform1f(b.shd.GlobalAlpha, float32(alpha))
gl.Uniform1i(b.shd.UseAlphaTex, 0)
gl.Uniform1i(b.shd.Func, shdFuncImage)

View file

@ -4,13 +4,16 @@ var unifiedVS = `
attribute vec2 vertex, texCoord;
uniform vec2 canvasSize;
uniform mat3 matrix;
varying vec2 v_cp, v_tc;
void main() {
v_tc = texCoord;
v_cp = vertex;
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
v_tc = texCoord;
vec3 v = matrix * vec3(vertex.xy, 1.0);
vec2 tf = v.xy / v.z;
v_cp = tf;
vec2 glp = tf * 2.0 / canvasSize - 1.0;
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
}
`
@ -129,6 +132,7 @@ type unifiedShader struct {
TexCoord uint32
CanvasSize int32
Matrix int32
Color int32
GlobalAlpha int32

View file

@ -19,9 +19,22 @@ func (b *SoftwareBackend) Clear(pts [4]backendbase.Vec) {
})
}
func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, canOverlap bool) {
func (b *SoftwareBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, tf backendbase.Mat, canOverlap bool) {
ffn := fillFunc(style)
var triBuf [500]backendbase.Vec
if tf != backendbase.MatIdentity {
ptsOld := pts
if len(pts) < len(triBuf) {
pts = triBuf[:len(pts)]
} else {
pts = make([]backendbase.Vec, len(pts))
}
for i, pt := range ptsOld {
pts[i] = pt.MulMat(tf)
}
}
if style.Blur > 0 {
b.activateBlurTarget()
b.fillTriangles(pts, ffn)

View file

@ -39,6 +39,7 @@ func (b *XMobileBackend) Clip(pts []backendbase.Vec) {
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform4f(b.shd.Color, 1, 1, 1, 1)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:])
b.glctx.Uniform1f(b.shd.GlobalAlpha, 1)
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
b.glctx.Uniform1i(b.shd.Func, shdFuncSolid)

View file

@ -35,6 +35,7 @@ func (b *XMobileBackend) Clear(pts [4]backendbase.Vec) {
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:])
b.glctx.Uniform4f(b.shd.Color, 0, 0, 0, 0)
b.glctx.Uniform1f(b.shd.GlobalAlpha, 1)
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
@ -85,7 +86,7 @@ func extent(pts []backendbase.Vec) (min, max backendbase.Vec) {
return
}
func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, canOverlap bool) {
func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Vec, tf backendbase.Mat, canOverlap bool) {
b.activate()
if style.Blur > 0 {
@ -115,7 +116,7 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Ve
b.glctx.BufferData(gl.ARRAY_BUFFER, byteSlice(unsafe.Pointer(&b.ptsBuf[0]), len(b.ptsBuf)*4), gl.STREAM_DRAW)
if !canOverlap || style.Color.A >= 255 {
vertex, _ := b.useShader(style, false, 0)
vertex, _ := b.useShader(style, mat3(tf), false, 0)
b.glctx.StencilFunc(gl.EQUAL, 0, 0xFF)
b.glctx.EnableVertexAttribArray(vertex)
@ -132,6 +133,8 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Ve
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform4f(b.shd.Color, 0, 0, 0, 0)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
m3 := mat3(tf)
b.glctx.UniformMatrix3fv(b.shd.Matrix, m3[:])
b.glctx.Uniform1f(b.shd.GlobalAlpha, 1)
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
b.glctx.Uniform1i(b.shd.Func, shdFuncSolid)
@ -145,7 +148,7 @@ func (b *XMobileBackend) Fill(style *backendbase.FillStyle, pts []backendbase.Ve
b.glctx.StencilFunc(gl.EQUAL, 1, 0xFF)
vertex, _ := b.useShader(style, false, 0)
vertex, _ := b.useShader(style, mat3identity, false, 0)
b.glctx.EnableVertexAttribArray(vertex)
b.glctx.VertexAttribPointer(vertex, 2, gl.FLOAT, false, 0, 0)
@ -190,7 +193,7 @@ func (b *XMobileBackend) FillImageMask(style *backendbase.FillStyle, mask *image
b.glctx.BindBuffer(gl.ARRAY_BUFFER, b.buf)
vertex, alphaTexCoord := b.useShader(style, true, 1)
vertex, alphaTexCoord := b.useShader(style, mat3identity, true, 1)
b.glctx.EnableVertexAttribArray(vertex)
b.glctx.EnableVertexAttribArray(alphaTexCoord)
@ -267,6 +270,7 @@ func (b *XMobileBackend) drawBlurred(size float64, min, max backendbase.Vec) {
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform1i(b.shd.Image, 0)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:])
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
b.glctx.Uniform1i(b.shd.Func, shdFuncBoxBlur)

View file

@ -88,6 +88,7 @@ func (b *XMobileBackend) PutImageData(img *image.RGBA, x, y int) {
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform1i(b.shd.Image, 0)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:])
b.glctx.Uniform1f(b.shd.GlobalAlpha, 1)
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
b.glctx.Uniform1i(b.shd.Func, shdFuncImage)

View file

@ -192,6 +192,7 @@ func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float6
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform1i(b.shd.Image, 0)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, mat3identity[:])
b.glctx.Uniform1f(b.shd.GlobalAlpha, float32(alpha))
b.glctx.Uniform1i(b.shd.UseAlphaTex, 0)
b.glctx.Uniform1i(b.shd.Func, shdFuncImage)

View file

@ -8,13 +8,16 @@ var unifiedVS = `
attribute vec2 vertex, texCoord;
uniform vec2 canvasSize;
uniform mat3 matrix;
varying vec2 v_cp, v_tc;
void main() {
v_tc = texCoord;
v_cp = vertex;
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
v_tc = texCoord;
vec3 v = matrix * vec3(vertex.xy, 1.0);
vec2 tf = v.xy / v.z;
v_cp = tf;
vec2 glp = tf * 2.0 / canvasSize - 1.0;
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
}
`
@ -133,6 +136,7 @@ type unifiedShader struct {
TexCoord gl.Attrib
CanvasSize gl.Uniform
Matrix gl.Uniform
Color gl.Uniform
GlobalAlpha gl.Uniform

View file

@ -251,9 +251,10 @@ func (b *XMobileBackendOffscreen) AsImage() backendbase.Image {
return &b.offscrImg
}
func (b *XMobileBackend) useShader(style *backendbase.FillStyle, useAlpha bool, alphaTexSlot int) (vertexLoc, alphaTexCoordLoc gl.Attrib) {
func (b *XMobileBackend) useShader(style *backendbase.FillStyle, tf [9]float32, useAlpha bool, alphaTexSlot int) (vertexLoc, alphaTexCoordLoc gl.Attrib) {
b.glctx.UseProgram(b.shd.ID)
b.glctx.Uniform2f(b.shd.CanvasSize, float32(b.fw), float32(b.fh))
b.glctx.UniformMatrix3fv(b.shd.Matrix, tf[:])
if useAlpha {
b.glctx.Uniform1i(b.shd.UseAlphaTex, 1)
b.glctx.Uniform1i(b.shd.AlphaTex, alphaTexSlot)
@ -376,6 +377,21 @@ func (b *XMobileBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
b.glctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
}
func mat3(m backendbase.Mat) (m3 [9]float32) {
m3[0] = float32(m[0])
m3[1] = float32(m[1])
m3[2] = 0
m3[3] = float32(m[2])
m3[4] = float32(m[3])
m3[5] = 0
m3[6] = float32(m[4])
m3[7] = float32(m[5])
m3[8] = 1
return
}
var mat3identity = [9]float32{1, 0, 0, 0, 1, 0, 0, 0, 1}
func byteSlice(ptr unsafe.Pointer, size int) []byte {
var buf []byte
sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))

View file

@ -13,6 +13,9 @@ type Path2D struct {
p []pathPoint
move backendbase.Vec
cwSum float64
standalone bool
fillCache []backendbase.Vec
}
type pathPoint struct {
@ -34,7 +37,11 @@ const (
// NewPath2D creates a new Path2D and returns it
func (cv *Canvas) NewPath2D() *Path2D {
return &Path2D{cv: cv, p: make([]pathPoint, 0, 20)}
return &Path2D{cv: cv, p: make([]pathPoint, 0, 20), standalone: true}
}
func (p *Path2D) clearCache() {
p.fillCache = nil
}
// func (p *Path2D) AddPath(p2 *Path2D) {
@ -45,6 +52,7 @@ func (p *Path2D) MoveTo(x, y float64) {
if len(p.p) > 0 && isSamePoint(p.p[len(p.p)-1].pos, backendbase.Vec{x, y}, 0.1) {
return
}
p.clearCache()
p.p = append(p.p, pathPoint{pos: backendbase.Vec{x, y}, flags: pathMove | pathIsConvex})
p.cwSum = 0
p.move = backendbase.Vec{x, y}
@ -60,6 +68,7 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
if count > 0 && isSamePoint(p.p[len(p.p)-1].pos, backendbase.Vec{x, y}, 0.1) {
return
}
p.clearCache()
if count == 0 {
p.MoveTo(x, y)
return

View file

@ -110,7 +110,7 @@ func (cv *Canvas) strokePath(path *Path2D, inv backendbase.Mat, doInv bool) {
cv.drawShadow(tris, nil, true)
stl := cv.backendFillStyle(&cv.state.stroke, 1)
cv.b.Fill(&stl, tris, true)
cv.b.Fill(&stl, tris, backendbase.MatIdentity, true)
}
func (cv *Canvas) strokeTris(path *Path2D, inv backendbase.Mat, doInv bool, target []backendbase.Vec) []backendbase.Vec {
@ -381,13 +381,25 @@ func (cv *Canvas) fillPath(path *Path2D, tf backendbase.Mat) {
return
}
var tris []backendbase.Vec
var triBuf [500]backendbase.Vec
tris := triBuf[:0]
if path.standalone && path.fillCache != nil {
tris = path.fillCache
} else {
if path.standalone {
tris = make([]backendbase.Vec, 0, 500)
} else {
tris = triBuf[:0]
}
runSubPaths(path.p, true, func(sp []pathPoint) bool {
tris = appendSubPathTriangles(tris, backendbase.MatIdentity, sp)
return false
})
if path.standalone {
path.fillCache = tris
}
}
runSubPaths(path.p, true, func(sp []pathPoint) bool {
tris = appendSubPathTriangles(tris, tf, sp)
return false
})
if len(tris) == 0 {
return
}
@ -395,7 +407,7 @@ func (cv *Canvas) fillPath(path *Path2D, tf backendbase.Mat) {
cv.drawShadow(tris, nil, false)
stl := cv.backendFillStyle(&cv.state.fill, 1)
cv.b.Fill(&stl, tris, false)
cv.b.Fill(&stl, tris, tf, false)
}
func appendSubPathTriangles(tris []backendbase.Vec, mat backendbase.Mat, path []pathPoint) []backendbase.Vec {
@ -504,7 +516,7 @@ func (cv *Canvas) FillRect(x, y, w, h float64) {
cv.drawShadow(data[:], nil, false)
stl := cv.backendFillStyle(&cv.state.fill, 1)
cv.b.Fill(&stl, data[:], false)
cv.b.Fill(&stl, data[:], backendbase.MatIdentity, false)
}
// ClearRect sets the color of the rectangle to transparent black

View file

@ -38,6 +38,6 @@ func (cv *Canvas) drawShadow(pts []backendbase.Vec, mask *image.Alpha, canOverla
copy(quad[:], cv.shadowBuf)
cv.b.FillImageMask(&style, mask, quad)
} else {
cv.b.Fill(&style, cv.shadowBuf, canOverlap)
cv.b.Fill(&style, cv.shadowBuf, backendbase.MatIdentity, canOverlap)
}
}