1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // GIF decode.
11
12 #include "./gifdec.h"
13
14 #include <stdio.h>
15
16 #ifdef WEBP_HAVE_GIF
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "webp/encode.h"
22 #include "webp/mux_types.h"
23
24 #define GIF_TRANSPARENT_COLOR 0x00000000u
25 #define GIF_WHITE_COLOR 0xffffffffu
26 #define GIF_TRANSPARENT_MASK 0x01
27 #define GIF_DISPOSE_MASK 0x07
28 #define GIF_DISPOSE_SHIFT 2
29
30 // from utils/utils.h
31 #ifdef __cplusplus
32 extern "C" {
33 #endif
34 extern void WebPCopyPlane(const uint8_t* src, int src_stride,
35 uint8_t* dst, int dst_stride,
36 int width, int height);
37 extern void WebPCopyPixels(const WebPPicture* const src,
38 WebPPicture* const dst);
39 #ifdef __cplusplus
40 }
41 #endif
42
GIFGetBackgroundColor(const ColorMapObject * const color_map,int bgcolor_index,int transparent_index,uint32_t * const bgcolor)43 void GIFGetBackgroundColor(const ColorMapObject* const color_map,
44 int bgcolor_index, int transparent_index,
45 uint32_t* const bgcolor) {
46 if (transparent_index != GIF_INDEX_INVALID &&
47 bgcolor_index == transparent_index) {
48 *bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
49 } else if (color_map == NULL || color_map->Colors == NULL
50 || bgcolor_index >= color_map->ColorCount) {
51 *bgcolor = GIF_WHITE_COLOR;
52 fprintf(stderr,
53 "GIF decode warning: invalid background color index. Assuming "
54 "white background.\n");
55 } else {
56 const GifColorType color = color_map->Colors[bgcolor_index];
57 *bgcolor = (0xffu << 24)
58 | (color.Red << 16)
59 | (color.Green << 8)
60 | (color.Blue << 0);
61 }
62 }
63
GIFReadGraphicsExtension(const GifByteType * const buf,int * const duration,GIFDisposeMethod * const dispose,int * const transparent_index)64 int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
65 GIFDisposeMethod* const dispose,
66 int* const transparent_index) {
67 const int flags = buf[1];
68 const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
69 const int duration_raw = buf[2] | (buf[3] << 8); // In 10 ms units.
70 if (buf[0] != 4) return 0;
71 *duration = duration_raw * 10; // Duration is in 1 ms units.
72 switch (dispose_raw) {
73 case 3:
74 *dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
75 break;
76 case 2:
77 *dispose = GIF_DISPOSE_BACKGROUND;
78 break;
79 case 1:
80 case 0:
81 default:
82 *dispose = GIF_DISPOSE_NONE;
83 break;
84 }
85 *transparent_index =
86 (flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
87 return 1;
88 }
89
Remap(const GifFileType * const gif,const uint8_t * const src,int len,int transparent_index,uint32_t * dst)90 static int Remap(const GifFileType* const gif, const uint8_t* const src,
91 int len, int transparent_index, uint32_t* dst) {
92 int i;
93 const GifColorType* colors;
94 const ColorMapObject* const cmap =
95 gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
96 if (cmap == NULL) return 1;
97 if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
98 colors = cmap->Colors;
99
100 for (i = 0; i < len; ++i) {
101 if (src[i] == transparent_index) {
102 dst[i] = GIF_TRANSPARENT_COLOR;
103 } else if (src[i] < cmap->ColorCount) {
104 const GifColorType c = colors[src[i]];
105 dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
106 } else {
107 return 0;
108 }
109 }
110 return 1;
111 }
112
GIFReadFrame(GifFileType * const gif,int transparent_index,GIFFrameRect * const gif_rect,WebPPicture * const picture)113 int GIFReadFrame(GifFileType* const gif, int transparent_index,
114 GIFFrameRect* const gif_rect, WebPPicture* const picture) {
115 WebPPicture sub_image;
116 const GifImageDesc* const image_desc = &gif->Image;
117 uint32_t* dst = NULL;
118 uint8_t* tmp = NULL;
119 const GIFFrameRect rect = {
120 image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
121 };
122 const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
123 int ok = 0;
124 *gif_rect = rect;
125
126 if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
127 fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
128 return 0;
129 }
130
131 // Use a view for the sub-picture:
132 if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
133 rect.width, rect.height, &sub_image)) {
134 fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
135 rect.width, rect.height, rect.x_offset, rect.y_offset);
136 return 0;
137 }
138 dst = sub_image.argb;
139
140 tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
141 if (tmp == NULL) goto End;
142
143 if (image_desc->Interlace) { // Interlaced image.
144 // We need 4 passes, with the following offsets and jumps.
145 const int interlace_offsets[] = { 0, 4, 2, 1 };
146 const int interlace_jumps[] = { 8, 8, 4, 2 };
147 int pass;
148 for (pass = 0; pass < 4; ++pass) {
149 const size_t stride = (size_t)sub_image.argb_stride;
150 int y = interlace_offsets[pass];
151 uint32_t* row = dst + y * stride;
152 const size_t jump = interlace_jumps[pass] * stride;
153 for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
154 if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
155 if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
156 }
157 }
158 } else { // Non-interlaced image.
159 int y;
160 uint32_t* ptr = dst;
161 for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
162 if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
163 if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
164 }
165 }
166 ok = 1;
167
168 End:
169 if (!ok) picture->error_code = sub_image.error_code;
170 WebPPictureFree(&sub_image);
171 free(tmp);
172 return ok;
173 }
174
GIFReadLoopCount(GifFileType * const gif,GifByteType ** const buf,int * const loop_count)175 int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
176 int* const loop_count) {
177 assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
178 !memcmp(*buf + 1, "ANIMEXTS1.0", 11));
179 if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
180 return 0;
181 }
182 if (*buf == NULL) {
183 return 0; // Loop count sub-block missing.
184 }
185 if ((*buf)[0] < 3 || (*buf)[1] != 1) {
186 return 0; // wrong size/marker
187 }
188 *loop_count = (*buf)[2] | ((*buf)[3] << 8);
189 return 1;
190 }
191
GIFReadMetadata(GifFileType * const gif,GifByteType ** const buf,WebPData * const metadata)192 int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
193 WebPData* const metadata) {
194 const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
195 const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
196 assert(is_xmp || is_icc);
197 (void)is_icc; // silence unused warning.
198 // Construct metadata from sub-blocks.
199 // Usual case (including ICC profile): In each sub-block, the
200 // first byte specifies its size in bytes (0 to 255) and the
201 // rest of the bytes contain the data.
202 // Special case for XMP data: In each sub-block, the first byte
203 // is also part of the XMP payload. XMP in GIF also has a 257
204 // byte padding data. See the XMP specification for details.
205 while (1) {
206 WebPData subblock;
207 const uint8_t* tmp;
208 if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
209 return 0;
210 }
211 if (*buf == NULL) break; // Finished.
212 subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
213 assert(subblock.size > 0);
214 subblock.bytes = is_xmp ? *buf : *buf + 1;
215 // Note: We store returned value in 'tmp' first, to avoid
216 // leaking old memory in metadata->bytes on error.
217 tmp = (uint8_t*)realloc((void*)metadata->bytes,
218 metadata->size + subblock.size);
219 if (tmp == NULL) {
220 return 0;
221 }
222 memcpy((void*)(tmp + metadata->size),
223 subblock.bytes, subblock.size);
224 metadata->bytes = tmp;
225 metadata->size += subblock.size;
226 }
227 if (is_xmp) {
228 // XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
229 const size_t xmp_pading_size = 257;
230 if (metadata->size > xmp_pading_size) {
231 metadata->size -= xmp_pading_size;
232 }
233 }
234 return 1;
235 }
236
ClearRectangle(WebPPicture * const picture,int left,int top,int width,int height)237 static void ClearRectangle(WebPPicture* const picture,
238 int left, int top, int width, int height) {
239 int i, j;
240 const size_t stride = picture->argb_stride;
241 uint32_t* dst = picture->argb + top * stride + left;
242 for (j = 0; j < height; ++j, dst += stride) {
243 for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
244 }
245 }
246
GIFClearPic(WebPPicture * const pic,const GIFFrameRect * const rect)247 void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
248 if (rect != NULL) {
249 ClearRectangle(pic, rect->x_offset, rect->y_offset,
250 rect->width, rect->height);
251 } else {
252 ClearRectangle(pic, 0, 0, pic->width, pic->height);
253 }
254 }
255
GIFCopyPixels(const WebPPicture * const src,WebPPicture * const dst)256 void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
257 WebPCopyPixels(src, dst);
258 }
259
GIFDisposeFrame(GIFDisposeMethod dispose,const GIFFrameRect * const rect,const WebPPicture * const prev_canvas,WebPPicture * const curr_canvas)260 void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
261 const WebPPicture* const prev_canvas,
262 WebPPicture* const curr_canvas) {
263 assert(rect != NULL);
264 if (dispose == GIF_DISPOSE_BACKGROUND) {
265 GIFClearPic(curr_canvas, rect);
266 } else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
267 const size_t src_stride = prev_canvas->argb_stride;
268 const uint32_t* const src = prev_canvas->argb + rect->x_offset
269 + rect->y_offset * src_stride;
270 const size_t dst_stride = curr_canvas->argb_stride;
271 uint32_t* const dst = curr_canvas->argb + rect->x_offset
272 + rect->y_offset * dst_stride;
273 assert(prev_canvas != NULL);
274 WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
275 (uint8_t*)dst, (int)(4 * dst_stride),
276 4 * rect->width, rect->height);
277 }
278 }
279
GIFBlendFrames(const WebPPicture * const src,const GIFFrameRect * const rect,WebPPicture * const dst)280 void GIFBlendFrames(const WebPPicture* const src,
281 const GIFFrameRect* const rect, WebPPicture* const dst) {
282 int i, j;
283 const size_t src_stride = src->argb_stride;
284 const size_t dst_stride = dst->argb_stride;
285 assert(src->width == dst->width && src->height == dst->height);
286 for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
287 for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
288 const uint32_t src_pixel = src->argb[j * src_stride + i];
289 const int src_alpha = src_pixel >> 24;
290 if (src_alpha != 0) {
291 dst->argb[j * dst_stride + i] = src_pixel;
292 }
293 }
294 }
295 }
296
GIFDisplayError(const GifFileType * const gif,int gif_error)297 void GIFDisplayError(const GifFileType* const gif, int gif_error) {
298 // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
299 #if LOCAL_GIF_PREREQ(4,2)
300 #if LOCAL_GIF_PREREQ(5,0)
301 // Static string actually, hence the const char* cast.
302 const char* error_str = (const char*)GifErrorString(
303 (gif == NULL) ? gif_error : gif->Error);
304 #else
305 const char* error_str = (const char*)GifErrorString();
306 (void)gif;
307 #endif
308 if (error_str == NULL) error_str = "Unknown error";
309 fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
310 #else
311 (void)gif;
312 fprintf(stderr, "GIFLib Error %d: ", gif_error);
313 PrintGifError();
314 fprintf(stderr, "\n");
315 #endif
316 }
317
318 #else // !WEBP_HAVE_GIF
319
ErrorGIFNotAvailable()320 static void ErrorGIFNotAvailable() {
321 fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
322 "package before building.\n");
323 }
324
GIFGetBackgroundColor(const struct ColorMapObject * const color_map,int bgcolor_index,int transparent_index,uint32_t * const bgcolor)325 void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
326 int bgcolor_index, int transparent_index,
327 uint32_t* const bgcolor) {
328 (void)color_map;
329 (void)bgcolor_index;
330 (void)transparent_index;
331 (void)bgcolor;
332 ErrorGIFNotAvailable();
333 }
334
GIFReadGraphicsExtension(const GifByteType * const data,int * const duration,GIFDisposeMethod * const dispose,int * const transparent_index)335 int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
336 GIFDisposeMethod* const dispose,
337 int* const transparent_index) {
338 (void)data;
339 (void)duration;
340 (void)dispose;
341 (void)transparent_index;
342 ErrorGIFNotAvailable();
343 return 0;
344 }
345
GIFReadFrame(struct GifFileType * const gif,int transparent_index,GIFFrameRect * const gif_rect,struct WebPPicture * const picture)346 int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
347 GIFFrameRect* const gif_rect,
348 struct WebPPicture* const picture) {
349 (void)gif;
350 (void)transparent_index;
351 (void)gif_rect;
352 (void)picture;
353 ErrorGIFNotAvailable();
354 return 0;
355 }
356
GIFReadLoopCount(struct GifFileType * const gif,GifByteType ** const buf,int * const loop_count)357 int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
358 int* const loop_count) {
359 (void)gif;
360 (void)buf;
361 (void)loop_count;
362 ErrorGIFNotAvailable();
363 return 0;
364 }
365
GIFReadMetadata(struct GifFileType * const gif,GifByteType ** const buf,struct WebPData * const metadata)366 int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
367 struct WebPData* const metadata) {
368 (void)gif;
369 (void)buf;
370 (void)metadata;
371 ErrorGIFNotAvailable();
372 return 0;
373 }
374
GIFDisposeFrame(GIFDisposeMethod dispose,const GIFFrameRect * const rect,const struct WebPPicture * const prev_canvas,struct WebPPicture * const curr_canvas)375 void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
376 const struct WebPPicture* const prev_canvas,
377 struct WebPPicture* const curr_canvas) {
378 (void)dispose;
379 (void)rect;
380 (void)prev_canvas;
381 (void)curr_canvas;
382 ErrorGIFNotAvailable();
383 }
384
GIFBlendFrames(const struct WebPPicture * const src,const GIFFrameRect * const rect,struct WebPPicture * const dst)385 void GIFBlendFrames(const struct WebPPicture* const src,
386 const GIFFrameRect* const rect,
387 struct WebPPicture* const dst) {
388 (void)src;
389 (void)rect;
390 (void)dst;
391 ErrorGIFNotAvailable();
392 }
393
GIFDisplayError(const struct GifFileType * const gif,int gif_error)394 void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
395 (void)gif;
396 (void)gif_error;
397 ErrorGIFNotAvailable();
398 }
399
GIFClearPic(struct WebPPicture * const pic,const GIFFrameRect * const rect)400 void GIFClearPic(struct WebPPicture* const pic,
401 const GIFFrameRect* const rect) {
402 (void)pic;
403 (void)rect;
404 ErrorGIFNotAvailable();
405 }
406
GIFCopyPixels(const struct WebPPicture * const src,struct WebPPicture * const dst)407 void GIFCopyPixels(const struct WebPPicture* const src,
408 struct WebPPicture* const dst) {
409 (void)src;
410 (void)dst;
411 ErrorGIFNotAvailable();
412 }
413
414 #endif // WEBP_HAVE_GIF
415
416 // -----------------------------------------------------------------------------
417