offscreen backend now has its own type, can be used as image when drawing images
This commit is contained in:
parent
122488e64c
commit
9c3cccabdd
9 changed files with 176 additions and 94 deletions
|
@ -26,6 +26,9 @@ type Backend interface {
|
|||
|
||||
GetImageData(x, y, w, h int) *image.RGBA
|
||||
PutImageData(img *image.RGBA, x, y int)
|
||||
|
||||
CanUseAsImage(b Backend) bool
|
||||
AsImage() Image // can return nil if not supported
|
||||
}
|
||||
|
||||
// FillStyle is the color and other details on how to fill
|
||||
|
|
|
@ -42,11 +42,10 @@ type GoGLBackend struct {
|
|||
|
||||
ptsBuf []float32
|
||||
|
||||
offscreen bool
|
||||
offscrBuf offscreenBuffer
|
||||
offscrImg Image
|
||||
|
||||
glChan chan func()
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
|
@ -70,6 +69,7 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
|
|||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
ptsBuf: make([]float32, 0, 4096),
|
||||
glChan: make(chan func()),
|
||||
}
|
||||
|
||||
gl.GetError() // clear error state
|
||||
|
@ -211,26 +211,54 @@ func New(x, y, w, h int) (*GoGLBackend, error) {
|
|||
|
||||
gl.Disable(gl.SCISSOR_TEST)
|
||||
|
||||
b.activateFn = func() {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
||||
}
|
||||
b.disableTextureRenderTarget = func() {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func NewOffscreen(w, h int, alpha bool) (*GoGLBackend, error) {
|
||||
type GoGLBackendOffscreen struct {
|
||||
GoGLBackend
|
||||
|
||||
offscrBuf offscreenBuffer
|
||||
offscrImg Image
|
||||
}
|
||||
|
||||
func NewOffscreen(w, h int, alpha bool) (*GoGLBackendOffscreen, error) {
|
||||
b, err := New(0, 0, w, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.offscreen = true
|
||||
b.offscrBuf.alpha = alpha
|
||||
return b, nil
|
||||
bo := &GoGLBackendOffscreen{}
|
||||
bo.offscrBuf.alpha = alpha
|
||||
|
||||
b.activateFn = func() {
|
||||
b.enableTextureRenderTarget(&bo.offscrBuf)
|
||||
gl.Viewport(0, 0, int32(b.w), int32(b.h))
|
||||
bo.offscrImg.w = bo.offscrBuf.w
|
||||
bo.offscrImg.h = bo.offscrBuf.h
|
||||
bo.offscrImg.tex = bo.offscrBuf.tex
|
||||
bo.offscrImg.flip = true
|
||||
}
|
||||
b.disableTextureRenderTarget = func() {
|
||||
b.enableTextureRenderTarget(&bo.offscrBuf)
|
||||
}
|
||||
|
||||
bo.GoGLBackend = *b
|
||||
|
||||
return bo, nil
|
||||
}
|
||||
|
||||
// SetBounds updates the bounds of the canvas. This would
|
||||
// usually be called for example when the window is resized
|
||||
func (b *GoGLBackend) SetBounds(x, y, w, h int) {
|
||||
if !b.offscreen {
|
||||
b.x, b.y = x, y
|
||||
b.fx, b.fy = float64(x), float64(y)
|
||||
}
|
||||
b.w, b.h = w, h
|
||||
b.fw, b.fh = float64(w), float64(h)
|
||||
if b == activeContext {
|
||||
|
@ -239,6 +267,11 @@ func (b *GoGLBackend) SetBounds(x, y, w, h int) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetBounds updates the size of the offscreen texture
|
||||
func (b *GoGLBackendOffscreen) SetBounds(w, h int) {
|
||||
b.GoGLBackend.SetBounds(0, 0, w, h)
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) Size() (int, int) {
|
||||
return b.w, b.h
|
||||
}
|
||||
|
@ -256,39 +289,39 @@ var activeContext *GoGLBackend
|
|||
func (b *GoGLBackend) activate() {
|
||||
if activeContext != b {
|
||||
activeContext = b
|
||||
if b.offscreen {
|
||||
gl.Viewport(0, 0, int32(b.w), int32(b.h))
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
b.offscrImg.w = b.offscrBuf.w
|
||||
b.offscrImg.h = b.offscrBuf.h
|
||||
b.offscrImg.tex = b.offscrBuf.tex
|
||||
} else {
|
||||
gl.Viewport(int32(b.x), int32(b.y), int32(b.w), int32(b.h))
|
||||
b.disableTextureRenderTarget()
|
||||
}
|
||||
b.activateFn()
|
||||
}
|
||||
b.runGLQueue()
|
||||
}
|
||||
|
||||
loop:
|
||||
func (b *GoGLBackend) runGLQueue() {
|
||||
for {
|
||||
select {
|
||||
case f := <-b.glChan:
|
||||
f()
|
||||
default:
|
||||
break loop
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) DeleteOffscreen() {
|
||||
if !b.offscreen {
|
||||
return
|
||||
}
|
||||
func (b *GoGLBackendOffscreen) Delete() {
|
||||
gl.DeleteTextures(1, &b.offscrBuf.tex)
|
||||
gl.DeleteFramebuffers(1, &b.offscrBuf.frameBuf)
|
||||
gl.DeleteRenderbuffers(1, &b.offscrBuf.renderStencilBuf)
|
||||
b.offscreen = false
|
||||
}
|
||||
|
||||
b.activate()
|
||||
func (b *GoGLBackend) CanUseAsImage(b2 backendbase.Backend) bool {
|
||||
_, ok := b2.(*GoGLBackendOffscreen)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) AsImage() backendbase.Image {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *GoGLBackendOffscreen) AsImage() backendbase.Image {
|
||||
return &b.offscrImg
|
||||
}
|
||||
|
||||
type glColor struct {
|
||||
|
@ -463,14 +496,6 @@ func (b *GoGLBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) disableTextureRenderTarget() {
|
||||
if b.offscreen {
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
} else {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
}
|
||||
}
|
||||
|
||||
type vec [2]float64
|
||||
|
||||
func (v1 vec) sub(v2 vec) vec {
|
||||
|
|
|
@ -17,6 +17,7 @@ type Image struct {
|
|||
tex uint32
|
||||
deleted bool
|
||||
opaque bool
|
||||
flip bool
|
||||
}
|
||||
|
||||
func (b *GoGLBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
||||
|
@ -34,6 +35,7 @@ func (b *GoGLBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.b = b
|
||||
|
||||
runtime.SetFinalizer(img, func(img *Image) {
|
||||
if !img.deleted {
|
||||
|
@ -207,6 +209,7 @@ func (img *Image) Replace(src image.Image) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newImg.b = img.b
|
||||
*img = *newImg
|
||||
return nil
|
||||
}
|
||||
|
@ -225,6 +228,11 @@ func (b *GoGLBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float64,
|
|||
sw /= float64(img.w)
|
||||
sh /= float64(img.h)
|
||||
|
||||
if img.flip {
|
||||
sy += sh
|
||||
sh = -sh
|
||||
}
|
||||
|
||||
var buf [16]float32
|
||||
data := buf[:0]
|
||||
for _, pt := range pts {
|
||||
|
|
|
@ -252,8 +252,8 @@ func rewriteMain(src string) string {
|
|||
"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) (*XMobileBackend, error)",
|
||||
"func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*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, ", ") + ")"
|
||||
|
|
|
@ -17,6 +17,7 @@ type Image struct {
|
|||
tex gl.Texture
|
||||
deleted bool
|
||||
opaque bool
|
||||
flip bool
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
||||
|
@ -34,6 +35,7 @@ func (b *XMobileBackend) LoadImage(src image.Image) (backendbase.Image, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.b = b
|
||||
|
||||
runtime.SetFinalizer(img, func(img *Image) {
|
||||
if !img.deleted {
|
||||
|
@ -209,6 +211,7 @@ func (img *Image) Replace(src image.Image) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newImg.b = img.b
|
||||
*img = *newImg
|
||||
return nil
|
||||
}
|
||||
|
@ -227,6 +230,11 @@ func (b *XMobileBackend) DrawImage(dimg backendbase.Image, sx, sy, sw, sh float6
|
|||
sw /= float64(img.w)
|
||||
sh /= float64(img.h)
|
||||
|
||||
if img.flip {
|
||||
sy += sh
|
||||
sh = -sh
|
||||
}
|
||||
|
||||
var buf [16]float32
|
||||
data := buf[:0]
|
||||
for _, pt := range pts {
|
||||
|
|
|
@ -46,11 +46,10 @@ type XMobileBackend struct {
|
|||
|
||||
ptsBuf []float32
|
||||
|
||||
offscreen bool
|
||||
offscrBuf offscreenBuffer
|
||||
offscrImg Image
|
||||
|
||||
glChan chan func()
|
||||
|
||||
activateFn func()
|
||||
disableTextureRenderTarget func()
|
||||
}
|
||||
|
||||
type offscreenBuffer struct {
|
||||
|
@ -73,6 +72,7 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
|
|||
fw: float64(w),
|
||||
fh: float64(h),
|
||||
ptsBuf: make([]float32, 0, 4096),
|
||||
glChan: make(chan func()),
|
||||
}
|
||||
|
||||
b.glctx.GetError() // clear error state
|
||||
|
@ -214,26 +214,54 @@ func New(glctx gl.Context, x, y, w, h int) (*XMobileBackend, error) {
|
|||
|
||||
b.glctx.Disable(gl.SCISSOR_TEST)
|
||||
|
||||
b.activateFn = func() {
|
||||
b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
|
||||
b.glctx.Viewport(b.x, b.y, b.w, b.h)
|
||||
}
|
||||
b.disableTextureRenderTarget = func() {
|
||||
b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackend, error) {
|
||||
type XMobileBackendOffscreen struct {
|
||||
XMobileBackend
|
||||
|
||||
offscrBuf offscreenBuffer
|
||||
offscrImg Image
|
||||
}
|
||||
|
||||
func NewOffscreen(glctx gl.Context, w, h int, alpha bool) (*XMobileBackendOffscreen, error) {
|
||||
b, err := New(glctx, 0, 0, w, h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.offscreen = true
|
||||
b.offscrBuf.alpha = alpha
|
||||
return b, nil
|
||||
bo := &XMobileBackendOffscreen{}
|
||||
bo.offscrBuf.alpha = alpha
|
||||
|
||||
b.activateFn = func() {
|
||||
b.enableTextureRenderTarget(&bo.offscrBuf)
|
||||
b.glctx.Viewport(0, 0, b.w, b.h)
|
||||
bo.offscrImg.w = bo.offscrBuf.w
|
||||
bo.offscrImg.h = bo.offscrBuf.h
|
||||
bo.offscrImg.tex = bo.offscrBuf.tex
|
||||
bo.offscrImg.flip = true
|
||||
}
|
||||
b.disableTextureRenderTarget = func() {
|
||||
b.enableTextureRenderTarget(&bo.offscrBuf)
|
||||
}
|
||||
|
||||
bo.XMobileBackend = *b
|
||||
|
||||
return bo, nil
|
||||
}
|
||||
|
||||
// SetBounds updates the bounds of the canvas. This would
|
||||
// usually be called for example when the window is resized
|
||||
func (b *XMobileBackend) SetBounds(x, y, w, h int) {
|
||||
if !b.offscreen {
|
||||
b.x, b.y = x, y
|
||||
b.fx, b.fy = float64(x), float64(y)
|
||||
}
|
||||
b.w, b.h = w, h
|
||||
b.fw, b.fh = float64(w), float64(h)
|
||||
if b == activeContext {
|
||||
|
@ -242,6 +270,11 @@ func (b *XMobileBackend) SetBounds(x, y, w, h int) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetBounds updates the size of the offscreen texture
|
||||
func (b *XMobileBackendOffscreen) SetBounds(w, h int) {
|
||||
b.XMobileBackend.SetBounds(0, 0, w, h)
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) Size() (int, int) {
|
||||
return b.w, b.h
|
||||
}
|
||||
|
@ -259,39 +292,39 @@ var activeContext *XMobileBackend
|
|||
func (b *XMobileBackend) activate() {
|
||||
if activeContext != b {
|
||||
activeContext = b
|
||||
if b.offscreen {
|
||||
b.glctx.Viewport(0, 0, b.w, b.h)
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
b.offscrImg.w = b.offscrBuf.w
|
||||
b.offscrImg.h = b.offscrBuf.h
|
||||
b.offscrImg.tex = b.offscrBuf.tex
|
||||
} else {
|
||||
b.glctx.Viewport(b.x, b.y, b.w, b.h)
|
||||
b.disableTextureRenderTarget()
|
||||
}
|
||||
b.activateFn()
|
||||
}
|
||||
b.runGLQueue()
|
||||
}
|
||||
|
||||
loop:
|
||||
func (b *XMobileBackend) runGLQueue() {
|
||||
for {
|
||||
select {
|
||||
case f := <-b.glChan:
|
||||
f()
|
||||
default:
|
||||
break loop
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) DeleteOffscreen() {
|
||||
if !b.offscreen {
|
||||
return
|
||||
}
|
||||
func (b *XMobileBackendOffscreen) Delete() {
|
||||
b.glctx.DeleteTexture(b.offscrBuf.tex)
|
||||
b.glctx.DeleteFramebuffer(b.offscrBuf.frameBuf)
|
||||
b.glctx.DeleteRenderbuffer(b.offscrBuf.renderStencilBuf)
|
||||
b.offscreen = false
|
||||
}
|
||||
|
||||
b.activate()
|
||||
func (b *XMobileBackend) CanUseAsImage(b2 backendbase.Backend) bool {
|
||||
_, ok := b2.(*XMobileBackendOffscreen)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) AsImage() backendbase.Image {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *XMobileBackendOffscreen) AsImage() backendbase.Image {
|
||||
return &b.offscrImg
|
||||
}
|
||||
|
||||
type glColor struct {
|
||||
|
@ -466,14 +499,6 @@ func (b *XMobileBackend) enableTextureRenderTarget(offscr *offscreenBuffer) {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *XMobileBackend) disableTextureRenderTarget() {
|
||||
if b.offscreen {
|
||||
b.enableTextureRenderTarget(&b.offscrBuf)
|
||||
} else {
|
||||
b.glctx.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{Value: 0})
|
||||
}
|
||||
}
|
||||
|
||||
type vec [2]float64
|
||||
|
||||
func (v1 vec) sub(v2 vec) vec {
|
||||
|
|
22
canvas.go
22
canvas.go
|
@ -20,9 +20,6 @@ import (
|
|||
type Canvas struct {
|
||||
b backendbase.Backend
|
||||
|
||||
w, h int
|
||||
fw, fh float64
|
||||
|
||||
path Path2D
|
||||
convex bool
|
||||
rect bool
|
||||
|
@ -138,8 +135,6 @@ func New(backend backendbase.Backend) *Canvas {
|
|||
stateStack: make([]drawState, 0, 20),
|
||||
images: make(map[interface{}]*Image),
|
||||
}
|
||||
w, h := backend.Size()
|
||||
cv.setBounds(w, h)
|
||||
cv.state.lineWidth = 1
|
||||
cv.state.lineAlpha = 1
|
||||
cv.state.miterLimitSqr = 100
|
||||
|
@ -150,19 +145,20 @@ func New(backend backendbase.Backend) *Canvas {
|
|||
return cv
|
||||
}
|
||||
|
||||
func (cv *Canvas) setBounds(w, h int) {
|
||||
cv.w, cv.h = w, h
|
||||
cv.fw, cv.fh = float64(w), float64(h)
|
||||
// Width returns the internal width of the canvas
|
||||
func (cv *Canvas) Width() int {
|
||||
w, _ := cv.b.Size()
|
||||
return w
|
||||
}
|
||||
|
||||
// Width returns the internal width of the canvas
|
||||
func (cv *Canvas) Width() int { return cv.w }
|
||||
|
||||
// Height returns the internal height of the canvas
|
||||
func (cv *Canvas) Height() int { return cv.h }
|
||||
func (cv *Canvas) Height() int {
|
||||
_, h := cv.b.Size()
|
||||
return h
|
||||
}
|
||||
|
||||
// Size returns the internal width and height of the canvas
|
||||
func (cv *Canvas) Size() (int, int) { return cv.w, cv.h }
|
||||
func (cv *Canvas) Size() (int, int) { return cv.b.Size() }
|
||||
|
||||
func (cv *Canvas) tf(v vec) vec {
|
||||
v, _ = v.mulMat(cv.state.transform)
|
||||
|
|
16
images.go
16
images.go
|
@ -42,7 +42,8 @@ func (cv *Canvas) LoadImage(src interface{}) (*Image, error) {
|
|||
return nil, err
|
||||
}
|
||||
case *Canvas:
|
||||
src = cv.GetImageData(0, 0, cv.Width(), cv.Height())
|
||||
w, h := cv.b.Size()
|
||||
src = cv.GetImageData(0, 0, w, h)
|
||||
default:
|
||||
return nil, errors.New("Unsupported source type")
|
||||
}
|
||||
|
@ -82,6 +83,19 @@ func (cv *Canvas) getImage(src interface{}) *Image {
|
|||
}
|
||||
cv.images[v] = img
|
||||
return img
|
||||
case *Canvas:
|
||||
if !cv.b.CanUseAsImage(v.b) {
|
||||
w, h := v.Size()
|
||||
return cv.getImage(v.GetImageData(0, 0, w, h))
|
||||
}
|
||||
bimg := v.b.AsImage()
|
||||
if bimg == nil {
|
||||
w, h := v.Size()
|
||||
return cv.getImage(v.GetImageData(0, 0, w, h))
|
||||
}
|
||||
img := &Image{cv: cv, img: bimg}
|
||||
cv.images[v] = img
|
||||
return img
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Unknown image type: %T\n", src)
|
||||
cv.images[src] = nil
|
||||
|
|
7
text.go
7
text.go
|
@ -96,14 +96,17 @@ func (cv *Canvas) FillText(str string, x, y float64) {
|
|||
curX += float64(kern) / 64
|
||||
}
|
||||
|
||||
w, h := cv.b.Size()
|
||||
fw, fh := float64(w), float64(h)
|
||||
|
||||
p0 := cv.tf(vec{float64(bounds.Min.X) + curX, float64(bounds.Min.Y) + y})
|
||||
p1 := cv.tf(vec{float64(bounds.Min.X) + curX, float64(bounds.Max.Y) + y})
|
||||
p2 := cv.tf(vec{float64(bounds.Max.X) + curX, float64(bounds.Max.Y) + y})
|
||||
p3 := cv.tf(vec{float64(bounds.Max.X) + curX, float64(bounds.Min.Y) + y})
|
||||
inside := (p0[0] >= 0 || p1[0] >= 0 || p2[0] >= 0 || p3[0] >= 0) &&
|
||||
(p0[1] >= 0 || p1[1] >= 0 || p2[1] >= 0 || p3[1] >= 0) &&
|
||||
(p0[0] < cv.fw || p1[0] < cv.fw || p2[0] < cv.fw || p3[0] < cv.fw) &&
|
||||
(p0[1] < cv.fh || p1[1] < cv.fh || p2[1] < cv.fh || p3[1] < cv.fh)
|
||||
(p0[0] < fw || p1[0] < fw || p2[0] < fw || p3[0] < fw) &&
|
||||
(p0[1] < fh || p1[1] < fh || p2[1] < fh || p3[1] < fh)
|
||||
|
||||
if !curInside && inside {
|
||||
curInside = true
|
||||
|
|
Loading…
Reference in a new issue