109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
package glhf
|
|
|
|
import (
|
|
"runtime"
|
|
|
|
"github.com/faiface/mainthread"
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
)
|
|
|
|
// Frame is a fixed resolution texture that you can draw on.
|
|
type Frame struct {
|
|
fb, rf, df binder // framebuffer, read framebuffer, draw framebuffer
|
|
tex *Texture
|
|
}
|
|
|
|
// NewFrame creates a new fully transparent Frame with given dimensions in pixels.
|
|
func NewFrame(width, height int, smooth bool) *Frame {
|
|
f := &Frame{
|
|
fb: binder{
|
|
restoreLoc: gl.FRAMEBUFFER_BINDING,
|
|
bindFunc: func(obj uint32) {
|
|
gl.BindFramebuffer(gl.FRAMEBUFFER, obj)
|
|
},
|
|
},
|
|
rf: binder{
|
|
restoreLoc: gl.READ_FRAMEBUFFER_BINDING,
|
|
bindFunc: func(obj uint32) {
|
|
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, obj)
|
|
},
|
|
},
|
|
df: binder{
|
|
restoreLoc: gl.DRAW_FRAMEBUFFER_BINDING,
|
|
bindFunc: func(obj uint32) {
|
|
gl.BindFramebuffer(gl.DRAW_FRAMEBUFFER, obj)
|
|
},
|
|
},
|
|
tex: NewTexture(width, height, smooth, make([]uint8, width*height*4)),
|
|
}
|
|
|
|
gl.GenFramebuffers(1, &f.fb.obj)
|
|
|
|
f.fb.bind()
|
|
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex.tex.obj, 0)
|
|
f.fb.restore()
|
|
|
|
runtime.SetFinalizer(f, (*Frame).delete)
|
|
|
|
return f
|
|
}
|
|
|
|
func (f *Frame) delete() {
|
|
mainthread.CallNonBlock(func() {
|
|
gl.DeleteFramebuffers(1, &f.fb.obj)
|
|
})
|
|
}
|
|
|
|
// ID returns the OpenGL framebuffer ID of this Frame.
|
|
func (f *Frame) ID() uint32 {
|
|
return f.fb.obj
|
|
}
|
|
|
|
// Begin binds the Frame. All draw operations will target this Frame until End is called.
|
|
func (f *Frame) Begin() {
|
|
f.fb.bind()
|
|
}
|
|
|
|
// End unbinds the Frame. All draw operations will go to whatever was bound before this Frame.
|
|
func (f *Frame) End() {
|
|
f.fb.restore()
|
|
}
|
|
|
|
// Blit copies rectangle (sx0, sy0, sx1, sy1) in this Frame onto rectangle (dx0, dy0, dx1, dy1) in
|
|
// dst Frame.
|
|
//
|
|
// If the dst Frame is nil, the destination will be the framebuffer 0, which is the screen.
|
|
//
|
|
// If the sizes of the rectangles don't match, the source will be stretched to fit the destination
|
|
// rectangle. The stretch will be either smooth or pixely according to the source Frame's
|
|
// smoothness.
|
|
func (f *Frame) Blit(dst *Frame, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int) {
|
|
f.rf.obj = f.fb.obj
|
|
if dst != nil {
|
|
f.df.obj = dst.fb.obj
|
|
} else {
|
|
f.df.obj = 0
|
|
}
|
|
f.rf.bind()
|
|
f.df.bind()
|
|
|
|
filter := gl.NEAREST
|
|
if f.tex.smooth {
|
|
filter = gl.LINEAR
|
|
}
|
|
|
|
gl.BlitFramebuffer(
|
|
int32(sx0), int32(sy0), int32(sx1), int32(sy1),
|
|
int32(dx0), int32(dy0), int32(dx1), int32(dy1),
|
|
gl.COLOR_BUFFER_BIT, uint32(filter),
|
|
)
|
|
|
|
f.rf.restore()
|
|
f.df.restore()
|
|
}
|
|
|
|
// Texture returns the Frame's underlying Texture that the Frame draws on.
|
|
func (f *Frame) Texture() *Texture {
|
|
return f.tex
|
|
}
|