-
Notifications
You must be signed in to change notification settings - Fork 2
/
frame.go
108 lines (91 loc) · 2.59 KB
/
frame.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package glhf
import (
"runtime"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/gopxl/mainthread/v2"
)
// 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
}