backend can now reuse gl contexts, updated examples and docs
This commit is contained in:
parent
22c3c25d8e
commit
c98bc6c23d
13 changed files with 276 additions and 185 deletions
|
@ -13,10 +13,9 @@ const alphaTexSize = 2048
|
|||
|
||||
var zeroes [alphaTexSize]byte
|
||||
|
||||
type GoGLBackend struct {
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
||||
// GLContext is a context that contains all the
|
||||
// shaders and buffers necessary for rendering
|
||||
type GLContext struct {
|
||||
buf uint32
|
||||
shadowBuf uint32
|
||||
alphaTex uint32
|
||||
|
@ -43,158 +42,144 @@ type GoGLBackend struct {
|
|||
ptsBuf []float32
|
||||
|
||||
glChan chan func()
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
tex uint32
|
||||
w int
|
||||
h int
|
||||
renderStencilBuf uint32
|
||||
frameBuf uint32
|
||||
alpha bool
|
||||
}
|
||||
// NewGLContext creates all the necessary GL resources,
|
||||
// like shaders and buffers
|
||||
func NewGLContext() (*GLContext, error) {
|
||||
ctx := &GLContext{
|
||||
ptsBuf: make([]float32, 0, 4096),
|
||||
glChan: make(chan func()),
|
||||
}
|
||||
|
||||
func New(x, y, w, h int) (*GoGLBackend, error) {
|
||||
err := gl.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := &GoGLBackend{
|
||||
w: w,
|
||||
h: h,
|
||||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
ptsBuf: make([]float32, 0, 4096),
|
||||
glChan: make(chan func()),
|
||||
}
|
||||
|
||||
gl.GetError() // clear error state
|
||||
|
||||
err = loadShader(solidVS, solidFS, &b.sr.shaderProgram)
|
||||
err = loadShader(solidVS, solidFS, &ctx.sr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.sr.shaderProgram.mustLoadLocations(&b.sr)
|
||||
ctx.sr.shaderProgram.mustLoadLocations(&ctx.sr)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(linearGradientVS, linearGradientFS, &b.lgr.shaderProgram)
|
||||
err = loadShader(linearGradientVS, linearGradientFS, &ctx.lgr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.lgr.shaderProgram.mustLoadLocations(&b.lgr)
|
||||
ctx.lgr.shaderProgram.mustLoadLocations(&ctx.lgr)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(radialGradientVS, radialGradientFS, &b.rgr.shaderProgram)
|
||||
err = loadShader(radialGradientVS, radialGradientFS, &ctx.rgr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.rgr.shaderProgram.mustLoadLocations(&b.rgr)
|
||||
ctx.rgr.shaderProgram.mustLoadLocations(&ctx.rgr)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(imagePatternVS, imagePatternFS, &b.ipr.shaderProgram)
|
||||
err = loadShader(imagePatternVS, imagePatternFS, &ctx.ipr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ipr.shaderProgram.mustLoadLocations(&b.ipr)
|
||||
ctx.ipr.shaderProgram.mustLoadLocations(&ctx.ipr)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(solidAlphaVS, solidAlphaFS, &b.sar.shaderProgram)
|
||||
err = loadShader(solidAlphaVS, solidAlphaFS, &ctx.sar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.sar.shaderProgram.mustLoadLocations(&b.sar)
|
||||
ctx.sar.shaderProgram.mustLoadLocations(&ctx.sar)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(linearGradientAlphaVS, linearGradientFS, &b.lgar.shaderProgram)
|
||||
err = loadShader(linearGradientAlphaVS, linearGradientFS, &ctx.lgar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.lgar.shaderProgram.mustLoadLocations(&b.lgar)
|
||||
ctx.lgar.shaderProgram.mustLoadLocations(&ctx.lgar)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(radialGradientAlphaVS, radialGradientAlphaFS, &b.rgar.shaderProgram)
|
||||
err = loadShader(radialGradientAlphaVS, radialGradientAlphaFS, &ctx.rgar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.rgar.shaderProgram.mustLoadLocations(&b.rgar)
|
||||
ctx.rgar.shaderProgram.mustLoadLocations(&ctx.rgar)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(imagePatternAlphaVS, imagePatternAlphaFS, &b.ipar.shaderProgram)
|
||||
err = loadShader(imagePatternAlphaVS, imagePatternAlphaFS, &ctx.ipar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ipar.shaderProgram.mustLoadLocations(&b.ipar)
|
||||
ctx.ipar.shaderProgram.mustLoadLocations(&ctx.ipar)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(imageVS, imageFS, &b.ir.shaderProgram)
|
||||
err = loadShader(imageVS, imageFS, &ctx.ir.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ir.shaderProgram.mustLoadLocations(&b.ir)
|
||||
ctx.ir.shaderProgram.mustLoadLocations(&ctx.ir)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(gaussian15VS, gaussian15FS, &b.gauss15r.shaderProgram)
|
||||
err = loadShader(gaussian15VS, gaussian15FS, &ctx.gauss15r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss15r.shaderProgram.mustLoadLocations(&b.gauss15r)
|
||||
ctx.gauss15r.shaderProgram.mustLoadLocations(&ctx.gauss15r)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(gaussian63VS, gaussian63FS, &b.gauss63r.shaderProgram)
|
||||
err = loadShader(gaussian63VS, gaussian63FS, &ctx.gauss63r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss63r.shaderProgram.mustLoadLocations(&b.gauss63r)
|
||||
ctx.gauss63r.shaderProgram.mustLoadLocations(&ctx.gauss63r)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(gaussian127VS, gaussian127FS, &b.gauss127r.shaderProgram)
|
||||
err = loadShader(gaussian127VS, gaussian127FS, &ctx.gauss127r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss127r.shaderProgram.mustLoadLocations(&b.gauss127r)
|
||||
ctx.gauss127r.shaderProgram.mustLoadLocations(&ctx.gauss127r)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gl.GenBuffers(1, &b.buf)
|
||||
gl.GenBuffers(1, &ctx.buf)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gl.GenBuffers(1, &b.shadowBuf)
|
||||
gl.GenBuffers(1, &ctx.shadowBuf)
|
||||
if err = glError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.GenTextures(1, &b.alphaTex)
|
||||
gl.BindTexture(gl.TEXTURE_2D, b.alphaTex)
|
||||
gl.GenTextures(1, &ctx.alphaTex)
|
||||
gl.BindTexture(gl.TEXTURE_2D, ctx.alphaTex)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
|
@ -211,6 +196,51 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
|
|||
|
||||
gl.Disable(gl.SCISSOR_TEST)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// GoGLBackend is a canvas backend using Go-GL
|
||||
type GoGLBackend struct {
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
||||
*GLContext
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
tex uint32
|
||||
w int
|
||||
h int
|
||||
renderStencilBuf uint32
|
||||
frameBuf uint32
|
||||
alpha bool
|
||||
}
|
||||
|
||||
// New returns a new canvas backend. x, y, w, h define the target
|
||||
// rectangle in the window. ctx is a GLContext created with
|
||||
// NewGLContext, but can be nil for a default one. It makes sense
|
||||
// to pass one in when using for example an onscreen and an
|
||||
// offscreen backend using the same GL context.
|
||||
func New(x, y, w, h int, ctx *GLContext) (*GoGLBackend, error) {
|
||||
if ctx == nil {
|
||||
var err error
|
||||
ctx, err = NewGLContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b := &GoGLBackend{
|
||||
w: w,
|
||||
h: h,
|
||||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
GLContext: ctx,
|
||||
}
|
||||
|
||||
b.activateFn = func() {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
||||
|
@ -222,6 +252,8 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
|
|||
return b, nil
|
||||
}
|
||||
|
||||
// GoGLBackendOffscreen is a canvas backend using an offscreen
|
||||
// texture
|
||||
type GoGLBackendOffscreen struct {
|
||||
GoGLBackend
|
||||
|
||||
|
@ -229,8 +261,13 @@ type GoGLBackendOffscreen struct {
|
|||
offscrImg Image
|
||||
}
|
||||
|
||||
func NewOffscreen(w, h int, alpha bool) (*GoGLBackendOffscreen, error) {
|
||||
b, err := New(0, 0, w, h)
|
||||
// NewOffscreen returns a new offscreen canvas backend. w, h define
|
||||
// the size of the offscreen texture. ctx is a GLContext created
|
||||
// with NewGLContext, but can be nil for a default one. It makes
|
||||
// sense to pass one in when using for example an onscreen and an
|
||||
// offscreen backend using the same GL context.
|
||||
func NewOffscreen(w, h int, alpha bool, ctx *GLContext) (*GoGLBackendOffscreen, error) {
|
||||
b, err := New(0, 0, w, h, ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -267,14 +304,16 @@ func (b *GoGLBackend) SetBounds(x, y, w, h int) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetBounds updates the size of the offscreen texture
|
||||
func (b *GoGLBackendOffscreen) SetBounds(w, h int) {
|
||||
// SetSize updates the size of the offscreen texture
|
||||
func (b *GoGLBackendOffscreen) SetSize(w, h int) {
|
||||
b.GoGLBackend.SetBounds(0, 0, w, h)
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
b.offscrImg.w = b.offscrBuf.w
|
||||
b.offscrImg.h = b.offscrBuf.h
|
||||
}
|
||||
|
||||
// Size returns the size of the window or offscreen
|
||||
// texture
|
||||
func (b *GoGLBackend) Size() (int, int) {
|
||||
return b.w, b.h
|
||||
}
|
||||
|
@ -308,21 +347,31 @@ func (b *GoGLBackend) runGLQueue() {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete deletes the offscreen texture. After calling this
|
||||
// the backend can no longer be used
|
||||
func (b *GoGLBackendOffscreen) Delete() {
|
||||
gl.DeleteTextures(1, &b.offscrBuf.tex)
|
||||
gl.DeleteFramebuffers(1, &b.offscrBuf.frameBuf)
|
||||
gl.DeleteRenderbuffers(1, &b.offscrBuf.renderStencilBuf)
|
||||
}
|
||||
|
||||
// CanUseAsImage returns true if the given backend can be
|
||||
// directly used by this backend to avoid a conversion.
|
||||
// Used internally
|
||||
func (b *GoGLBackend) CanUseAsImage(b2 backendbase.Backend) bool {
|
||||
_, ok := b2.(*GoGLBackendOffscreen)
|
||||
return ok
|
||||
}
|
||||
|
||||
// AsImage returns nil, since this backend cannot be directly
|
||||
// used as an image. Used internally
|
||||
func (b *GoGLBackend) AsImage() backendbase.Image {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsImage returns an implementation of the Image interface
|
||||
// that can be used to render this offscreen texture
|
||||
// directly. Used internally
|
||||
func (b *GoGLBackendOffscreen) AsImage() backendbase.Image {
|
||||
return &b.offscrImg
|
||||
}
|
||||
|
@ -506,8 +555,8 @@ func (b *GoGLBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
|||
|
||||
type vec [2]float64
|
||||
|
||||
func (v1 vec) sub(v2 vec) vec {
|
||||
return vec{v1[0] - v2[0], v1[1] - v2[1]}
|
||||
func (v vec) sub(v2 vec) vec {
|
||||
return vec{v[0] - v2[0], v[1] - v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) len() float64 {
|
||||
|
|
|
@ -248,16 +248,16 @@ func rewrite(filename, src string) (string, string) {
|
|||
}
|
||||
|
||||
func rewriteMain(src string) string {
|
||||
src = strings.Replace(src, "type GLContext struct {\n",
|
||||
"type GLContext struct {\n\tglctx gl.Context\n\n", 1)
|
||||
src = strings.Replace(src, "ctx := &GLContext{\n",
|
||||
"ctx := &GLContext{\n\t\tglctx: glctx,\n\n", 1)
|
||||
src = strings.Replace(src, "\tb.glctx.GetError() // clear error state\n",
|
||||
"\tb := &XMobileBackend{GLContext: ctx}\n\n\tb.glctx.GetError() // clear error state\n\n", 1)
|
||||
src = strings.Replace(src, "type XMobileBackend struct {\n",
|
||||
"type XMobileBackend struct {\n\tglctx gl.Context\n\n", 1)
|
||||
src = strings.Replace(src, "func New(x, y, w, h int) (*XMobileBackend, error) {",
|
||||
"func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {", 1)
|
||||
src = strings.Replace(src, "func NewOffscreen(w, h int, alpha bool) (*XMobileBackendOffscreen, error)",
|
||||
"func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackendOffscreen, error)", 1)
|
||||
|
||||
src = rewriteCalls(src, "New", func(params []string) string {
|
||||
return "New(glctx, " + strings.Join(params, ", ") + ")"
|
||||
})
|
||||
"type XMobileBackend struct {\n", 1)
|
||||
src = strings.Replace(src, "func NewGLContext() (*GLContext, error) {",
|
||||
"func NewGLContext(glctx gl.Context) (*GLContext, error) {", 1)
|
||||
|
||||
src = strings.Replace(src,
|
||||
` err := gl.Init()
|
||||
|
@ -268,8 +268,37 @@ func rewriteMain(src string) string {
|
|||
`, ` var err error
|
||||
|
||||
`, 1)
|
||||
src = strings.Replace(src, "\tb := &XMobileBackend{\n",
|
||||
"\tb := &XMobileBackend{\n\t\tglctx: glctx,\n\n", 1)
|
||||
|
||||
src = strings.Replace(src,
|
||||
`// New returns a new canvas backend. x, y, w, h define the target
|
||||
// rectangle in the window. ctx is a GLContext created with
|
||||
// NewGLContext, but can be nil for a default one. It makes sense
|
||||
// to pass one in when using for example an onscreen and an
|
||||
// offscreen backend using the same GL context.
|
||||
`, `// New returns a new canvas backend. x, y, w, h define the target
|
||||
// rectangle in the window. ctx is a GLContext created with
|
||||
// NewGLContext
|
||||
`, 1)
|
||||
src = strings.Replace(src,
|
||||
`// NewOffscreen returns a new offscreen canvas backend. w, h define
|
||||
// the size of the offscreen texture. ctx is a GLContext created
|
||||
// with NewGLContext, but can be nil for a default one. It makes
|
||||
// sense to pass one in when using for example an onscreen and an
|
||||
// offscreen backend using the same GL context.
|
||||
`, `// NewOffscreen returns a new offscreen canvas backend. w, h define
|
||||
// the size of the offscreen texture. ctx is a GLContext created
|
||||
// with NewGLContext
|
||||
`, 1)
|
||||
src = strings.Replace(src,
|
||||
` if ctx == nil {
|
||||
var err error
|
||||
ctx, err = NewGLContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
`, "", 1)
|
||||
|
||||
src = strings.Replace(src,
|
||||
` buf uint32
|
||||
|
|
|
@ -27,15 +27,13 @@ type gradient struct {
|
|||
b *XMobileBackend
|
||||
tex gl.Texture
|
||||
loaded bool
|
||||
deleted bool
|
||||
opaque bool
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) LoadLinearGradient(data backendbase.Gradient) backendbase.LinearGradient {
|
||||
b.activate()
|
||||
|
||||
lg := &LinearGradient{
|
||||
gradient: gradient{b: b, opaque: true},
|
||||
gradient: gradient{b: b},
|
||||
}
|
||||
lg.tex = b.glctx.CreateTexture()
|
||||
b.glctx.ActiveTexture(gl.TEXTURE0)
|
||||
|
@ -57,7 +55,7 @@ func (b *XMobileBackend) LoadRadialGradient(data backendbase.Gradient) backendba
|
|||
b.activate()
|
||||
|
||||
rg := &RadialGradient{
|
||||
gradient: gradient{b: b, opaque: true},
|
||||
gradient: gradient{b: b},
|
||||
}
|
||||
rg.tex = b.glctx.CreateTexture()
|
||||
b.glctx.ActiveTexture(gl.TEXTURE0)
|
||||
|
@ -81,12 +79,8 @@ func (g *gradient) Delete() {
|
|||
g.b.activate()
|
||||
|
||||
b.glctx.DeleteTexture(g.tex)
|
||||
g.deleted = true
|
||||
}
|
||||
|
||||
func (g *gradient) IsDeleted() bool { return g.deleted }
|
||||
func (g *gradient) IsOpaque() bool { return g.opaque }
|
||||
|
||||
func (lg *LinearGradient) Replace(data backendbase.Gradient) { lg.load(data) }
|
||||
func (rg *RadialGradient) Replace(data backendbase.Gradient) { rg.load(data) }
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ type Image struct {
|
|||
b *XMobileBackend
|
||||
w, h int
|
||||
tex gl.Texture
|
||||
deleted bool
|
||||
opaque bool
|
||||
flip bool
|
||||
}
|
||||
|
||||
|
@ -38,11 +36,9 @@ func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
|||
img.b = b
|
||||
|
||||
runtime.SetFinalizer(img, func(img *Image) {
|
||||
if !img.deleted {
|
||||
b.glChan <- func() {
|
||||
b.glctx.DeleteTexture(img.tex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return img, nil
|
||||
|
@ -74,19 +70,7 @@ func loadImage(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, erro
|
|||
}
|
||||
|
||||
func loadImageRGBA(b *XMobileBackend, src *image.RGBA, tex gl.Texture) (*Image, error) {
|
||||
img := &Image{tex: tex, 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
|
||||
}
|
||||
}
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
||||
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
|
@ -147,7 +131,7 @@ func loadImageGray(b *XMobileBackend, src *image.Gray, tex gl.Texture) (*Image,
|
|||
}
|
||||
|
||||
func loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture) (*Image, error) {
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy(), opaque: true}
|
||||
img := &Image{tex: tex, w: src.Bounds().Dx(), h: src.Bounds().Dy()}
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
|
@ -161,9 +145,6 @@ func loadImageConverted(b *XMobileBackend, src image.Image, tex gl.Texture) (*Im
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
b.glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.w, img.h, gl.RGBA, gl.UNSIGNED_BYTE, data[0:])
|
||||
|
@ -193,13 +174,8 @@ func (img *Image) Delete() {
|
|||
img.b.activate()
|
||||
|
||||
b.glctx.DeleteTexture(img.tex)
|
||||
img.deleted = true
|
||||
}
|
||||
|
||||
// IsDeleted returns true if the Delete function has been
|
||||
// called on this image
|
||||
func (img *Image) IsDeleted() bool { return img.deleted }
|
||||
|
||||
// Replace replaces the image with the new one
|
||||
func (img *Image) Replace(src image.Image) error {
|
||||
b := img.b
|
||||
|
@ -216,10 +192,6 @@ func (img *Image) Replace(src image.Image) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// IsOpaque returns true if all pixels in the image
|
||||
// have a full alpha value
|
||||
func (img *Image) IsOpaque() bool { return img.opaque }
|
||||
|
||||
func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64, pts [4][2]float64, alpha float64) {
|
||||
b.activate()
|
||||
|
||||
|
|
|
@ -15,12 +15,11 @@ const alphaTexSize = 2048
|
|||
|
||||
var zeroes [alphaTexSize]byte
|
||||
|
||||
type XMobileBackend struct {
|
||||
// GLContext is a context that contains all the
|
||||
// shaders and buffers necessary for rendering
|
||||
type GLContext struct {
|
||||
glctx gl.Context
|
||||
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
||||
buf gl.Buffer
|
||||
shadowBuf gl.Buffer
|
||||
alphaTex gl.Texture
|
||||
|
@ -47,157 +46,145 @@ type XMobileBackend struct {
|
|||
ptsBuf []float32
|
||||
|
||||
glChan chan func()
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
tex gl.Texture
|
||||
w int
|
||||
h int
|
||||
renderStencilBuf gl.Renderbuffer
|
||||
frameBuf gl.Framebuffer
|
||||
alpha bool
|
||||
}
|
||||
|
||||
func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
|
||||
var err error
|
||||
|
||||
b := &XMobileBackend{
|
||||
// NewGLContext creates all the necessary GL resources,
|
||||
// like shaders and buffers
|
||||
func NewGLContext(glctx gl.Context) (*GLContext, error) {
|
||||
ctx := &GLContext{
|
||||
glctx: glctx,
|
||||
|
||||
w: w,
|
||||
h: h,
|
||||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
ptsBuf: make([]float32, 0, 4096),
|
||||
glChan: make(chan func()),
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
b := &XMobileBackend{GLContext: ctx}
|
||||
|
||||
b.glctx.GetError() // clear error state
|
||||
|
||||
err = loadShader(b, solidVS, solidFS, &b.sr.shaderProgram)
|
||||
err = loadShader(b, solidVS, solidFS, &ctx.sr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.sr.shaderProgram.mustLoadLocations(&b.sr)
|
||||
ctx.sr.shaderProgram.mustLoadLocations(&ctx.sr)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, linearGradientVS, linearGradientFS, &b.lgr.shaderProgram)
|
||||
err = loadShader(b, linearGradientVS, linearGradientFS, &ctx.lgr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.lgr.shaderProgram.mustLoadLocations(&b.lgr)
|
||||
ctx.lgr.shaderProgram.mustLoadLocations(&ctx.lgr)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, radialGradientVS, radialGradientFS, &b.rgr.shaderProgram)
|
||||
err = loadShader(b, radialGradientVS, radialGradientFS, &ctx.rgr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.rgr.shaderProgram.mustLoadLocations(&b.rgr)
|
||||
ctx.rgr.shaderProgram.mustLoadLocations(&ctx.rgr)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, imagePatternVS, imagePatternFS, &b.ipr.shaderProgram)
|
||||
err = loadShader(b, imagePatternVS, imagePatternFS, &ctx.ipr.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ipr.shaderProgram.mustLoadLocations(&b.ipr)
|
||||
ctx.ipr.shaderProgram.mustLoadLocations(&ctx.ipr)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, solidAlphaVS, solidAlphaFS, &b.sar.shaderProgram)
|
||||
err = loadShader(b, solidAlphaVS, solidAlphaFS, &ctx.sar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.sar.shaderProgram.mustLoadLocations(&b.sar)
|
||||
ctx.sar.shaderProgram.mustLoadLocations(&ctx.sar)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, linearGradientAlphaVS, linearGradientFS, &b.lgar.shaderProgram)
|
||||
err = loadShader(b, linearGradientAlphaVS, linearGradientFS, &ctx.lgar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.lgar.shaderProgram.mustLoadLocations(&b.lgar)
|
||||
ctx.lgar.shaderProgram.mustLoadLocations(&ctx.lgar)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, radialGradientAlphaVS, radialGradientAlphaFS, &b.rgar.shaderProgram)
|
||||
err = loadShader(b, radialGradientAlphaVS, radialGradientAlphaFS, &ctx.rgar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.rgar.shaderProgram.mustLoadLocations(&b.rgar)
|
||||
ctx.rgar.shaderProgram.mustLoadLocations(&ctx.rgar)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, imagePatternAlphaVS, imagePatternAlphaFS, &b.ipar.shaderProgram)
|
||||
err = loadShader(b, imagePatternAlphaVS, imagePatternAlphaFS, &ctx.ipar.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ipar.shaderProgram.mustLoadLocations(&b.ipar)
|
||||
ctx.ipar.shaderProgram.mustLoadLocations(&ctx.ipar)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, imageVS, imageFS, &b.ir.shaderProgram)
|
||||
err = loadShader(b, imageVS, imageFS, &ctx.ir.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.ir.shaderProgram.mustLoadLocations(&b.ir)
|
||||
ctx.ir.shaderProgram.mustLoadLocations(&ctx.ir)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, gaussian15VS, gaussian15FS, &b.gauss15r.shaderProgram)
|
||||
err = loadShader(b, gaussian15VS, gaussian15FS, &ctx.gauss15r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss15r.shaderProgram.mustLoadLocations(&b.gauss15r)
|
||||
ctx.gauss15r.shaderProgram.mustLoadLocations(&ctx.gauss15r)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, gaussian63VS, gaussian63FS, &b.gauss63r.shaderProgram)
|
||||
err = loadShader(b, gaussian63VS, gaussian63FS, &ctx.gauss63r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss63r.shaderProgram.mustLoadLocations(&b.gauss63r)
|
||||
ctx.gauss63r.shaderProgram.mustLoadLocations(&ctx.gauss63r)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = loadShader(b, gaussian127VS, gaussian127FS, &b.gauss127r.shaderProgram)
|
||||
err = loadShader(b, gaussian127VS, gaussian127FS, &ctx.gauss127r.shaderProgram)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.gauss127r.shaderProgram.mustLoadLocations(&b.gauss127r)
|
||||
ctx.gauss127r.shaderProgram.mustLoadLocations(&ctx.gauss127r)
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.buf = b.glctx.CreateBuffer()
|
||||
ctx.buf = b.glctx.CreateBuffer()
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.shadowBuf = b.glctx.CreateBuffer()
|
||||
ctx.shadowBuf = b.glctx.CreateBuffer()
|
||||
if err = glError(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.glctx.ActiveTexture(gl.TEXTURE0)
|
||||
b.alphaTex = b.glctx.CreateTexture()
|
||||
b.glctx.BindTexture(gl.TEXTURE_2D, b.alphaTex)
|
||||
ctx.alphaTex = b.glctx.CreateTexture()
|
||||
b.glctx.BindTexture(gl.TEXTURE_2D, ctx.alphaTex)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
b.glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
|
@ -214,6 +201,41 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
|
|||
|
||||
b.glctx.Disable(gl.SCISSOR_TEST)
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// XMobileBackend is a canvas backend using Go-GL
|
||||
type XMobileBackend struct {
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
||||
*GLContext
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
tex gl.Texture
|
||||
w int
|
||||
h int
|
||||
renderStencilBuf gl.Renderbuffer
|
||||
frameBuf gl.Framebuffer
|
||||
alpha bool
|
||||
}
|
||||
|
||||
// New returns a new canvas backend. x, y, w, h define the target
|
||||
// rectangle in the window. ctx is a GLContext created with
|
||||
// NewGLContext
|
||||
func New(x, y, w, h int, ctx *GLContext) (*XMobileBackend, error) {
|
||||
b := &XMobileBackend{
|
||||
w: w,
|
||||
h: h,
|
||||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
GLContext: ctx,
|
||||
}
|
||||
|
||||
b.activateFn = func() {
|
||||
b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
|
||||
b.glctx.Viewport(b.x, b.y, b.w, b.h)
|
||||
|
@ -225,6 +247,8 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
|
|||
return b, nil
|
||||
}
|
||||
|
||||
// XMobileBackendOffscreen is a canvas backend using an offscreen
|
||||
// texture
|
||||
type XMobileBackendOffscreen struct {
|
||||
XMobileBackend
|
||||
|
||||
|
@ -232,8 +256,11 @@ type XMobileBackendOffscreen struct {
|
|||
offscrImg Image
|
||||
}
|
||||
|
||||
func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackendOffscreen, error) {
|
||||
b, err := New(glctx, 0, 0, w, h)
|
||||
// NewOffscreen returns a new offscreen canvas backend. w, h define
|
||||
// the size of the offscreen texture. ctx is a GLContext created
|
||||
// with NewGLContext
|
||||
func NewOffscreen(w, h int, alpha bool, ctx *GLContext) (*XMobileBackendOffscreen, error) {
|
||||
b, err := New(0, 0, w, h, ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -270,14 +297,16 @@ func (b *XMobileBackend) SetBounds(x, y, w, h int) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetBounds updates the size of the offscreen texture
|
||||
func (b *XMobileBackendOffscreen) SetBounds(w, h int) {
|
||||
// SetSize updates the size of the offscreen texture
|
||||
func (b *XMobileBackendOffscreen) SetSize(w, h int) {
|
||||
b.XMobileBackend.SetBounds(0, 0, w, h)
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
b.offscrImg.w = b.offscrBuf.w
|
||||
b.offscrImg.h = b.offscrBuf.h
|
||||
}
|
||||
|
||||
// Size returns the size of the window or offscreen
|
||||
// texture
|
||||
func (b *XMobileBackend) Size() (int, int) {
|
||||
return b.w, b.h
|
||||
}
|
||||
|
@ -311,21 +340,31 @@ func (b *XMobileBackend) runGLQueue() {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete deletes the offscreen texture. After calling this
|
||||
// the backend can no longer be used
|
||||
func (b *XMobileBackendOffscreen) Delete() {
|
||||
b.glctx.DeleteTexture(b.offscrBuf.tex)
|
||||
b.glctx.DeleteFramebuffer(b.offscrBuf.frameBuf)
|
||||
b.glctx.DeleteRenderbuffer(b.offscrBuf.renderStencilBuf)
|
||||
}
|
||||
|
||||
// CanUseAsImage returns true if the given backend can be
|
||||
// directly used by this backend to avoid a conversion.
|
||||
// Used internally
|
||||
func (b *XMobileBackend) CanUseAsImage(b2 backendbase.Backend) bool {
|
||||
_, ok := b2.(*XMobileBackendOffscreen)
|
||||
return ok
|
||||
}
|
||||
|
||||
// AsImage returns nil, since this backend cannot be directly
|
||||
// used as an image. Used internally
|
||||
func (b *XMobileBackend) AsImage() backendbase.Image {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsImage returns an implementation of the Image interface
|
||||
// that can be used to render this offscreen texture
|
||||
// directly. Used internally
|
||||
func (b *XMobileBackendOffscreen) AsImage() backendbase.Image {
|
||||
return &b.offscrImg
|
||||
}
|
||||
|
@ -509,8 +548,8 @@ func (b *XMobileBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
|||
|
||||
type vec [2]float64
|
||||
|
||||
func (v1 vec) sub(v2 vec) vec {
|
||||
return vec{v1[0] - v2[0], v1[1] - v2[1]}
|
||||
func (v vec) sub(v2 vec) vec {
|
||||
return vec{v[0] - v2[0], v[1] - v2[1]}
|
||||
}
|
||||
|
||||
func (v vec) len() float64 {
|
||||
|
|
|
@ -18,7 +18,7 @@ func OnSurfaceCreated() {
|
|||
}
|
||||
|
||||
func OnSurfaceChanged(w, h int) {
|
||||
backend, err := goglbackend.New(0, 0, w, h)
|
||||
backend, err := goglbackend.New(0, 0, w, h, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func main() {
|
|||
gl.Enable(gl.MULTISAMPLE)
|
||||
|
||||
// load GL backend
|
||||
backend, err := goglbackend.New(0, 0, 0, 0)
|
||||
backend, err := goglbackend.New(0, 0, 0, 0, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading canvas GL assets: %v", err)
|
||||
}
|
||||
|
|
|
@ -29,11 +29,15 @@ func main() {
|
|||
case lifecycle.CrossOn:
|
||||
var err error
|
||||
glctx = e.DrawContext.(gl.Context)
|
||||
cvb, err = xmobilebackend.NewOffscreen(glctx, 0, 0, false)
|
||||
ctx, err := xmobilebackend.NewGLContext(glctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cvb, err = xmobilebackend.NewOffscreen(0, 0, false, ctx)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
painterb, err = xmobilebackend.New(glctx, 0, 0, 0, 0)
|
||||
painterb, err = xmobilebackend.New(0, 0, 0, 0, ctx)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
@ -51,7 +55,7 @@ func main() {
|
|||
fw, fh := float64(w), float64(h)
|
||||
color := math.Sin(float64(time.Now().UnixNano())*0.000000002)*0.3 + 0.7
|
||||
|
||||
cvb.SetBounds(w, h)
|
||||
cvb.SetSize(w, h)
|
||||
cv.SetFillStyle(color*0.2, color*0.2, color*0.8)
|
||||
cv.FillRect(fw*0.25, fh*0.25, fw*0.5, fh*0.5)
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ func TouchEvent(typ string, x, y int) {
|
|||
}
|
||||
|
||||
func LoadGL(w, h int) {
|
||||
backend, err := goglbackend.New(0, 0, w, h)
|
||||
backend, err := goglbackend.New(0, 0, w, h, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func main() {
|
|||
gl.Enable(gl.MULTISAMPLE)
|
||||
|
||||
// load GL backend
|
||||
backend, err := goglbackend.New(0, 0, 0, 0)
|
||||
backend, err := goglbackend.New(0, 0, 0, 0, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading canvas GL assets: %v", err)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,11 @@ func main() {
|
|||
gldriver.Main(func(s screen.Screen) {
|
||||
glw := glwidget.NewGL(draw)
|
||||
sheet = widget.NewSheet(glw)
|
||||
backend, err := xmobilebackend.New(glw.Ctx, 0, 0, 600, 600)
|
||||
ctx, err := xmobilebackend.NewGLContext(glw.Ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
backend, err := xmobilebackend.New(0, 0, 600, 600, ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
|
|||
gl.Enable(gl.MULTISAMPLE)
|
||||
|
||||
// load canvas GL backend
|
||||
backend, err := goglbackend.New(0, 0, w, h)
|
||||
backend, err := goglbackend.New(0, 0, w, h, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error loading GoGL backend: %v", err)
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
|
|||
}
|
||||
|
||||
// load canvas GL backend
|
||||
backend, err := goglbackend.New(0, 0, w, h)
|
||||
backend, err := goglbackend.New(0, 0, w, h, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error loading GoGL backend: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue