performance optimization, avoid use of stencil buffer when drawing with opaque styles
This commit is contained in:
parent
78a6d8f020
commit
5da544d78b
4 changed files with 107 additions and 55 deletions
13
canvas.go
13
canvas.go
|
@ -360,6 +360,19 @@ func parseStyle(value ...interface{}) drawStyle {
|
||||||
return style
|
return style
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *drawStyle) isOpaque() bool {
|
||||||
|
if lg := s.linearGradient; lg != nil {
|
||||||
|
return lg.opaque
|
||||||
|
}
|
||||||
|
if rg := s.radialGradient; rg != nil {
|
||||||
|
return rg.opaque
|
||||||
|
}
|
||||||
|
if img := s.image; img != nil {
|
||||||
|
return img.opaque
|
||||||
|
}
|
||||||
|
return s.color.a >= 1
|
||||||
|
}
|
||||||
|
|
||||||
func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) {
|
func (cv *Canvas) useShader(style *drawStyle) (vertexLoc uint32) {
|
||||||
if lg := style.linearGradient; lg != nil {
|
if lg := style.linearGradient; lg != nil {
|
||||||
lg.load()
|
lg.load()
|
||||||
|
|
|
@ -28,6 +28,7 @@ type gradient struct {
|
||||||
tex uint32
|
tex uint32
|
||||||
loaded bool
|
loaded bool
|
||||||
deleted bool
|
deleted bool
|
||||||
|
opaque bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type gradientStop struct {
|
type gradientStop struct {
|
||||||
|
@ -39,7 +40,7 @@ type gradientStop struct {
|
||||||
// the coordinates from where to where the gradient
|
// the coordinates from where to where the gradient
|
||||||
// will apply on the canvas
|
// will apply on the canvas
|
||||||
func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
|
func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
|
||||||
lg := &LinearGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}}
|
lg := &LinearGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}, opaque: true}}
|
||||||
gli.GenTextures(1, &lg.tex)
|
gli.GenTextures(1, &lg.tex)
|
||||||
gli.ActiveTexture(gl_TEXTURE0)
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
gli.BindTexture(gl_TEXTURE_2D, lg.tex)
|
gli.BindTexture(gl_TEXTURE_2D, lg.tex)
|
||||||
|
@ -60,7 +61,7 @@ func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
|
||||||
// gradient will apply from the first to the second
|
// gradient will apply from the first to the second
|
||||||
// circle
|
// circle
|
||||||
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGradient {
|
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGradient {
|
||||||
rg := &RadialGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}, radFrom: r0, radTo: r1}
|
rg := &RadialGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}, opaque: true}, radFrom: r0, radTo: r1}
|
||||||
gli.GenTextures(1, &rg.tex)
|
gli.GenTextures(1, &rg.tex)
|
||||||
gli.ActiveTexture(gl_TEXTURE0)
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
gli.BindTexture(gl_TEXTURE_2D, rg.tex)
|
gli.BindTexture(gl_TEXTURE_2D, rg.tex)
|
||||||
|
@ -137,6 +138,9 @@ func (g *gradient) colorAt(pos float64) glColor {
|
||||||
// right place
|
// right place
|
||||||
func (g *gradient) AddColorStop(pos float64, color ...interface{}) {
|
func (g *gradient) AddColorStop(pos float64, color ...interface{}) {
|
||||||
c, _ := parseColor(color...)
|
c, _ := parseColor(color...)
|
||||||
|
if c.a < 1 {
|
||||||
|
g.opaque = false
|
||||||
|
}
|
||||||
insert := len(g.stops)
|
insert := len(g.stops)
|
||||||
for i, stop := range g.stops {
|
for i, stop := range g.stops {
|
||||||
if stop.pos > pos {
|
if stop.pos > pos {
|
||||||
|
|
21
images.go
21
images.go
|
@ -14,6 +14,7 @@ type Image struct {
|
||||||
w, h int
|
w, h int
|
||||||
tex uint32
|
tex uint32
|
||||||
deleted bool
|
deleted bool
|
||||||
|
opaque bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var images = make(map[string]*Image)
|
var images = make(map[string]*Image)
|
||||||
|
@ -89,7 +90,20 @@ func getImage(image interface{}) *Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageRGBA(src *image.RGBA) (*Image, error) {
|
func loadImageRGBA(src *image.RGBA) (*Image, error) {
|
||||||
img := &Image{w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
img := &Image{w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||||
|
|
||||||
|
checkOpaque:
|
||||||
|
for y := 0; y < img.h; y++ {
|
||||||
|
off := src.PixOffset(0, y) + 3
|
||||||
|
for x := 0; x < img.w; x++ {
|
||||||
|
if src.Pix[off] < 255 {
|
||||||
|
img.opaque = false
|
||||||
|
break checkOpaque
|
||||||
|
}
|
||||||
|
off += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gli.GenTextures(1, &img.tex)
|
gli.GenTextures(1, &img.tex)
|
||||||
gli.ActiveTexture(gl_TEXTURE0)
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||||
|
@ -155,7 +169,7 @@ func loadImageGray(src *image.Gray) (*Image, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageConverted(src image.Image) (*Image, error) {
|
func loadImageConverted(src image.Image) (*Image, error) {
|
||||||
img := &Image{w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
img := &Image{w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||||
gli.GenTextures(1, &img.tex)
|
gli.GenTextures(1, &img.tex)
|
||||||
gli.ActiveTexture(gl_TEXTURE0)
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
gli.BindTexture(gl_TEXTURE_2D, img.tex)
|
||||||
|
@ -172,6 +186,9 @@ func loadImageConverted(src image.Image) (*Image, error) {
|
||||||
ir, ig, ib, ia := src.At(x, y).RGBA()
|
ir, ig, ib, ia := src.At(x, y).RGBA()
|
||||||
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
|
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
|
||||||
data = append(data, r, g, b, a)
|
data = append(data, r, g, b, a)
|
||||||
|
if a < 255 {
|
||||||
|
img.opaque = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(img.w), int32(img.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&data[0]))
|
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(img.w), int32(img.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&data[0]))
|
||||||
|
|
120
paths.go
120
paths.go
|
@ -364,41 +364,50 @@ func (cv *Canvas) stroke(path []pathPoint) {
|
||||||
|
|
||||||
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||||
|
|
||||||
gli.ColorMask(false, false, false, false)
|
if cv.state.globalAlpha >= 1 && cv.state.lineAlpha >= 1 && cv.state.stroke.isOpaque() {
|
||||||
gli.StencilFunc(gl_ALWAYS, 1, 0xFF)
|
vertex := cv.useShader(&cv.state.stroke)
|
||||||
gli.StencilOp(gl_REPLACE, gl_REPLACE, gl_REPLACE)
|
|
||||||
gli.StencilMask(0x01)
|
|
||||||
|
|
||||||
gli.UseProgram(sr.id)
|
gli.EnableVertexAttribArray(vertex)
|
||||||
gli.Uniform4f(sr.color, 0, 0, 0, 0)
|
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh))
|
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
||||||
|
gli.DisableVertexAttribArray(vertex)
|
||||||
|
} else {
|
||||||
|
gli.ColorMask(false, false, false, false)
|
||||||
|
gli.StencilFunc(gl_ALWAYS, 1, 0xFF)
|
||||||
|
gli.StencilOp(gl_REPLACE, gl_REPLACE, gl_REPLACE)
|
||||||
|
gli.StencilMask(0x01)
|
||||||
|
|
||||||
gli.EnableVertexAttribArray(sr.vertex)
|
gli.UseProgram(sr.id)
|
||||||
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, 0)
|
gli.Uniform4f(sr.color, 0, 0, 0, 0)
|
||||||
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||||
gli.DisableVertexAttribArray(sr.vertex)
|
|
||||||
|
|
||||||
gli.ColorMask(true, true, true, true)
|
gli.EnableVertexAttribArray(sr.vertex)
|
||||||
|
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
|
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
||||||
|
gli.DisableVertexAttribArray(sr.vertex)
|
||||||
|
|
||||||
gli.StencilFunc(gl_EQUAL, 1, 0xFF)
|
gli.ColorMask(true, true, true, true)
|
||||||
|
|
||||||
origAlpha := cv.state.globalAlpha
|
gli.StencilFunc(gl_EQUAL, 1, 0xFF)
|
||||||
if cv.state.lineAlpha < 1 {
|
|
||||||
cv.state.globalAlpha *= cv.state.lineAlpha
|
origAlpha := cv.state.globalAlpha
|
||||||
|
if cv.state.lineAlpha < 1 {
|
||||||
|
cv.state.globalAlpha *= cv.state.lineAlpha
|
||||||
|
}
|
||||||
|
vertex := cv.useShader(&cv.state.stroke)
|
||||||
|
cv.state.globalAlpha = origAlpha
|
||||||
|
|
||||||
|
gli.EnableVertexAttribArray(vertex)
|
||||||
|
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
|
gli.DrawArrays(gl_TRIANGLES, 0, 6)
|
||||||
|
gli.DisableVertexAttribArray(vertex)
|
||||||
|
|
||||||
|
gli.StencilOp(gl_KEEP, gl_KEEP, gl_KEEP)
|
||||||
|
gli.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
||||||
|
|
||||||
|
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
||||||
|
gli.StencilMask(0xFF)
|
||||||
}
|
}
|
||||||
vertex := cv.useShader(&cv.state.stroke)
|
|
||||||
cv.state.globalAlpha = origAlpha
|
|
||||||
|
|
||||||
gli.EnableVertexAttribArray(vertex)
|
|
||||||
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
|
||||||
gli.DrawArrays(gl_TRIANGLES, 0, 6)
|
|
||||||
gli.DisableVertexAttribArray(vertex)
|
|
||||||
|
|
||||||
gli.StencilOp(gl_KEEP, gl_KEEP, gl_KEEP)
|
|
||||||
gli.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
|
||||||
|
|
||||||
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
|
||||||
gli.StencilMask(0xFF)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cv *Canvas) lineJoint(p pathPoint, p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris []float32) []float32 {
|
func (cv *Canvas) lineJoint(p pathPoint, p0, p1, p2, l0p0, l0p1, l0p2, l0p3 vec, tris []float32) []float32 {
|
||||||
|
@ -521,35 +530,44 @@ func (cv *Canvas) Fill() {
|
||||||
|
|
||||||
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||||
|
|
||||||
gli.ColorMask(false, false, false, false)
|
if cv.state.globalAlpha >= 1 && cv.state.lineAlpha >= 1 && cv.state.fill.isOpaque() {
|
||||||
gli.StencilFunc(gl_ALWAYS, 1, 0xFF)
|
vertex := cv.useShader(&cv.state.fill)
|
||||||
gli.StencilOp(gl_REPLACE, gl_REPLACE, gl_REPLACE)
|
|
||||||
gli.StencilMask(0x01)
|
|
||||||
|
|
||||||
gli.UseProgram(sr.id)
|
gli.EnableVertexAttribArray(vertex)
|
||||||
gli.Uniform4f(sr.color, 0, 0, 0, 0)
|
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh))
|
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
||||||
|
gli.DisableVertexAttribArray(vertex)
|
||||||
|
} else {
|
||||||
|
gli.ColorMask(false, false, false, false)
|
||||||
|
gli.StencilFunc(gl_ALWAYS, 1, 0xFF)
|
||||||
|
gli.StencilOp(gl_REPLACE, gl_REPLACE, gl_REPLACE)
|
||||||
|
gli.StencilMask(0x01)
|
||||||
|
|
||||||
gli.EnableVertexAttribArray(sr.vertex)
|
gli.UseProgram(sr.id)
|
||||||
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, 0)
|
gli.Uniform4f(sr.color, 0, 0, 0, 0)
|
||||||
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
gli.Uniform2f(sr.canvasSize, float32(cv.fw), float32(cv.fh))
|
||||||
gli.DisableVertexAttribArray(sr.vertex)
|
|
||||||
|
|
||||||
gli.ColorMask(true, true, true, true)
|
gli.EnableVertexAttribArray(sr.vertex)
|
||||||
|
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
|
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2-6))
|
||||||
|
gli.DisableVertexAttribArray(sr.vertex)
|
||||||
|
|
||||||
gli.StencilFunc(gl_EQUAL, 1, 0xFF)
|
gli.ColorMask(true, true, true, true)
|
||||||
|
|
||||||
vertex := cv.useShader(&cv.state.fill)
|
gli.StencilFunc(gl_EQUAL, 1, 0xFF)
|
||||||
gli.EnableVertexAttribArray(vertex)
|
|
||||||
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
|
||||||
gli.DrawArrays(gl_TRIANGLES, 0, 6)
|
|
||||||
gli.DisableVertexAttribArray(vertex)
|
|
||||||
|
|
||||||
gli.StencilOp(gl_KEEP, gl_KEEP, gl_KEEP)
|
vertex := cv.useShader(&cv.state.fill)
|
||||||
gli.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
gli.EnableVertexAttribArray(vertex)
|
||||||
|
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
||||||
|
gli.DrawArrays(gl_TRIANGLES, 0, 6)
|
||||||
|
gli.DisableVertexAttribArray(vertex)
|
||||||
|
|
||||||
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
gli.StencilOp(gl_KEEP, gl_KEEP, gl_KEEP)
|
||||||
gli.StencilMask(0xFF)
|
gli.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
||||||
|
|
||||||
|
gli.Clear(gl_STENCIL_BUFFER_BIT)
|
||||||
|
gli.StencilMask(0xFF)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []float32 {
|
func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []float32 {
|
||||||
|
|
Loading…
Reference in a new issue