go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

gifdec.c (13647B)


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