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
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
21
images.go
21
images.go
|
@ -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]))
|
||||
|
|
18
paths.go
18
paths.go
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue