performance optimization, avoid use of stencil buffer when drawing with opaque styles

This commit is contained in:
Thomas Friedel 2018-07-19 16:59:22 +02:00
parent 78a6d8f020
commit 5da544d78b
4 changed files with 107 additions and 55 deletions

View file

@ -360,6 +360,19 @@ func parseStyle(value ...interface{}) drawStyle {
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) {
if lg := style.linearGradient; lg != nil {
lg.load()

View file

@ -28,6 +28,7 @@ type gradient struct {
tex uint32
loaded bool
deleted bool
opaque bool
}
type gradientStop struct {
@ -39,7 +40,7 @@ type gradientStop struct {
// the coordinates from where to where the gradient
// will apply on the canvas
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.ActiveTexture(gl_TEXTURE0)
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
// circle
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.ActiveTexture(gl_TEXTURE0)
gli.BindTexture(gl_TEXTURE_2D, rg.tex)
@ -137,6 +138,9 @@ func (g *gradient) colorAt(pos float64) glColor {
// right place
func (g *gradient) AddColorStop(pos float64, color ...interface{}) {
c, _ := parseColor(color...)
if c.a < 1 {
g.opaque = false
}
insert := len(g.stops)
for i, stop := range g.stops {
if stop.pos > pos {

View file

@ -14,6 +14,7 @@ type Image struct {
w, h int
tex uint32
deleted bool
opaque bool
}
var images = make(map[string]*Image)
@ -89,7 +90,20 @@ func getImage(image interface{}) *Image {
}
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.ActiveTexture(gl_TEXTURE0)
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) {
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.ActiveTexture(gl_TEXTURE0)
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()
r, g, b, a := uint8(ir>>8), uint8(ig>>8), uint8(ib>>8), uint8(ia>>8)
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]))

View file

@ -364,6 +364,14 @@ func (cv *Canvas) stroke(path []pathPoint) {
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
if cv.state.globalAlpha >= 1 && cv.state.lineAlpha >= 1 && cv.state.stroke.isOpaque() {
vertex := cv.useShader(&cv.state.stroke)
gli.EnableVertexAttribArray(vertex)
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
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)
@ -399,6 +407,7 @@ func (cv *Canvas) stroke(path []pathPoint) {
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 {
@ -521,6 +530,14 @@ func (cv *Canvas) Fill() {
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
if cv.state.globalAlpha >= 1 && cv.state.lineAlpha >= 1 && cv.state.fill.isOpaque() {
vertex := cv.useShader(&cv.state.fill)
gli.EnableVertexAttribArray(vertex)
gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
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)
@ -550,6 +567,7 @@ func (cv *Canvas) Fill() {
gli.Clear(gl_STENCIL_BUFFER_BIT)
gli.StencilMask(0xFF)
}
}
func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []float32 {