From e7d0b1a7ee8d914b8898eebb001550d28ad04a34 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Thu, 21 Feb 2019 16:59:42 +0100 Subject: [PATCH] text shadow rendering moved to backend --- backend/backendbase/base.go | 2 +- backend/gogl/fill.go | 13 ++- backend/gogl/gogl.go | 2 +- canvas.go | 69 --------------- images.go | 2 +- paths.go | 6 +- shadows.go | 162 ++---------------------------------- text.go | 6 +- 8 files changed, 29 insertions(+), 233 deletions(-) diff --git a/backend/backendbase/base.go b/backend/backendbase/base.go index 2471dad..46f4b41 100644 --- a/backend/backendbase/base.go +++ b/backend/backendbase/base.go @@ -18,7 +18,7 @@ type Backend interface { Clear(pts [4][2]float64) Fill(style *FillStyle, pts [][2]float64) DrawImage(dimg Image, sx, sy, sw, sh, dx, dy, dw, dh float64, alpha float64) - FillImageMask(style *FillStyle, mask *image.Alpha, pts [4][2]float64) + FillImageMask(style *FillStyle, mask *image.Alpha, pts [][2]float64) // pts must have four points } // FillStyle is the color and other details on how to fill diff --git a/backend/gogl/fill.go b/backend/gogl/fill.go index 158d7b2..a18e410 100644 --- a/backend/gogl/fill.go +++ b/backend/gogl/fill.go @@ -118,7 +118,7 @@ func (b *GoGLBackend) Fill(style *backendbase.FillStyle, pts [][2]float64) { } } -func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [4][2]float64) { +func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Alpha, pts [][2]float64) { w, h := mask.Rect.Dx(), mask.Rect.Dy() gl.ActiveTexture(gl.TEXTURE1) @@ -128,7 +128,12 @@ func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Al gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, int32(alphaTexSize-1-y), int32(w), 1, gl.ALPHA, gl.UNSIGNED_BYTE, gl.Ptr(&mask.Pix[off])) } - // b.drawTextShadow(textOffset, w, h, x, y) + if style.Blur > 0 { + b.offscr1.alpha = true + b.enableTextureRenderTarget(&b.offscr1) + gl.ClearColor(0, 0, 0, 0) + gl.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + } gl.StencilFunc(gl.EQUAL, 0, 0xFF) @@ -167,6 +172,10 @@ func (b *GoGLBackend) FillImageMask(style *backendbase.FillStyle, mask *image.Al } gl.ActiveTexture(gl.TEXTURE0) + + if style.Blur > 0 { + b.drawBlurred(style.Blur) + } } func (b *GoGLBackend) drawBlurred(blur float64) { diff --git a/backend/gogl/gogl.go b/backend/gogl/gogl.go index 594df28..1689c7e 100644 --- a/backend/gogl/gogl.go +++ b/backend/gogl/gogl.go @@ -225,7 +225,7 @@ func glError() error { // needs to be called if any other GL code changes the viewport func (b *GoGLBackend) Activate() { // if b.offscreen { - // gli.Viewport(0, 0, int32(cv.w), int32(cv.h)) + // gl.Viewport(0, 0, int32(cv.w), int32(cv.h)) // cv.enableTextureRenderTarget(&cv.offscrBuf) // cv.offscrImg.w = cv.offscrBuf.w // cv.offscrImg.h = cv.offscrBuf.h diff --git a/canvas.go b/canvas.go index e5b7d74..ecb923d 100644 --- a/canvas.go +++ b/canvas.go @@ -519,75 +519,6 @@ func (cv *Canvas) backendFillStyle(s *drawStyle, alpha float64) backendbase.Fill return stl } -func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) { - if lg := style.linearGradient; lg != nil { - lg.load() - // gli.ActiveTexture(gl_TEXTURE0) - // gli.BindTexture(gl_TEXTURE_2D, lg.tex) - // gli.UseProgram(lgar.id) - // from := cv.tf(lg.from) - // to := cv.tf(lg.to) - // dir := to.sub(from) - // length := dir.len() - // dir = dir.divf(length) - // gli.Uniform2f(lgar.canvasSize, float32(cv.fw), float32(cv.fh)) - // inv := cv.state.transform.invert().f32() - // gli.UniformMatrix3fv(lgar.invmat, 1, false, &inv[0]) - // gli.Uniform2f(lgar.from, float32(from[0]), float32(from[1])) - // gli.Uniform2f(lgar.dir, float32(dir[0]), float32(dir[1])) - // gli.Uniform1f(lgar.len, float32(length)) - // gli.Uniform1i(lgar.gradient, 0) - // gli.Uniform1i(lgar.alphaTex, alphaTexSlot) - // gli.Uniform1f(lgar.globalAlpha, float32(cv.state.globalAlpha)) - // return lgar.vertex, lgar.alphaTexCoord - } - if rg := style.radialGradient; rg != nil { - rg.load() - // gli.ActiveTexture(gl_TEXTURE0) - // gli.BindTexture(gl_TEXTURE_2D, rg.tex) - // gli.UseProgram(rgar.id) - // from := cv.tf(rg.from) - // to := cv.tf(rg.to) - // dir := to.sub(from) - // length := dir.len() - // dir = dir.divf(length) - // gli.Uniform2f(rgar.canvasSize, float32(cv.fw), float32(cv.fh)) - // inv := cv.state.transform.invert().f32() - // gli.UniformMatrix3fv(rgar.invmat, 1, false, &inv[0]) - // gli.Uniform2f(rgar.from, float32(from[0]), float32(from[1])) - // gli.Uniform2f(rgar.to, float32(to[0]), float32(to[1])) - // gli.Uniform2f(rgar.dir, float32(dir[0]), float32(dir[1])) - // gli.Uniform1f(rgar.radFrom, float32(rg.radFrom)) - // gli.Uniform1f(rgar.radTo, float32(rg.radTo)) - // gli.Uniform1f(rgar.len, float32(length)) - // gli.Uniform1i(rgar.gradient, 0) - // gli.Uniform1i(rgar.alphaTex, alphaTexSlot) - // gli.Uniform1f(rgar.globalAlpha, float32(cv.state.globalAlpha)) - // return rgar.vertex, rgar.alphaTexCoord - } - // if img := style.image; img != nil { - // gli.UseProgram(ipar.id) - // gli.ActiveTexture(gl_TEXTURE0) - // gli.BindTexture(gl_TEXTURE_2D, img.tex) - // gli.Uniform2f(ipar.canvasSize, float32(cv.fw), float32(cv.fh)) - // inv := cv.state.transform.invert().f32() - // gli.UniformMatrix3fv(ipar.invmat, 1, false, &inv[0]) - // gli.Uniform2f(ipar.imageSize, float32(img.w), float32(img.h)) - // gli.Uniform1i(ipar.image, 0) - // gli.Uniform1i(ipar.alphaTex, alphaTexSlot) - // gli.Uniform1f(ipar.globalAlpha, float32(cv.state.globalAlpha)) - // return ipar.vertex, ipar.alphaTexCoord - // } - - gli.UseProgram(sar.id) - gli.Uniform2f(sar.canvasSize, float32(cv.fw), float32(cv.fh)) - c := colorGoToGL(style.color) - gli.Uniform4f(sar.color, float32(c.r), float32(c.g), float32(c.b), float32(c.a)) - gli.Uniform1i(sar.alphaTex, alphaTexSlot) - gli.Uniform1f(sar.globalAlpha, float32(cv.state.globalAlpha)) - return sar.vertex, sar.alphaTexCoord -} - func (cv *Canvas) enableTextureRenderTarget(offscr *offscreenBuffer) { if offscr.w != cv.w || offscr.h != cv.h { if offscr.w != 0 && offscr.h != 0 { diff --git a/images.go b/images.go index 0a835d4..4db66bf 100644 --- a/images.go +++ b/images.go @@ -168,7 +168,7 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) { data[2] = cv.tf(vec{dx + dw, dy + dh}) data[3] = cv.tf(vec{dx + dw, dy}) - cv.drawShadow2(data[:]) + cv.drawShadow2(data[:], nil) cv.b.DrawImage(img.img, sx, sy, sw, sh, dx, dy, dw, dh, cv.state.globalAlpha) } diff --git a/paths.go b/paths.go index 80be987..39c146c 100644 --- a/paths.go +++ b/paths.go @@ -156,7 +156,7 @@ func (cv *Canvas) strokePath(path *Path2D) { start = false } - cv.drawShadow2(tris) + cv.drawShadow2(tris, nil) stl := cv.backendFillStyle(&cv.state.stroke, 1) cv.b.Fill(&stl, tris) @@ -350,7 +350,7 @@ func (cv *Canvas) FillPath(path *Path2D) { return } - cv.drawShadow2(tris) + cv.drawShadow2(tris, nil) stl := cv.backendFillStyle(&cv.state.fill, 1) cv.b.Fill(&stl, tris) @@ -526,7 +526,7 @@ func (cv *Canvas) FillRect(x, y, w, h float64) { data := [4][2]float64{{p0[0], p0[1]}, {p1[0], p1[1]}, {p2[0], p2[1]}, {p3[0], p3[1]}} - cv.drawShadow2(data[:]) + cv.drawShadow2(data[:], nil) stl := cv.backendFillStyle(&cv.state.fill, 1) cv.b.Fill(&stl, data[:]) diff --git a/shadows.go b/shadows.go index 945b2d8..f67ee9a 100644 --- a/shadows.go +++ b/shadows.go @@ -2,13 +2,11 @@ package canvas import ( "image" - "math" - "unsafe" "github.com/tfriedel6/canvas/backend/backendbase" ) -func (cv *Canvas) drawShadow2(pts [][2]float64) { +func (cv *Canvas) drawShadow2(pts [][2]float64, mask *image.Alpha) { if cv.state.shadowColor.A == 0 { return } @@ -29,158 +27,12 @@ func (cv *Canvas) drawShadow2(pts [][2]float64) { } style := backendbase.FillStyle{Color: cv.state.shadowColor, Blur: cv.state.shadowBlur} - cv.b.Fill(&style, cv.shadowBuf) -} - -func (cv *Canvas) drawTextShadow(offset image.Point, strWidth, strHeight int, x, y float64) { - if cv.state.shadowColor.A == 0 { - return - } - - x += cv.state.shadowOffsetX - y += cv.state.shadowOffsetY - - if cv.state.shadowBlur > 0 { - offscr1.alpha = true - cv.enableTextureRenderTarget(&offscr1) - gli.ClearColor(0, 0, 0, 0) - gli.Clear(gl_COLOR_BUFFER_BIT | gl_STENCIL_BUFFER_BIT) - } - - gli.StencilFunc(gl_EQUAL, 0, 0xFF) - - gli.BindBuffer(gl_ARRAY_BUFFER, buf) - - var style drawStyle - style.color = cv.state.shadowColor - - vertex, alphaTexCoord := cv.useAlphaShader(&style, 1) - - gli.EnableVertexAttribArray(vertex) - gli.EnableVertexAttribArray(alphaTexCoord) - - p0 := cv.tf(vec{float64(offset.X) + x, float64(offset.Y) + y}) - p1 := cv.tf(vec{float64(offset.X) + x, float64(offset.Y+strHeight) + y}) - p2 := cv.tf(vec{float64(offset.X+strWidth) + x, float64(offset.Y+strHeight) + y}) - p3 := cv.tf(vec{float64(offset.X+strWidth) + x, float64(offset.Y) + y}) - - tw := float64(strWidth) / alphaTexSize - th := float64(strHeight) / alphaTexSize - data := [16]float32{float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1]), float32(p2[0]), float32(p2[1]), float32(p3[0]), float32(p3[1]), - 0, 1, 0, float32(1 - th), float32(tw), float32(1 - th), float32(tw), 1} - gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) - - gli.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0) - gli.VertexAttribPointer(alphaTexCoord, 2, gl_FLOAT, false, 0, 8*4) - gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) - - gli.DisableVertexAttribArray(vertex) - gli.DisableVertexAttribArray(alphaTexCoord) - - gli.ActiveTexture(gl_TEXTURE0) - - gli.StencilFunc(gl_ALWAYS, 0, 0xFF) - - if cv.state.shadowBlur > 0 { - cv.drawBlurredShadow() - } -} - -func (cv *Canvas) drawBlurredShadow() { - gli.BlendFunc(gl_ONE, gl_ONE_MINUS_SRC_ALPHA) - - var kernel []float32 - var kernelBuf [255]float32 - var gs *gaussianShader - if cv.state.shadowBlur < 3 { - gs = gauss15r - kernel = kernelBuf[:15] - } else if cv.state.shadowBlur < 12 { - gs = gauss63r - kernel = kernelBuf[:63] + if mask != nil { + if len(cv.shadowBuf) != 4 { + panic("invalid number of points to fill with mask, must be 4") + } + cv.b.FillImageMask(&style, mask, cv.shadowBuf) } else { - gs = gauss127r - kernel = kernelBuf[:127] - } - - gaussianKernel(cv.state.shadowBlur, kernel) - - offscr2.alpha = true - cv.enableTextureRenderTarget(&offscr2) - gli.ClearColor(0, 0, 0, 0) - gli.Clear(gl_COLOR_BUFFER_BIT | gl_STENCIL_BUFFER_BIT) - - gli.StencilFunc(gl_EQUAL, 0, 0xFF) - - gli.BindBuffer(gl_ARRAY_BUFFER, shadowBuf) - data := [16]float32{0, 0, 0, float32(cv.h), float32(cv.w), float32(cv.h), float32(cv.w), 0, 0, 0, 0, 1, 1, 1, 1, 0} - gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) - - gli.ActiveTexture(gl_TEXTURE0) - gli.BindTexture(gl_TEXTURE_2D, offscr1.tex) - - gli.UseProgram(gs.id) - gli.Uniform1i(gs.image, 0) - gli.Uniform2f(gs.canvasSize, float32(cv.fw), float32(cv.fh)) - gli.Uniform2f(gs.kernelScale, 1.0/float32(cv.fw), 0.0) - gli.Uniform1fv(gs.kernel, int32(len(kernel)), &kernel[0]) - gli.VertexAttribPointer(gs.vertex, 2, gl_FLOAT, false, 0, 0) - gli.VertexAttribPointer(gs.texCoord, 2, gl_FLOAT, false, 0, 8*4) - gli.EnableVertexAttribArray(gs.vertex) - gli.EnableVertexAttribArray(gs.texCoord) - gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) - gli.DisableVertexAttribArray(gs.vertex) - gli.DisableVertexAttribArray(gs.texCoord) - - gli.StencilFunc(gl_ALWAYS, 0, 0xFF) - - cv.disableTextureRenderTarget() - - gli.StencilFunc(gl_EQUAL, 0, 0xFF) - - gli.BindBuffer(gl_ARRAY_BUFFER, shadowBuf) - data = [16]float32{0, 0, 0, float32(cv.h), float32(cv.w), float32(cv.h), float32(cv.w), 0, 0, 0, 0, 1, 1, 1, 1, 0} - gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) - - gli.ActiveTexture(gl_TEXTURE0) - gli.BindTexture(gl_TEXTURE_2D, offscr2.tex) - - gli.UseProgram(gs.id) - gli.Uniform1i(gs.image, 0) - gli.Uniform2f(gs.canvasSize, float32(cv.fw), float32(cv.fh)) - gli.Uniform2f(gs.kernelScale, 0.0, 1.0/float32(cv.fh)) - gli.Uniform1fv(gs.kernel, int32(len(kernel)), &kernel[0]) - gli.VertexAttribPointer(gs.vertex, 2, gl_FLOAT, false, 0, 0) - gli.VertexAttribPointer(gs.texCoord, 2, gl_FLOAT, false, 0, 8*4) - gli.EnableVertexAttribArray(gs.vertex) - gli.EnableVertexAttribArray(gs.texCoord) - gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) - gli.DisableVertexAttribArray(gs.vertex) - gli.DisableVertexAttribArray(gs.texCoord) - - gli.StencilFunc(gl_ALWAYS, 0, 0xFF) - - gli.BlendFunc(gl_SRC_ALPHA, gl_ONE_MINUS_SRC_ALPHA) -} - -func gaussianKernel(stddev float64, target []float32) { - stddevSqr := stddev * stddev - center := float64(len(target) / 2) - factor := 1.0 / math.Sqrt(2*math.Pi*stddevSqr) - for i := range target { - x := float64(i) - center - target[i] = float32(factor * math.Pow(math.E, -x*x/(2*stddevSqr))) - } - // normalizeKernel(target) -} - -func normalizeKernel(kernel []float32) { - var sum float32 - for _, v := range kernel { - sum += v - } - factor := 1.0 / sum - for i := range kernel { - kernel[i] *= factor + cv.b.Fill(&style, cv.shadowBuf) } } diff --git a/text.go b/text.go index 2496857..b075098 100644 --- a/text.go +++ b/text.go @@ -215,8 +215,12 @@ func (cv *Canvas) FillText(str string, x, y float64) { pts[2] = cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y+strHeight) + y + yOff}) pts[3] = cv.tf(vec{float64(textOffset.X+strWidth) + x, float64(textOffset.Y) + y + yOff}) + mask := textImage.SubImage(image.Rect(0, 0, strWidth, strHeight)).(*image.Alpha) + + cv.drawShadow2(pts[:], mask) + stl := cv.backendFillStyle(&cv.state.fill, 1) - cv.b.FillImageMask(&stl, textImage.SubImage(image.Rect(0, 0, strWidth, strHeight)).(*image.Alpha), pts) + cv.b.FillImageMask(&stl, mask, pts[:]) } // StrokeText draws the given string at the given coordinates