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 // -----------------------------------------------------------------------------