-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathgifAnimation.go
170 lines (125 loc) · 4.47 KB
/
gifAnimation.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package main
import (
"image/gif"
"github.com/veandco/go-sdl2/sdl"
)
type GifAnimation struct {
Data *gif.GIF
Frames []*sdl.Surface
Delays []float32 // 100ths of a second?
frameImg *sdl.Surface
Width float32
Height float32
progressChannel chan float32
progress float32
}
func NewGifAnimation(data *gif.GIF) *GifAnimation {
surf, _ := sdl.CreateRGBSurfaceWithFormat(0, int32(data.Image[0].Rect.Dx()), int32(data.Image[0].Rect.Dy()), 32, sdl.PIXELFORMAT_RGBA8888)
anim := &GifAnimation{
Data: data,
frameImg: surf,
Width: float32(data.Image[0].Rect.Dx()),
Height: float32(data.Image[0].Rect.Dy()),
progressChannel: make(chan float32, 1),
}
go anim.Load() // Load the frames in the background
return anim
}
// Load loads the frames of the GIF animation.
func (gifAnim *GifAnimation) Load() {
for index, img := range gifAnim.Data.Image {
// After decoding, we have to manually create a new image and plot each frame of the GIF because transparent GIFs
// can only have frames that account for changed pixels (i.e. if you have a 320x240 GIF, but on frame
// 17 only one pixel changes, the image generated for frame 17 will be 1x1 for Bounds.Size()).
prev := 0
disposalMode := byte(gif.DisposalNone)
if index > 0 {
disposalMode = gifAnim.Data.Disposal[index-1]
}
empty := sdl.RGBA8888{0, 0, 0, 0}
for y := 0; y < gifAnim.frameImg.Bounds().Size().Y; y++ {
for x := 0; x < gifAnim.frameImg.Bounds().Size().X; x++ {
// We clear each pixel of each frame, but only the pixels within the rectangle specified by the frame is plotted below, as
// some frames of GIFs can have a "changed rectangle", indicating which pixels in which rectangle need to actually change.
if disposalMode == gif.DisposalBackground {
gifAnim.frameImg.Set(x, y, empty)
} else if disposalMode == gif.DisposalPrevious {
r, g, b, a := gifAnim.Data.Image[prev].At(x, y).RGBA()
gifAnim.frameImg.Set(x, y, sdl.RGBA8888{byte(r), byte(g), byte(b), byte(a)})
}
if disposalMode != gif.DisposalPrevious {
prev = index
}
if x >= img.Bounds().Min.X && x < img.Bounds().Max.X && y >= img.Bounds().Min.Y && y < img.Bounds().Max.Y {
color := img.At(x, y)
if _, _, _, alpha := color.RGBA(); alpha > 0 {
r, g, b, a := color.RGBA()
gifAnim.frameImg.Set(x, y, sdl.RGBA8888{byte(r), byte(g), byte(b), byte(a)})
}
}
}
}
newSurf, _ := gifAnim.frameImg.Duplicate()
gifAnim.Frames = append(gifAnim.Frames, newSurf)
delay := float32(gifAnim.Data.Delay[index]) / 100
if delay <= 0 {
delay = 0.1
}
gifAnim.Delays = append(gifAnim.Delays, delay)
// If there's something in the progress channel, it's an old value indicating the progress of the
// loading process, so we take it out.
if len(gifAnim.progressChannel) > 0 {
<-gifAnim.progressChannel
}
gifAnim.progressChannel <- float32(len(gifAnim.Frames)) / float32(len(gifAnim.Data.Image))
}
}
func (gifAnim *GifAnimation) Destroy() {
for _, frame := range gifAnim.Frames {
frame.Free()
}
}
func (gifAnim *GifAnimation) IsReady() bool {
return gifAnim.LoadingProgress() >= 1
}
// LoadingProgress returns the progress at loading the GIF into memory as a fraction spanning 0 to 1.
func (gifAnim *GifAnimation) LoadingProgress() float32 {
for len(gifAnim.progressChannel) > 0 {
gifAnim.progress = <-gifAnim.progressChannel
}
return gifAnim.progress
}
type GifPlayer struct {
Animation *GifAnimation
CurrentFrame int
Timer float32
// FrameTex *sdl.Texture
Frames []*sdl.Texture
}
func NewGifPlayer(gifAnim *GifAnimation) *GifPlayer {
return &GifPlayer{
Animation: gifAnim,
}
}
func (gifPlayer *GifPlayer) Update(dt float32) {
gifPlayer.Timer += dt
for gifPlayer.Timer >= gifPlayer.Animation.Delays[gifPlayer.CurrentFrame] {
gifPlayer.Timer -= gifPlayer.Animation.Delays[gifPlayer.CurrentFrame]
gifPlayer.CurrentFrame++
if gifPlayer.CurrentFrame >= len(gifPlayer.Animation.Frames) {
gifPlayer.CurrentFrame = 0
}
}
}
func (gifPlayer *GifPlayer) Destroy() {
for _, frame := range gifPlayer.Frames {
frame.Destroy()
}
}
func (gifPlayer *GifPlayer) Texture() *sdl.Texture {
for len(gifPlayer.Frames) <= gifPlayer.CurrentFrame {
frame, _ := globals.Renderer.CreateTextureFromSurface(gifPlayer.Animation.Frames[gifPlayer.CurrentFrame])
gifPlayer.Frames = append(gifPlayer.Frames, frame)
}
return gifPlayer.Frames[gifPlayer.CurrentFrame]
}