From f2225e857a77f5a577916d9ac0c6af459b185043 Mon Sep 17 00:00:00 2001 From: Thomas Friedel Date: Fri, 27 Jul 2018 14:11:47 +0200 Subject: [PATCH] implemented shadowBlur (wip) --- canvas.go | 120 ++++--- glimpl/gogl/goglimpl.go | 3 + images.go | 63 ++++ made_shaders.go | 734 +++++++++++++++++++++++++++------------- make_shaders.go | 7 +- openglinterface.go | 1 + shaders.go | 97 ++++++ shadows.go | 106 ++++++ text.go | 5 +- 9 files changed, 860 insertions(+), 276 deletions(-) diff --git a/canvas.go b/canvas.go index 0dac4d0..31ca2f7 100644 --- a/canvas.go +++ b/canvas.go @@ -50,6 +50,7 @@ type drawState struct { shadowColor glColor shadowOffsetX float64 shadowOffsetY float64 + shadowBlur float64 /* The current transformation matrix. @@ -177,26 +178,34 @@ loop: const alphaTexSize = 2048 var ( - gli GL - buf uint32 - shadowBuf uint32 - alphaTex uint32 - sr *solidShader - lgr *linearGradientShader - rgr *radialGradientShader - ipr *imagePatternShader - sar *solidAlphaShader - rgar *radialGradientAlphaShader - lgar *linearGradientAlphaShader - ipar *imagePatternAlphaShader - ir *imageShader - offScrTex uint32 - offScrW int - offScrH int + gli GL + buf uint32 + shadowBuf uint32 + alphaTex uint32 + sr *solidShader + lgr *linearGradientShader + rgr *radialGradientShader + ipr *imagePatternShader + sar *solidAlphaShader + rgar *radialGradientAlphaShader + lgar *linearGradientAlphaShader + ipar *imagePatternAlphaShader + ir *imageShader + gauss15r *gaussian15Shader + gauss63r *gaussian63Shader + gauss255r *gaussian255Shader + offscr1 offscreenBuffer + offscr2 offscreenBuffer + glChan = make(chan func()) +) + +type offscreenBuffer struct { + tex uint32 + w int + h int renderStencilBuf uint32 frameBuf uint32 - glChan = make(chan func()) -) +} // LoadGL needs to be called once per GL context to load the GL assets // that canvas needs. The parameter is an implementation of the GL interface @@ -288,6 +297,33 @@ func LoadGL(glimpl GL) (err error) { return } + gauss15r, err = loadGaussian15Shader() + if err != nil { + return + } + err = glError() + if err != nil { + return + } + + gauss63r, err = loadGaussian63Shader() + if err != nil { + return + } + err = glError() + if err != nil { + return + } + + gauss255r, err = loadGaussian255Shader() + if err != nil { + return + } + err = glError() + if err != nil { + return + } + gli.GenBuffers(1, &buf) err = glError() if err != nil { @@ -514,32 +550,33 @@ func (cv *Canvas) useAlphaShader(style *drawStyle, alphaTexSlot int32) (vertexLo return sar.vertex, sar.alphaTexCoord } -func (cv *Canvas) enableTextureRenderTarget() { - if offScrW != cv.w || offScrH != cv.h { - if offScrW != 0 && offScrH != 0 { - gli.DeleteTextures(1, &offScrTex) - gli.DeleteFramebuffers(1, &frameBuf) - gli.DeleteRenderbuffers(1, &renderStencilBuf) +func (cv *Canvas) enableTextureRenderTarget(offscr *offscreenBuffer) { + if offscr.w != cv.w || offscr.h != cv.h { + if offscr.w != 0 && offscr.h != 0 { + gli.DeleteTextures(1, &offscr.tex) + gli.DeleteFramebuffers(1, &offscr.frameBuf) + gli.DeleteRenderbuffers(1, &offscr.renderStencilBuf) } - offScrW = cv.w - offScrH = cv.h + offscr.w = cv.w + offscr.h = cv.h - gli.GenTextures(1, &offScrTex) - gli.BindTexture(gl_TEXTURE_2D, offScrTex) + gli.ActiveTexture(gl_TEXTURE0) + gli.GenTextures(1, &offscr.tex) + gli.BindTexture(gl_TEXTURE_2D, offscr.tex) // todo do non-power-of-two textures work everywhere? - gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGB, int32(cv.w), int32(cv.h), 0, gl_RGB, gl_UNSIGNED_BYTE, nil) + gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, int32(cv.w), int32(cv.h), 0, gl_RGBA, gl_UNSIGNED_BYTE, nil) gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_NEAREST) gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_NEAREST) - gli.GenFramebuffers(1, &frameBuf) - gli.BindFramebuffer(gl_FRAMEBUFFER, frameBuf) + gli.GenFramebuffers(1, &offscr.frameBuf) + gli.BindFramebuffer(gl_FRAMEBUFFER, offscr.frameBuf) - gli.GenRenderbuffers(1, &renderStencilBuf) - gli.BindRenderbuffer(gl_RENDERBUFFER, renderStencilBuf) + gli.GenRenderbuffers(1, &offscr.renderStencilBuf) + gli.BindRenderbuffer(gl_RENDERBUFFER, offscr.renderStencilBuf) gli.RenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_STENCIL, int32(cv.w), int32(cv.h)) - gli.FramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_STENCIL_ATTACHMENT, gl_RENDERBUFFER, renderStencilBuf) + gli.FramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_STENCIL_ATTACHMENT, gl_RENDERBUFFER, offscr.renderStencilBuf) - gli.FramebufferTexture(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, offScrTex, 0) + gli.FramebufferTexture(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, offscr.tex, 0) if gli.CheckFramebufferStatus(gl_FRAMEBUFFER) != gl_FRAMEBUFFER_COMPLETE { // todo this should maybe not panic @@ -548,7 +585,7 @@ func (cv *Canvas) enableTextureRenderTarget() { gli.Clear(gl_COLOR_BUFFER_BIT | gl_STENCIL_BUFFER_BIT) } else { - gli.BindFramebuffer(gl_FRAMEBUFFER, frameBuf) + gli.BindFramebuffer(gl_FRAMEBUFFER, offscr.frameBuf) } } @@ -556,11 +593,6 @@ func (cv *Canvas) disableTextureRenderTarget() { gli.BindFramebuffer(gl_FRAMEBUFFER, 0) } -func (cv *Canvas) renderOffscreenTexture() { - img := Image{w: cv.w, h: cv.h, tex: offScrTex} - cv.DrawImage(&img, 0, 0, cv.fw, cv.fh) -} - // SetLineWidth sets the line width for any line drawing calls func (cv *Canvas) SetLineWidth(width float64) { if width < 0 { @@ -720,3 +752,9 @@ func (cv *Canvas) SetShadowOffset(x, y float64) { cv.state.shadowOffsetX = x cv.state.shadowOffsetY = y } + +// SetShadowBlur sets the gaussian blur radius of the shadow +// (0 for no blur) +func (cv *Canvas) SetShadowBlur(r float64) { + cv.state.shadowBlur = r +} diff --git a/glimpl/gogl/goglimpl.go b/glimpl/gogl/goglimpl.go index e3382a0..ccd3919 100644 --- a/glimpl/gogl/goglimpl.go +++ b/glimpl/gogl/goglimpl.go @@ -181,6 +181,9 @@ func (_ GLImpl) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset func (_ GLImpl) Uniform1f(location int32, v0 float32) { gl.Uniform1f(location, v0) } +func (_ GLImpl) Uniform1fv(location int32, count int32, v *float32) { + gl.Uniform1fv(location, count, v) +} func (_ GLImpl) Uniform1i(location int32, v0 int32) { gl.Uniform1i(location, v0) } diff --git a/images.go b/images.go index d071bab..e6459ff 100644 --- a/images.go +++ b/images.go @@ -309,3 +309,66 @@ func (cv *Canvas) DrawImage(image interface{}, coords ...float64) { gli.StencilFunc(gl_ALWAYS, 0, 0xFF) } + +func (cv *Canvas) drawImageTemp(image interface{}, coords ...float64) { + img := getImage(image) + + if img == nil { + return + } + + if img.deleted { + return + } + + cv.activate() + + var sx, sy, sw, sh, dx, dy, dw, dh float64 + sw, sh = float64(img.w), float64(img.h) + dw, dh = float64(img.w), float64(img.h) + if len(coords) == 2 { + dx, dy = coords[0], coords[1] + } else if len(coords) == 4 { + dx, dy = coords[0], coords[1] + dw, dh = coords[2], coords[3] + } else if len(coords) == 8 { + sx, sy = coords[0], coords[1] + sw, sh = coords[2], coords[3] + dx, dy = coords[4], coords[5] + dw, dh = coords[6], coords[7] + } + + sx /= float64(img.w) + sy /= float64(img.h) + sw /= float64(img.w) + sh /= float64(img.h) + + p0 := vec{dx, dy} + p1 := vec{dx, dy + dh} + p2 := vec{dx + dw, dy + dh} + p3 := vec{dx + dw, dy} + + gli.StencilFunc(gl_EQUAL, 0, 0xFF) + + gli.BindBuffer(gl_ARRAY_BUFFER, shadowBuf) + 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]), + float32(sx), float32(sy), float32(sx), float32(sy + sh), float32(sx + sw), float32(sy + sh), float32(sx + sw), float32(sy)} + gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW) + + gli.ActiveTexture(gl_TEXTURE0) + gli.BindTexture(gl_TEXTURE_2D, img.tex) + + gli.UseProgram(ir.id) + gli.Uniform1i(ir.image, 0) + gli.Uniform2f(ir.canvasSize, float32(cv.fw), float32(cv.fh)) + gli.Uniform1f(ir.globalAlpha, float32(cv.state.globalAlpha)) + gli.VertexAttribPointer(ir.vertex, 2, gl_FLOAT, false, 0, 0) + gli.VertexAttribPointer(ir.texCoord, 2, gl_FLOAT, false, 0, 8*4) + gli.EnableVertexAttribArray(ir.vertex) + gli.EnableVertexAttribArray(ir.texCoord) + gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) + gli.DisableVertexAttribArray(ir.vertex) + gli.DisableVertexAttribArray(ir.texCoord) + + gli.StencilFunc(gl_ALWAYS, 0, 0xFF) +} diff --git a/made_shaders.go b/made_shaders.go index a9c6ca4..4781270 100755 --- a/made_shaders.go +++ b/made_shaders.go @@ -5,63 +5,58 @@ import ( "fmt" ) -type radialGradientShader struct { +type gaussian63Shader struct { id uint32 vertex uint32 + texCoord uint32 canvasSize int32 - invmat int32 - gradient int32 - from int32 - to int32 - dir int32 - radFrom int32 - radTo int32 - len int32 - globalAlpha int32 + kernelScale int32 + image int32 + kernel int32 } -func loadRadialGradientShader() (*radialGradientShader, error) { +func loadGaussian63Shader() (*gaussian63Shader, error) { var vs, fs, program uint32 { vs = gli.CreateShader(gl_VERTEX_SHADER) - gli.ShaderSource(vs, radialGradientVS) + gli.ShaderSource(vs, gaussian63VS) gli.CompileShader(vs) shLog := gli.GetShaderInfoLog(vs) if len(shLog) > 0 { - fmt.Printf("VERTEX_SHADER compilation log for radialGradientVS:\n\n%s\n", shLog) + fmt.Printf("VERTEX_SHADER compilation log for gaussian63VS:\n\n%s\n", shLog) } var status int32 gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) if status != gl_TRUE { gli.DeleteShader(vs) - return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for radialGradientVS") + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for gaussian63VS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for radialGradientVS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error compiling shader part for gaussian63VS, glError: " + fmt.Sprint(glErr)) } } { fs = gli.CreateShader(gl_FRAGMENT_SHADER) - gli.ShaderSource(fs, radialGradientFS) + gli.ShaderSource(fs, gaussian63FS) gli.CompileShader(fs) shLog := gli.GetShaderInfoLog(fs) if len(shLog) > 0 { - fmt.Printf("FRAGMENT_SHADER compilation log for radialGradientFS:\n\n%s\n", shLog) + fmt.Printf("FRAGMENT_SHADER compilation log for gaussian63FS:\n\n%s\n", shLog) } var status int32 gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) if status != gl_TRUE { gli.DeleteShader(fs) - return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for radialGradientFS") + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for gaussian63FS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for radialGradientFS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error compiling shader part for gaussian63FS, glError: " + fmt.Sprint(glErr)) } } @@ -73,7 +68,7 @@ func loadRadialGradientShader() (*radialGradientShader, error) { shLog := gli.GetProgramInfoLog(program) if len(shLog) > 0 { - fmt.Printf("Shader link log for radialGradientFS:\n\n%s\n", shLog) + fmt.Printf("Shader link log for gaussian63FS:\n\n%s\n", shLog) } var status int32 @@ -81,26 +76,21 @@ func loadRadialGradientShader() (*radialGradientShader, error) { if status != gl_TRUE { gli.DeleteShader(vs) gli.DeleteShader(fs) - return nil, errors.New("error linking shader for radialGradientFS") + return nil, errors.New("error linking shader for gaussian63FS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error linking shader for radialGradientFS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error linking shader for gaussian63FS, glError: " + fmt.Sprint(glErr)) } } - result := &radialGradientShader{} + result := &gaussian63Shader{} result.id = program result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.texCoord = uint32(gli.GetAttribLocation(program, "texCoord")) result.canvasSize = gli.GetUniformLocation(program, "canvasSize") - result.invmat = gli.GetUniformLocation(program, "invmat") - result.gradient = gli.GetUniformLocation(program, "gradient") - result.from = gli.GetUniformLocation(program, "from") - result.to = gli.GetUniformLocation(program, "to") - result.dir = gli.GetUniformLocation(program, "dir") - result.radFrom = gli.GetUniformLocation(program, "radFrom") - result.radTo = gli.GetUniformLocation(program, "radTo") - result.len = gli.GetUniformLocation(program, "len") - result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + result.kernelScale = gli.GetUniformLocation(program, "kernelScale") + result.image = gli.GetUniformLocation(program, "image") + result.kernel = gli.GetUniformLocation(program, "kernel") return result, nil } @@ -191,190 +181,6 @@ func loadSolidShader() (*solidShader, error) { return result, nil } -type solidAlphaShader struct { - id uint32 - vertex uint32 - alphaTexCoord uint32 - canvasSize int32 - color int32 - alphaTex int32 - globalAlpha int32 -} - -func loadSolidAlphaShader() (*solidAlphaShader, error) { - var vs, fs, program uint32 - - { - vs = gli.CreateShader(gl_VERTEX_SHADER) - gli.ShaderSource(vs, solidAlphaVS) - gli.CompileShader(vs) - - shLog := gli.GetShaderInfoLog(vs) - if len(shLog) > 0 { - fmt.Printf("VERTEX_SHADER compilation log for solidAlphaVS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(vs) - return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for solidAlphaVS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for solidAlphaVS, glError: " + fmt.Sprint(glErr)) - } - } - - { - fs = gli.CreateShader(gl_FRAGMENT_SHADER) - gli.ShaderSource(fs, solidAlphaFS) - gli.CompileShader(fs) - - shLog := gli.GetShaderInfoLog(fs) - if len(shLog) > 0 { - fmt.Printf("FRAGMENT_SHADER compilation log for solidAlphaFS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(fs) - return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for solidAlphaFS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for solidAlphaFS, glError: " + fmt.Sprint(glErr)) - } - } - - { - program = gli.CreateProgram() - gli.AttachShader(program, vs) - gli.AttachShader(program, fs) - gli.LinkProgram(program) - - shLog := gli.GetProgramInfoLog(program) - if len(shLog) > 0 { - fmt.Printf("Shader link log for solidAlphaFS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetProgramiv(program, gl_LINK_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(vs) - gli.DeleteShader(fs) - return nil, errors.New("error linking shader for solidAlphaFS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error linking shader for solidAlphaFS, glError: " + fmt.Sprint(glErr)) - } - } - - result := &solidAlphaShader{} - result.id = program - result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) - result.alphaTexCoord = uint32(gli.GetAttribLocation(program, "alphaTexCoord")) - result.canvasSize = gli.GetUniformLocation(program, "canvasSize") - result.color = gli.GetUniformLocation(program, "color") - result.alphaTex = gli.GetUniformLocation(program, "alphaTex") - result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") - - return result, nil -} - -type linearGradientShader struct { - id uint32 - vertex uint32 - canvasSize int32 - invmat int32 - gradient int32 - from int32 - dir int32 - len int32 - globalAlpha int32 -} - -func loadLinearGradientShader() (*linearGradientShader, error) { - var vs, fs, program uint32 - - { - vs = gli.CreateShader(gl_VERTEX_SHADER) - gli.ShaderSource(vs, linearGradientVS) - gli.CompileShader(vs) - - shLog := gli.GetShaderInfoLog(vs) - if len(shLog) > 0 { - fmt.Printf("VERTEX_SHADER compilation log for linearGradientVS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(vs) - return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for linearGradientVS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for linearGradientVS, glError: " + fmt.Sprint(glErr)) - } - } - - { - fs = gli.CreateShader(gl_FRAGMENT_SHADER) - gli.ShaderSource(fs, linearGradientFS) - gli.CompileShader(fs) - - shLog := gli.GetShaderInfoLog(fs) - if len(shLog) > 0 { - fmt.Printf("FRAGMENT_SHADER compilation log for linearGradientFS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(fs) - return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for linearGradientFS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for linearGradientFS, glError: " + fmt.Sprint(glErr)) - } - } - - { - program = gli.CreateProgram() - gli.AttachShader(program, vs) - gli.AttachShader(program, fs) - gli.LinkProgram(program) - - shLog := gli.GetProgramInfoLog(program) - if len(shLog) > 0 { - fmt.Printf("Shader link log for linearGradientFS:\n\n%s\n", shLog) - } - - var status int32 - gli.GetProgramiv(program, gl_LINK_STATUS, &status) - if status != gl_TRUE { - gli.DeleteShader(vs) - gli.DeleteShader(fs) - return nil, errors.New("error linking shader for linearGradientFS") - } - if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error linking shader for linearGradientFS, glError: " + fmt.Sprint(glErr)) - } - } - - result := &linearGradientShader{} - result.id = program - result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) - result.canvasSize = gli.GetUniformLocation(program, "canvasSize") - result.invmat = gli.GetUniformLocation(program, "invmat") - result.gradient = gli.GetUniformLocation(program, "gradient") - result.from = gli.GetUniformLocation(program, "from") - result.dir = gli.GetUniformLocation(program, "dir") - result.len = gli.GetUniformLocation(program, "len") - result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") - - return result, nil -} - type linearGradientAlphaShader struct { id uint32 vertex uint32 @@ -577,57 +383,58 @@ func loadRadialGradientAlphaShader() (*radialGradientAlphaShader, error) { return result, nil } -type imageShader struct { +type gaussian15Shader struct { id uint32 vertex uint32 texCoord uint32 canvasSize int32 + kernelScale int32 image int32 - globalAlpha int32 + kernel int32 } -func loadImageShader() (*imageShader, error) { +func loadGaussian15Shader() (*gaussian15Shader, error) { var vs, fs, program uint32 { vs = gli.CreateShader(gl_VERTEX_SHADER) - gli.ShaderSource(vs, imageVS) + gli.ShaderSource(vs, gaussian15VS) gli.CompileShader(vs) shLog := gli.GetShaderInfoLog(vs) if len(shLog) > 0 { - fmt.Printf("VERTEX_SHADER compilation log for imageVS:\n\n%s\n", shLog) + fmt.Printf("VERTEX_SHADER compilation log for gaussian15VS:\n\n%s\n", shLog) } var status int32 gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) if status != gl_TRUE { gli.DeleteShader(vs) - return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for imageVS") + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for gaussian15VS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for imageVS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error compiling shader part for gaussian15VS, glError: " + fmt.Sprint(glErr)) } } { fs = gli.CreateShader(gl_FRAGMENT_SHADER) - gli.ShaderSource(fs, imageFS) + gli.ShaderSource(fs, gaussian15FS) gli.CompileShader(fs) shLog := gli.GetShaderInfoLog(fs) if len(shLog) > 0 { - fmt.Printf("FRAGMENT_SHADER compilation log for imageFS:\n\n%s\n", shLog) + fmt.Printf("FRAGMENT_SHADER compilation log for gaussian15FS:\n\n%s\n", shLog) } var status int32 gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) if status != gl_TRUE { gli.DeleteShader(fs) - return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for imageFS") + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for gaussian15FS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error compiling shader part for imageFS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error compiling shader part for gaussian15FS, glError: " + fmt.Sprint(glErr)) } } @@ -639,7 +446,7 @@ func loadImageShader() (*imageShader, error) { shLog := gli.GetProgramInfoLog(program) if len(shLog) > 0 { - fmt.Printf("Shader link log for imageFS:\n\n%s\n", shLog) + fmt.Printf("Shader link log for gaussian15FS:\n\n%s\n", shLog) } var status int32 @@ -647,20 +454,21 @@ func loadImageShader() (*imageShader, error) { if status != gl_TRUE { gli.DeleteShader(vs) gli.DeleteShader(fs) - return nil, errors.New("error linking shader for imageFS") + return nil, errors.New("error linking shader for gaussian15FS") } if glErr := gli.GetError(); glErr != gl_NO_ERROR { - return nil, errors.New("error linking shader for imageFS, glError: " + fmt.Sprint(glErr)) + return nil, errors.New("error linking shader for gaussian15FS, glError: " + fmt.Sprint(glErr)) } } - result := &imageShader{} + result := &gaussian15Shader{} result.id = program result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) result.texCoord = uint32(gli.GetAttribLocation(program, "texCoord")) result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.kernelScale = gli.GetUniformLocation(program, "kernelScale") result.image = gli.GetUniformLocation(program, "image") - result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + result.kernel = gli.GetUniformLocation(program, "kernel") return result, nil } @@ -755,6 +563,468 @@ func loadImagePatternShader() (*imagePatternShader, error) { return result, nil } +type solidAlphaShader struct { + id uint32 + vertex uint32 + alphaTexCoord uint32 + canvasSize int32 + color int32 + alphaTex int32 + globalAlpha int32 +} + +func loadSolidAlphaShader() (*solidAlphaShader, error) { + var vs, fs, program uint32 + + { + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, solidAlphaVS) + gli.CompileShader(vs) + + shLog := gli.GetShaderInfoLog(vs) + if len(shLog) > 0 { + fmt.Printf("VERTEX_SHADER compilation log for solidAlphaVS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for solidAlphaVS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for solidAlphaVS, glError: " + fmt.Sprint(glErr)) + } + } + + { + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, solidAlphaFS) + gli.CompileShader(fs) + + shLog := gli.GetShaderInfoLog(fs) + if len(shLog) > 0 { + fmt.Printf("FRAGMENT_SHADER compilation log for solidAlphaFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for solidAlphaFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for solidAlphaFS, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + shLog := gli.GetProgramInfoLog(program) + if len(shLog) > 0 { + fmt.Printf("Shader link log for solidAlphaFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader for solidAlphaFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader for solidAlphaFS, glError: " + fmt.Sprint(glErr)) + } + } + + result := &solidAlphaShader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.alphaTexCoord = uint32(gli.GetAttribLocation(program, "alphaTexCoord")) + result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.color = gli.GetUniformLocation(program, "color") + result.alphaTex = gli.GetUniformLocation(program, "alphaTex") + result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + + return result, nil +} + +type gaussian255Shader struct { + id uint32 + vertex uint32 + texCoord uint32 + canvasSize int32 + kernelScale int32 + image int32 + kernel int32 +} + +func loadGaussian255Shader() (*gaussian255Shader, error) { + var vs, fs, program uint32 + + { + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, gaussian255VS) + gli.CompileShader(vs) + + shLog := gli.GetShaderInfoLog(vs) + if len(shLog) > 0 { + fmt.Printf("VERTEX_SHADER compilation log for gaussian255VS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for gaussian255VS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for gaussian255VS, glError: " + fmt.Sprint(glErr)) + } + } + + { + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, gaussian255FS) + gli.CompileShader(fs) + + shLog := gli.GetShaderInfoLog(fs) + if len(shLog) > 0 { + fmt.Printf("FRAGMENT_SHADER compilation log for gaussian255FS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for gaussian255FS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for gaussian255FS, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + shLog := gli.GetProgramInfoLog(program) + if len(shLog) > 0 { + fmt.Printf("Shader link log for gaussian255FS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader for gaussian255FS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader for gaussian255FS, glError: " + fmt.Sprint(glErr)) + } + } + + result := &gaussian255Shader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.texCoord = uint32(gli.GetAttribLocation(program, "texCoord")) + result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.kernelScale = gli.GetUniformLocation(program, "kernelScale") + result.image = gli.GetUniformLocation(program, "image") + result.kernel = gli.GetUniformLocation(program, "kernel") + + return result, nil +} + +type radialGradientShader struct { + id uint32 + vertex uint32 + canvasSize int32 + invmat int32 + gradient int32 + from int32 + to int32 + dir int32 + radFrom int32 + radTo int32 + len int32 + globalAlpha int32 +} + +func loadRadialGradientShader() (*radialGradientShader, error) { + var vs, fs, program uint32 + + { + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, radialGradientVS) + gli.CompileShader(vs) + + shLog := gli.GetShaderInfoLog(vs) + if len(shLog) > 0 { + fmt.Printf("VERTEX_SHADER compilation log for radialGradientVS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for radialGradientVS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for radialGradientVS, glError: " + fmt.Sprint(glErr)) + } + } + + { + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, radialGradientFS) + gli.CompileShader(fs) + + shLog := gli.GetShaderInfoLog(fs) + if len(shLog) > 0 { + fmt.Printf("FRAGMENT_SHADER compilation log for radialGradientFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for radialGradientFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for radialGradientFS, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + shLog := gli.GetProgramInfoLog(program) + if len(shLog) > 0 { + fmt.Printf("Shader link log for radialGradientFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader for radialGradientFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader for radialGradientFS, glError: " + fmt.Sprint(glErr)) + } + } + + result := &radialGradientShader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.invmat = gli.GetUniformLocation(program, "invmat") + result.gradient = gli.GetUniformLocation(program, "gradient") + result.from = gli.GetUniformLocation(program, "from") + result.to = gli.GetUniformLocation(program, "to") + result.dir = gli.GetUniformLocation(program, "dir") + result.radFrom = gli.GetUniformLocation(program, "radFrom") + result.radTo = gli.GetUniformLocation(program, "radTo") + result.len = gli.GetUniformLocation(program, "len") + result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + + return result, nil +} + +type linearGradientShader struct { + id uint32 + vertex uint32 + canvasSize int32 + invmat int32 + gradient int32 + from int32 + dir int32 + len int32 + globalAlpha int32 +} + +func loadLinearGradientShader() (*linearGradientShader, error) { + var vs, fs, program uint32 + + { + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, linearGradientVS) + gli.CompileShader(vs) + + shLog := gli.GetShaderInfoLog(vs) + if len(shLog) > 0 { + fmt.Printf("VERTEX_SHADER compilation log for linearGradientVS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for linearGradientVS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for linearGradientVS, glError: " + fmt.Sprint(glErr)) + } + } + + { + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, linearGradientFS) + gli.CompileShader(fs) + + shLog := gli.GetShaderInfoLog(fs) + if len(shLog) > 0 { + fmt.Printf("FRAGMENT_SHADER compilation log for linearGradientFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for linearGradientFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for linearGradientFS, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + shLog := gli.GetProgramInfoLog(program) + if len(shLog) > 0 { + fmt.Printf("Shader link log for linearGradientFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader for linearGradientFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader for linearGradientFS, glError: " + fmt.Sprint(glErr)) + } + } + + result := &linearGradientShader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.invmat = gli.GetUniformLocation(program, "invmat") + result.gradient = gli.GetUniformLocation(program, "gradient") + result.from = gli.GetUniformLocation(program, "from") + result.dir = gli.GetUniformLocation(program, "dir") + result.len = gli.GetUniformLocation(program, "len") + result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + + return result, nil +} + +type imageShader struct { + id uint32 + vertex uint32 + texCoord uint32 + canvasSize int32 + image int32 + globalAlpha int32 +} + +func loadImageShader() (*imageShader, error) { + var vs, fs, program uint32 + + { + vs = gli.CreateShader(gl_VERTEX_SHADER) + gli.ShaderSource(vs, imageVS) + gli.CompileShader(vs) + + shLog := gli.GetShaderInfoLog(vs) + if len(shLog) > 0 { + fmt.Printf("VERTEX_SHADER compilation log for imageVS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part for imageVS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for imageVS, glError: " + fmt.Sprint(glErr)) + } + } + + { + fs = gli.CreateShader(gl_FRAGMENT_SHADER) + gli.ShaderSource(fs, imageFS) + gli.CompileShader(fs) + + shLog := gli.GetShaderInfoLog(fs) + if len(shLog) > 0 { + fmt.Printf("FRAGMENT_SHADER compilation log for imageFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(fs) + return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part for imageFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error compiling shader part for imageFS, glError: " + fmt.Sprint(glErr)) + } + } + + { + program = gli.CreateProgram() + gli.AttachShader(program, vs) + gli.AttachShader(program, fs) + gli.LinkProgram(program) + + shLog := gli.GetProgramInfoLog(program) + if len(shLog) > 0 { + fmt.Printf("Shader link log for imageFS:\n\n%s\n", shLog) + } + + var status int32 + gli.GetProgramiv(program, gl_LINK_STATUS, &status) + if status != gl_TRUE { + gli.DeleteShader(vs) + gli.DeleteShader(fs) + return nil, errors.New("error linking shader for imageFS") + } + if glErr := gli.GetError(); glErr != gl_NO_ERROR { + return nil, errors.New("error linking shader for imageFS, glError: " + fmt.Sprint(glErr)) + } + } + + result := &imageShader{} + result.id = program + result.vertex = uint32(gli.GetAttribLocation(program, "vertex")) + result.texCoord = uint32(gli.GetAttribLocation(program, "texCoord")) + result.canvasSize = gli.GetUniformLocation(program, "canvasSize") + result.image = gli.GetUniformLocation(program, "image") + result.globalAlpha = gli.GetUniformLocation(program, "globalAlpha") + + return result, nil +} + type imagePatternAlphaShader struct { id uint32 vertex uint32 diff --git a/make_shaders.go b/make_shaders.go index 7d5ad3e..58da595 100644 --- a/make_shaders.go +++ b/make_shaders.go @@ -116,9 +116,12 @@ func shaderFindInputVariables(source string) []ShaderInput { if !isAttrib && !isUniform { continue } - for i := 2; i < len(parts); i++ { + for _, part := range parts[2:] { + if idx := strings.IndexByte(part, '['); idx >= 0 { + part = part[:idx] + } inputs = append(inputs, ShaderInput{ - Name: parts[i], + Name: part, IsAttrib: isAttrib, IsUniform: isUniform}) } diff --git a/openglinterface.go b/openglinterface.go index 39786ce..144d319 100644 --- a/openglinterface.go +++ b/openglinterface.go @@ -844,6 +844,7 @@ type GL interface { TexParameteri(target uint32, pname uint32, param int32) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels unsafe.Pointer) Uniform1f(location int32, v0 float32) + Uniform1fv(location int32, count int32, v *float32) Uniform1i(location int32, v0 int32) Uniform2f(location int32, v0 float32, v1 float32) Uniform4f(location int32, v0 float32, v1 float32, v2 float32, v3 float32) diff --git a/shaders.go b/shaders.go index 138f377..74d2165 100644 --- a/shaders.go +++ b/shaders.go @@ -1,5 +1,11 @@ package canvas +import ( + "bytes" + "fmt" + "strings" +) + var imageVS = ` attribute vec2 vertex, texCoord; uniform vec2 canvasSize; @@ -274,3 +280,94 @@ void main() { col.a *= texture2D(alphaTex, v_atc).a * globalAlpha; gl_FragColor = col; }` + +var gaussian15VS = ` +attribute vec2 vertex, texCoord; +uniform vec2 canvasSize; +varying vec2 v_texCoord; +void main() { + v_texCoord = texCoord; + vec2 glp = vertex * 2.0 / canvasSize - 1.0; + gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); +}` +var gaussian15FS = ` +#ifdef GL_ES +precision mediump float; +#endif +varying vec2 v_texCoord; +uniform vec2 kernelScale; +uniform sampler2D image; +uniform float kernel[15]; +void main() { + vec4 color = vec4(0.0, 0.0, 0.0, 0.0); +_SUM_ + gl_FragColor = color; +}` + +var gaussian63VS = ` +attribute vec2 vertex, texCoord; +uniform vec2 canvasSize; +varying vec2 v_texCoord; +void main() { + v_texCoord = texCoord; + vec2 glp = vertex * 2.0 / canvasSize - 1.0; + gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); +}` +var gaussian63FS = ` +#ifdef GL_ES +precision mediump float; +#endif +varying vec2 v_texCoord; +uniform vec2 kernelScale; +uniform sampler2D image; +uniform float kernel[63]; +void main() { + vec4 color = vec4(0.0, 0.0, 0.0, 0.0); +_SUM_ + gl_FragColor = color; +}` + +var gaussian255VS = ` +attribute vec2 vertex, texCoord; +uniform vec2 canvasSize; +varying vec2 v_texCoord; +void main() { + v_texCoord = texCoord; + vec2 glp = vertex * 2.0 / canvasSize - 1.0; + gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0); +}` +var gaussian255FS = ` +#ifdef GL_ES +precision mediump float; +#endif +varying vec2 v_texCoord; +uniform vec2 kernelScale; +uniform sampler2D image; +uniform float kernel[255]; +void main() { + vec4 color = vec4(0.0, 0.0, 0.0, 0.0); +_SUM_ + gl_FragColor = color; +}` + +func init() { + fstr := "\tcolor += texture2D(image, v_texCoord + vec2(%.1f * kernelScale.x, %.1f * kernelScale.y)) * kernel[%d];\n" + bb := bytes.Buffer{} + for i := 0; i < 255; i++ { + off := float64(i) - 127 + fmt.Fprintf(&bb, fstr, off, off, i) + } + gaussian255FS = strings.Replace(gaussian255FS, "_SUM_", bb.String(), -1) + bb.Reset() + for i := 0; i < 63; i++ { + off := float64(i) - 31 + fmt.Fprintf(&bb, fstr, off, off, i) + } + gaussian63FS = strings.Replace(gaussian63FS, "_SUM_", bb.String(), -1) + bb.Reset() + for i := 0; i < 15; i++ { + off := float64(i) - 7 + fmt.Fprintf(&bb, fstr, off, off, i) + } + gaussian15FS = strings.Replace(gaussian15FS, "_SUM_", bb.String(), -1) +} diff --git a/shadows.go b/shadows.go index 46eddd2..a79f7bf 100644 --- a/shadows.go +++ b/shadows.go @@ -2,6 +2,7 @@ package canvas import ( "image" + "math" "unsafe" ) @@ -10,6 +11,12 @@ func (cv *Canvas) drawShadow(tris []float32) { return } + if cv.state.shadowBlur > 0 { + cv.enableTextureRenderTarget(&offscr1) + gli.ClearColor(0, 0, 0, 0) + gli.Clear(gl_COLOR_BUFFER_BIT | gl_STENCIL_BUFFER_BIT) + } + ox, oy := float32(cv.state.shadowOffsetX), float32(cv.state.shadowOffsetY) count := len(tris) @@ -53,12 +60,22 @@ func (cv *Canvas) drawShadow(tris []float32) { gli.Clear(gl_STENCIL_BUFFER_BIT) gli.StencilMask(0xFF) + + if cv.state.shadowBlur > 0 { + cv.drawBlurredShadow() + } } func (cv *Canvas) drawTextShadow(offset image.Point, strWidth, strHeight int, x, y float64) { x += cv.state.shadowOffsetX y += cv.state.shadowOffsetY + if cv.state.shadowBlur > 0 { + 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) @@ -92,4 +109,93 @@ func (cv *Canvas) drawTextShadow(offset image.Point, strWidth, strHeight int, x, 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 [255]float32 + gaussianKernel(cv.state.shadowBlur, kernel[:]) + + 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(gauss255r.id) + gli.Uniform1i(gauss255r.image, 0) + gli.Uniform2f(gauss255r.canvasSize, float32(cv.fw), float32(cv.fh)) + gli.Uniform2f(gauss255r.kernelScale, 1.0/float32(cv.fw), 0.0) + gli.Uniform1fv(gauss255r.kernel, int32(len(kernel)), &kernel[0]) + gli.VertexAttribPointer(gauss255r.vertex, 2, gl_FLOAT, false, 0, 0) + gli.VertexAttribPointer(gauss255r.texCoord, 2, gl_FLOAT, false, 0, 8*4) + gli.EnableVertexAttribArray(gauss255r.vertex) + gli.EnableVertexAttribArray(gauss255r.texCoord) + gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) + gli.DisableVertexAttribArray(gauss255r.vertex) + gli.DisableVertexAttribArray(gauss255r.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(gauss255r.id) + gli.Uniform1i(gauss255r.image, 0) + gli.Uniform2f(gauss255r.canvasSize, float32(cv.fw), float32(cv.fh)) + gli.Uniform2f(gauss255r.kernelScale, 0.0, 1.0/float32(cv.fh)) + gli.Uniform1fv(gauss255r.kernel, int32(len(kernel)), &kernel[0]) + gli.VertexAttribPointer(gauss255r.vertex, 2, gl_FLOAT, false, 0, 0) + gli.VertexAttribPointer(gauss255r.texCoord, 2, gl_FLOAT, false, 0, 8*4) + gli.EnableVertexAttribArray(gauss255r.vertex) + gli.EnableVertexAttribArray(gauss255r.texCoord) + gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4) + gli.DisableVertexAttribArray(gauss255r.vertex) + gli.DisableVertexAttribArray(gauss255r.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 + } } diff --git a/text.go b/text.go index 86c4673..52d5ac4 100644 --- a/text.go +++ b/text.go @@ -220,13 +220,16 @@ func (cv *Canvas) FillText(str string, x, y float64) { gli.DisableVertexAttribArray(vertex) gli.DisableVertexAttribArray(alphaTexCoord) - gli.ActiveTexture(gl_TEXTURE0) + gli.ActiveTexture(gl_TEXTURE1) + gli.BindTexture(gl_TEXTURE_2D, alphaTex) gli.StencilFunc(gl_ALWAYS, 0, 0xFF) for y := 0; y < strHeight; y++ { gli.TexSubImage2D(gl_TEXTURE_2D, 0, 0, int32(alphaTexSize-1-y), int32(strWidth), 1, gl_ALPHA, gl_UNSIGNED_BYTE, gli.Ptr(&zeroes[0])) } + + gli.ActiveTexture(gl_TEXTURE0) } // TextMetrics is the result of a MeasureText call