Skip to content

Commit d1510db

Browse files
committed
fix: Report the frame-count and dispose method for animated gif inputs.
1 parent 2a8aa7e commit d1510db

File tree

3 files changed

+73
-24
lines changed

3 files changed

+73
-24
lines changed

giflib.cpp

+36-10
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ int giflib_decoder_get_prev_frame_delay(const giflib_decoder d)
123123
return d->prev_frame_delay_time;
124124
}
125125

126+
int giflib_decoder_get_prev_frame_disposal(const giflib_decoder d)
127+
{
128+
switch (d->prev_frame_disposal) {
129+
case DISPOSE_DO_NOT:
130+
return GIF_DISPOSE_NONE;
131+
default:
132+
return GIF_DISPOSE_BACKGROUND;
133+
}
134+
}
135+
126136
void giflib_decoder_release(giflib_decoder d)
127137
{
128138
if (d->pixels) {
@@ -1113,15 +1123,15 @@ int giflib_encoder_get_output_length(giflib_encoder e)
11131123
return e->dst_offset;
11141124
}
11151125

1116-
int giflib_decoder_get_loop_count(const giflib_decoder d) {
1126+
struct GifAnimationInfo giflib_decoder_get_animation_info(const giflib_decoder d) {
11171127
// Default to 1 loop (play once) if no NETSCAPE2.0 extension is found
1118-
int loop_count = 1;
1128+
GifAnimationInfo info = {1, 0}; // Initialize with defaults
11191129

11201130
// Create a temporary decoder to read extension blocks
11211131
// We need a separate decoder because reading extension blocks modifies decoder state
11221132
giflib_decoder loopReader = new struct giflib_decoder_struct();
11231133
if (!loopReader) {
1124-
return loop_count; // Return default on allocation failure
1134+
return info; // Return default on allocation failure
11251135
}
11261136

11271137
memset(loopReader, 0, sizeof(struct giflib_decoder_struct));
@@ -1131,10 +1141,11 @@ int giflib_decoder_get_loop_count(const giflib_decoder d) {
11311141
GifFileType* gif = DGifOpen(loopReader, decode_func, &error);
11321142
if (error) {
11331143
delete loopReader;
1134-
return loop_count;
1144+
return info;
11351145
}
11361146

1137-
// Read all blocks until we find NETSCAPE2.0 or hit end
1147+
bool found_loop_count = false;
1148+
// Read all blocks until we hit end
11381149
GifRecordType recordType;
11391150
while (DGifGetRecordType(gif, &recordType) == GIF_OK) {
11401151
switch (recordType) {
@@ -1144,15 +1155,17 @@ int giflib_decoder_get_loop_count(const giflib_decoder d) {
11441155

11451156
if (DGifGetExtension(gif, &ExtFunction, &ExtData) == GIF_OK && ExtData != NULL) {
11461157
// Look for NETSCAPE2.0 extension
1147-
if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] >= 11 &&
1158+
if (!found_loop_count &&
1159+
ExtFunction == APPLICATION_EXT_FUNC_CODE &&
1160+
ExtData[0] >= 11 &&
11481161
memcmp(ExtData + 1, "NETSCAPE2.0", 11) == 0) {
11491162
// Get the next block with loop count
11501163
if (DGifGetExtensionNext(gif, &ExtData) == GIF_OK &&
11511164
ExtData != NULL &&
11521165
ExtData[0] >= 3 &&
11531166
ExtData[1] == 1) {
1154-
loop_count = ExtData[2] | (ExtData[3] << 8);
1155-
goto cleanup; // Found what we need
1167+
info.loop_count = ExtData[2] | (ExtData[3] << 8);
1168+
found_loop_count = true;
11561169
}
11571170
}
11581171

@@ -1167,10 +1180,23 @@ int giflib_decoder_get_loop_count(const giflib_decoder d) {
11671180
}
11681181

11691182
case IMAGE_DESC_RECORD_TYPE:
1170-
// Skip image data
1183+
// Count frame and skip image data
1184+
info.frame_count++;
11711185
if (DGifGetImageDesc(gif) != GIF_OK) {
11721186
goto cleanup;
11731187
}
1188+
// Skip the image data
1189+
{
1190+
GifByteType* CodeBlock;
1191+
if (DGifGetCode(gif, &error, &CodeBlock) == GIF_ERROR) {
1192+
goto cleanup;
1193+
}
1194+
while (CodeBlock != NULL) {
1195+
if (DGifGetCodeNext(gif, &CodeBlock) == GIF_ERROR) {
1196+
goto cleanup;
1197+
}
1198+
}
1199+
}
11741200
break;
11751201

11761202
case TERMINATE_RECORD_TYPE:
@@ -1184,5 +1210,5 @@ int giflib_decoder_get_loop_count(const giflib_decoder d) {
11841210
cleanup:
11851211
DGifCloseFile(gif, &error);
11861212
delete loopReader;
1187-
return loop_count;
1213+
return info;
11881214
}

giflib.go

+27-12
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
)
1313

1414
type gifDecoder struct {
15-
decoder C.giflib_decoder
16-
mat C.opencv_mat
17-
buf []byte
18-
frameIndex int
19-
loopCount int
20-
loopCountRead bool
15+
decoder C.giflib_decoder
16+
mat C.opencv_mat
17+
buf []byte
18+
frameIndex int
19+
loopCount int
20+
frameCount int
21+
animationInfoRead bool
2122
}
2223

2324
type gifEncoder struct {
@@ -69,7 +70,7 @@ func (d *gifDecoder) Header() (*ImageHeader, error) {
6970
height: int(C.giflib_decoder_get_height(d.decoder)),
7071
pixelType: PixelType(C.CV_8UC4),
7172
orientation: OrientationTopLeft,
72-
numFrames: int(C.giflib_decoder_get_num_frames(d.decoder)),
73+
numFrames: d.FrameCount(),
7374
contentLength: len(d.buf),
7475
}, nil
7576
}
@@ -115,14 +116,28 @@ func (d *gifDecoder) BackgroundColor() uint32 {
115116
return 0x00FFFFFF // use a non-opaque alpha value so we can see the background if needed
116117
}
117118

118-
func (d *gifDecoder) LoopCount() int {
119-
if !d.loopCountRead {
120-
d.loopCount = int(C.giflib_decoder_get_loop_count(d.decoder))
121-
d.loopCountRead = true
119+
// readAnimationInfo reads the GIF info from the decoder and caches it
120+
// this involves reading extension blocks and is relatively expensive
121+
// so we only do it once when we need it
122+
func (d *gifDecoder) readAnimationInfo() {
123+
if !d.animationInfoRead {
124+
info := C.giflib_decoder_get_animation_info(d.decoder)
125+
d.loopCount = int(info.loop_count)
126+
d.frameCount = int(info.frame_count)
127+
d.animationInfoRead = true
122128
}
129+
}
130+
131+
func (d *gifDecoder) LoopCount() int {
132+
d.readAnimationInfo()
123133
return d.loopCount
124134
}
125135

136+
func (d *gifDecoder) FrameCount() int {
137+
d.readAnimationInfo()
138+
return d.frameCount
139+
}
140+
126141
func (d *gifDecoder) DecodeTo(f *Framebuffer) error {
127142
h, err := d.Header()
128143
if err != nil {
@@ -157,7 +172,7 @@ func (d *gifDecoder) DecodeTo(f *Framebuffer) error {
157172
}
158173
f.duration = time.Duration(C.giflib_decoder_get_prev_frame_delay(d.decoder)) * 10 * time.Millisecond
159174
f.blend = NoBlend
160-
f.dispose = DisposeToBackgroundColor
175+
f.dispose = DisposeMethod(C.giflib_decoder_get_prev_frame_disposal(d.decoder))
161176
f.xOffset = 0
162177
f.yOffset = 0
163178
d.frameIndex++

giflib.hpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
extern "C" {
88
#endif
99

10+
struct GifAnimationInfo {
11+
int loop_count;
12+
int frame_count;
13+
};
14+
15+
#define GIF_DISPOSE_NONE 1
16+
#define GIF_DISPOSE_BACKGROUND 2
17+
1018
typedef struct giflib_decoder_struct* giflib_decoder;
1119
typedef struct giflib_encoder_struct* giflib_encoder;
1220

@@ -34,8 +42,8 @@ bool giflib_encoder_encode_frame(giflib_encoder e, const giflib_decoder d, const
3442
bool giflib_encoder_flush(giflib_encoder e, const giflib_decoder d);
3543
void giflib_encoder_release(giflib_encoder e);
3644
int giflib_encoder_get_output_length(giflib_encoder e);
37-
int giflib_decoder_get_loop_count(const giflib_decoder d);
38-
45+
struct GifAnimationInfo giflib_decoder_get_animation_info(const giflib_decoder d);
46+
int giflib_decoder_get_prev_frame_disposal(const giflib_decoder d);
3947
#ifdef __cplusplus
4048
}
4149
#endif

0 commit comments

Comments
 (0)