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