diff --git a/backend/gogl/gogl.go b/backend/gogl/gogl.go index a775445..db791b6 100644 --- a/backend/gogl/gogl.go +++ b/backend/gogl/gogl.go @@ -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 { diff --git a/backend/xmobile/gen/gen.go b/backend/xmobile/gen/gen.go index 2d54cc4..f62214b 100644 --- a/backend/xmobile/gen/gen.go +++ b/backend/xmobile/gen/gen.go @@ -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 diff --git a/backend/xmobile/gradients.go b/backend/xmobile/gradients.go index 5868c58..8163f64 100755 --- a/backend/xmobile/gradients.go +++ b/backend/xmobile/gradients.go @@ -24,18 +24,16 @@ type RadialGradient struct { } type gradient struct { - b *XMobileBackend - tex gl.Texture - loaded bool - deleted bool - opaque bool + b *XMobileBackend + tex gl.Texture + loaded 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) } diff --git a/backend/xmobile/images.go b/backend/xmobile/images.go index c88d3b2..3d63040 100755 --- a/backend/xmobile/images.go +++ b/backend/xmobile/images.go @@ -12,12 +12,10 @@ import ( // Image represents a loaded image that can be used in various drawing functions type Image struct { - b *XMobileBackend - w, h int - tex gl.Texture - deleted bool - opaque bool - flip bool + b *XMobileBackend + w, h int + tex gl.Texture + flip bool } func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) { @@ -38,10 +36,8 @@ 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) - } + b.glChan <- func() { + b.glctx.DeleteTexture(img.tex) } }) @@ -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() diff --git a/backend/xmobile/xmobile.go b/backend/xmobile/xmobile.go index f18642b..06c749b 100755 --- a/backend/xmobile/xmobile.go +++ b/backend/xmobile/xmobile.go @@ -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 { diff --git a/examples/android/android.go b/examples/android/android.go index dd38d46..0de6dde 100644 --- a/examples/android/android.go +++ b/examples/android/android.go @@ -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) } diff --git a/examples/glfw/glfw.go b/examples/glfw/glfw.go index 62dab3a..80df12b 100644 --- a/examples/glfw/glfw.go +++ b/examples/glfw/glfw.go @@ -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) } diff --git a/examples/gomobile/gomobile.go b/examples/gomobile/gomobile.go index 5d59041..8756283 100644 --- a/examples/gomobile/gomobile.go +++ b/examples/gomobile/gomobile.go @@ -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) diff --git a/examples/ios/ios.go b/examples/ios/ios.go index d0c8d7c..a952319 100644 --- a/examples/ios/ios.go +++ b/examples/ios/ios.go @@ -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) } diff --git a/examples/sdl/sdl.go b/examples/sdl/sdl.go index fd37747..d201d2b 100644 --- a/examples/sdl/sdl.go +++ b/examples/sdl/sdl.go @@ -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) } diff --git a/examples/shiny/shiny.go b/examples/shiny/shiny.go index 0568fa1..c42235a 100644 --- a/examples/shiny/shiny.go +++ b/examples/shiny/shiny.go @@ -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) } diff --git a/glfwcanvas/glfwcanvas.go b/glfwcanvas/glfwcanvas.go index 85e314f..727bae7 100644 --- a/glfwcanvas/glfwcanvas.go +++ b/glfwcanvas/glfwcanvas.go @@ -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) } diff --git a/sdlcanvas/sdlcanvas.go b/sdlcanvas/sdlcanvas.go index b8d4162..fa83d67 100644 --- a/sdlcanvas/sdlcanvas.go +++ b/sdlcanvas/sdlcanvas.go @@ -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) }