1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/images/SkImageDecoder_libgif.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,538 @@ 1.4 +/* 1.5 + * Copyright 2006 The Android Open Source Project 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkColor.h" 1.12 +#include "SkColorPriv.h" 1.13 +#include "SkColorTable.h" 1.14 +#include "SkImageDecoder.h" 1.15 +#include "SkRTConf.h" 1.16 +#include "SkScaledBitmapSampler.h" 1.17 +#include "SkStream.h" 1.18 +#include "SkTemplates.h" 1.19 +#include "SkUtils.h" 1.20 + 1.21 +#include "gif_lib.h" 1.22 + 1.23 +class SkGIFImageDecoder : public SkImageDecoder { 1.24 +public: 1.25 + virtual Format getFormat() const SK_OVERRIDE { 1.26 + return kGIF_Format; 1.27 + } 1.28 + 1.29 +protected: 1.30 + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; 1.31 + 1.32 +private: 1.33 + typedef SkImageDecoder INHERITED; 1.34 +}; 1.35 + 1.36 +static const uint8_t gStartingIterlaceYValue[] = { 1.37 + 0, 4, 2, 1 1.38 +}; 1.39 +static const uint8_t gDeltaIterlaceYValue[] = { 1.40 + 8, 8, 4, 2 1.41 +}; 1.42 + 1.43 +SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings, 1.44 + "images.gif.suppressDecoderWarnings", true, 1.45 + "Suppress GIF warnings and errors when calling image decode " 1.46 + "functions."); 1.47 + 1.48 + 1.49 +/* Implement the GIF interlace algorithm in an iterator. 1.50 + 1) grab every 8th line beginning at 0 1.51 + 2) grab every 8th line beginning at 4 1.52 + 3) grab every 4th line beginning at 2 1.53 + 4) grab every 2nd line beginning at 1 1.54 +*/ 1.55 +class GifInterlaceIter { 1.56 +public: 1.57 + GifInterlaceIter(int height) : fHeight(height) { 1.58 + fStartYPtr = gStartingIterlaceYValue; 1.59 + fDeltaYPtr = gDeltaIterlaceYValue; 1.60 + 1.61 + fCurrY = *fStartYPtr++; 1.62 + fDeltaY = *fDeltaYPtr++; 1.63 + } 1.64 + 1.65 + int currY() const { 1.66 + SkASSERT(fStartYPtr); 1.67 + SkASSERT(fDeltaYPtr); 1.68 + return fCurrY; 1.69 + } 1.70 + 1.71 + void next() { 1.72 + SkASSERT(fStartYPtr); 1.73 + SkASSERT(fDeltaYPtr); 1.74 + 1.75 + int y = fCurrY + fDeltaY; 1.76 + // We went from an if statement to a while loop so that we iterate 1.77 + // through fStartYPtr until a valid row is found. This is so that images 1.78 + // that are smaller than 5x5 will not trash memory. 1.79 + while (y >= fHeight) { 1.80 + if (gStartingIterlaceYValue + 1.81 + SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { 1.82 + // we done 1.83 + SkDEBUGCODE(fStartYPtr = NULL;) 1.84 + SkDEBUGCODE(fDeltaYPtr = NULL;) 1.85 + y = 0; 1.86 + } else { 1.87 + y = *fStartYPtr++; 1.88 + fDeltaY = *fDeltaYPtr++; 1.89 + } 1.90 + } 1.91 + fCurrY = y; 1.92 + } 1.93 + 1.94 +private: 1.95 + const int fHeight; 1.96 + int fCurrY; 1.97 + int fDeltaY; 1.98 + const uint8_t* fStartYPtr; 1.99 + const uint8_t* fDeltaYPtr; 1.100 +}; 1.101 + 1.102 +/////////////////////////////////////////////////////////////////////////////// 1.103 + 1.104 +static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, 1.105 + int size) { 1.106 + SkStream* stream = (SkStream*) fileType->UserData; 1.107 + return (int) stream->read(out, size); 1.108 +} 1.109 + 1.110 +void CheckFreeExtension(SavedImage* Image) { 1.111 + if (Image->ExtensionBlocks) { 1.112 +#if GIFLIB_MAJOR < 5 1.113 + FreeExtension(Image); 1.114 +#else 1.115 + GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks); 1.116 +#endif 1.117 + } 1.118 +} 1.119 + 1.120 +// return NULL on failure 1.121 +static const ColorMapObject* find_colormap(const GifFileType* gif) { 1.122 + const ColorMapObject* cmap = gif->Image.ColorMap; 1.123 + if (NULL == cmap) { 1.124 + cmap = gif->SColorMap; 1.125 + } 1.126 + 1.127 + if (NULL == cmap) { 1.128 + // no colormap found 1.129 + return NULL; 1.130 + } 1.131 + // some sanity checks 1.132 + if (cmap && ((unsigned)cmap->ColorCount > 256 || 1.133 + cmap->ColorCount != (1 << cmap->BitsPerPixel))) { 1.134 + cmap = NULL; 1.135 + } 1.136 + return cmap; 1.137 +} 1.138 + 1.139 +// return -1 if not found (i.e. we're completely opaque) 1.140 +static int find_transpIndex(const SavedImage& image, int colorCount) { 1.141 + int transpIndex = -1; 1.142 + for (int i = 0; i < image.ExtensionBlockCount; ++i) { 1.143 + const ExtensionBlock* eb = image.ExtensionBlocks + i; 1.144 + if (eb->Function == 0xF9 && eb->ByteCount == 4) { 1.145 + if (eb->Bytes[0] & 1) { 1.146 + transpIndex = (unsigned char)eb->Bytes[3]; 1.147 + // check for valid transpIndex 1.148 + if (transpIndex >= colorCount) { 1.149 + transpIndex = -1; 1.150 + } 1.151 + break; 1.152 + } 1.153 + } 1.154 + } 1.155 + return transpIndex; 1.156 +} 1.157 + 1.158 +static bool error_return(const SkBitmap& bm, const char msg[]) { 1.159 + if (!c_suppressGIFImageDecoderWarnings) { 1.160 + SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n", 1.161 + msg, bm.width(), bm.height(), bm.getPixels(), 1.162 + bm.getColorTable()); 1.163 + } 1.164 + return false; 1.165 +} 1.166 +static void gif_warning(const SkBitmap& bm, const char msg[]) { 1.167 + if (!c_suppressGIFImageDecoderWarnings) { 1.168 + SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n", 1.169 + msg, bm.width(), bm.height(), bm.getPixels(), 1.170 + bm.getColorTable()); 1.171 + } 1.172 +} 1.173 + 1.174 +/** 1.175 + * Skip rows in the source gif image. 1.176 + * @param gif Source image. 1.177 + * @param dst Scratch output needed by gif library call. Must be >= width bytes. 1.178 + * @param width Bytes per row in the source image. 1.179 + * @param rowsToSkip Number of rows to skip. 1.180 + * @return True on success, false on GIF_ERROR. 1.181 + */ 1.182 +static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) { 1.183 + for (int i = 0; i < rowsToSkip; i++) { 1.184 + if (DGifGetLine(gif, dst, width) == GIF_ERROR) { 1.185 + return false; 1.186 + } 1.187 + } 1.188 + return true; 1.189 +} 1.190 + 1.191 +/** 1.192 + * GIFs with fewer then 256 color entries will sometimes index out of 1.193 + * bounds of the color table (this is malformed, but libgif does not 1.194 + * check sicne it is rare). This function checks for this error and 1.195 + * fixes it. This makes the output image consistantly deterministic. 1.196 + */ 1.197 +static void sanitize_indexed_bitmap(SkBitmap* bm) { 1.198 + if ((SkBitmap::kIndex8_Config == bm->config()) && !(bm->empty())) { 1.199 + SkAutoLockPixels alp(*bm); 1.200 + if (NULL != bm->getPixels()) { 1.201 + SkColorTable* ct = bm->getColorTable(); // Index8 must have it. 1.202 + SkASSERT(ct != NULL); 1.203 + uint32_t count = ct->count(); 1.204 + SkASSERT(count > 0); 1.205 + SkASSERT(count <= 0x100); 1.206 + if (count != 0x100) { // Full colortables can't go wrong. 1.207 + // Count is a power of 2; asserted elsewhere. 1.208 + uint8_t byteMask = (~(count - 1)); 1.209 + bool warning = false; 1.210 + uint8_t* addr = static_cast<uint8_t*>(bm->getPixels()); 1.211 + int height = bm->height(); 1.212 + int width = bm->width(); 1.213 + size_t rowBytes = bm->rowBytes(); 1.214 + while (--height >= 0) { 1.215 + uint8_t* ptr = addr; 1.216 + int x = width; 1.217 + while (--x >= 0) { 1.218 + if (0 != ((*ptr) & byteMask)) { 1.219 + warning = true; 1.220 + *ptr = 0; 1.221 + } 1.222 + ++ptr; 1.223 + } 1.224 + addr += rowBytes; 1.225 + } 1.226 + if (warning) { 1.227 + gif_warning(*bm, "Index out of bounds."); 1.228 + } 1.229 + } 1.230 + } 1.231 + } 1.232 +} 1.233 + 1.234 +bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { 1.235 +#if GIFLIB_MAJOR < 5 1.236 + GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); 1.237 +#else 1.238 + GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); 1.239 +#endif 1.240 + if (NULL == gif) { 1.241 + return error_return(*bm, "DGifOpen"); 1.242 + } 1.243 + 1.244 + SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); 1.245 + 1.246 + SavedImage temp_save; 1.247 + temp_save.ExtensionBlocks=NULL; 1.248 + temp_save.ExtensionBlockCount=0; 1.249 + SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); 1.250 + 1.251 + int width, height; 1.252 + GifRecordType recType; 1.253 + GifByteType *extData; 1.254 +#if GIFLIB_MAJOR >= 5 1.255 + int extFunction; 1.256 +#endif 1.257 + int transpIndex = -1; // -1 means we don't have it (yet) 1.258 + int fillIndex = gif->SBackGroundColor; 1.259 + 1.260 + do { 1.261 + if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { 1.262 + return error_return(*bm, "DGifGetRecordType"); 1.263 + } 1.264 + 1.265 + switch (recType) { 1.266 + case IMAGE_DESC_RECORD_TYPE: { 1.267 + if (DGifGetImageDesc(gif) == GIF_ERROR) { 1.268 + return error_return(*bm, "IMAGE_DESC_RECORD_TYPE"); 1.269 + } 1.270 + 1.271 + if (gif->ImageCount < 1) { // sanity check 1.272 + return error_return(*bm, "ImageCount < 1"); 1.273 + } 1.274 + 1.275 + width = gif->SWidth; 1.276 + height = gif->SHeight; 1.277 + 1.278 + SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; 1.279 + const GifImageDesc& desc = image->ImageDesc; 1.280 + 1.281 + int imageLeft = desc.Left; 1.282 + int imageTop = desc.Top; 1.283 + const int innerWidth = desc.Width; 1.284 + const int innerHeight = desc.Height; 1.285 + if (innerWidth <= 0 || innerHeight <= 0) { 1.286 + return error_return(*bm, "invalid dimensions"); 1.287 + } 1.288 + 1.289 + // check for valid descriptor 1.290 + if (innerWidth > width) { 1.291 + gif_warning(*bm, "image too wide, expanding output to size"); 1.292 + width = innerWidth; 1.293 + imageLeft = 0; 1.294 + } else if (imageLeft + innerWidth > width) { 1.295 + gif_warning(*bm, "shifting image left to fit"); 1.296 + imageLeft = width - innerWidth; 1.297 + } else if (imageLeft < 0) { 1.298 + gif_warning(*bm, "shifting image right to fit"); 1.299 + imageLeft = 0; 1.300 + } 1.301 + 1.302 + 1.303 + if (innerHeight > height) { 1.304 + gif_warning(*bm, "image too tall, expanding output to size"); 1.305 + height = innerHeight; 1.306 + imageTop = 0; 1.307 + } else if (imageTop + innerHeight > height) { 1.308 + gif_warning(*bm, "shifting image up to fit"); 1.309 + imageTop = height - innerHeight; 1.310 + } else if (imageTop < 0) { 1.311 + gif_warning(*bm, "shifting image down to fit"); 1.312 + imageTop = 0; 1.313 + } 1.314 + 1.315 + // FIXME: We could give the caller a choice of images or configs. 1.316 + if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) { 1.317 + return error_return(*bm, "chooseFromOneChoice"); 1.318 + } 1.319 + 1.320 + SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); 1.321 + 1.322 + bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), 1.323 + sampler.scaledHeight()); 1.324 + 1.325 + if (SkImageDecoder::kDecodeBounds_Mode == mode) { 1.326 + return true; 1.327 + } 1.328 + 1.329 + 1.330 + // now we decode the colortable 1.331 + int colorCount = 0; 1.332 + { 1.333 + // Declare colorPtr here for scope. 1.334 + SkPMColor colorPtr[256]; // storage for worst-case 1.335 + const ColorMapObject* cmap = find_colormap(gif); 1.336 + SkAlphaType alphaType = kOpaque_SkAlphaType; 1.337 + if (cmap != NULL) { 1.338 + SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel))); 1.339 + colorCount = cmap->ColorCount; 1.340 + if (colorCount > 256) { 1.341 + colorCount = 256; // our kIndex8 can't support more 1.342 + } 1.343 + for (int index = 0; index < colorCount; index++) { 1.344 + colorPtr[index] = SkPackARGB32(0xFF, 1.345 + cmap->Colors[index].Red, 1.346 + cmap->Colors[index].Green, 1.347 + cmap->Colors[index].Blue); 1.348 + } 1.349 + } else { 1.350 + // find_colormap() returned NULL. Some (rare, broken) 1.351 + // GIFs don't have a color table, so we force one. 1.352 + gif_warning(*bm, "missing colormap"); 1.353 + colorCount = 256; 1.354 + sk_memset32(colorPtr, SK_ColorWHITE, colorCount); 1.355 + } 1.356 + transpIndex = find_transpIndex(temp_save, colorCount); 1.357 + if (transpIndex >= 0) { 1.358 + colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor 1.359 + alphaType = kPremul_SkAlphaType; 1.360 + fillIndex = transpIndex; 1.361 + } else if (fillIndex >= colorCount) { 1.362 + // gif->SBackGroundColor should be less than colorCount. 1.363 + fillIndex = 0; // If not, fix it. 1.364 + } 1.365 + 1.366 + SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, 1.367 + (colorPtr, colorCount, 1.368 + alphaType))); 1.369 + if (!this->allocPixelRef(bm, ctable)) { 1.370 + return error_return(*bm, "allocPixelRef"); 1.371 + } 1.372 + } 1.373 + 1.374 + // abort if either inner dimension is <= 0 1.375 + if (innerWidth <= 0 || innerHeight <= 0) { 1.376 + return error_return(*bm, "non-pos inner width/height"); 1.377 + } 1.378 + 1.379 + SkAutoLockPixels alp(*bm); 1.380 + 1.381 + SkAutoMalloc storage(innerWidth); 1.382 + uint8_t* scanline = (uint8_t*) storage.get(); 1.383 + 1.384 + // GIF has an option to store the scanlines of an image, plus a larger background, 1.385 + // filled by a fill color. In this case, we will use a subset of the larger bitmap 1.386 + // for sampling. 1.387 + SkBitmap subset; 1.388 + SkBitmap* workingBitmap; 1.389 + // are we only a subset of the total bounds? 1.390 + if ((imageTop | imageLeft) > 0 || 1.391 + innerWidth < width || innerHeight < height) { 1.392 + // Fill the background. 1.393 + memset(bm->getPixels(), fillIndex, bm->getSize()); 1.394 + 1.395 + // Create a subset of the bitmap. 1.396 + SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(), 1.397 + imageTop / sampler.srcDY(), 1.398 + innerWidth / sampler.srcDX(), 1.399 + innerHeight / sampler.srcDY())); 1.400 + if (!bm->extractSubset(&subset, subsetRect)) { 1.401 + return error_return(*bm, "Extract failed."); 1.402 + } 1.403 + // Update the sampler. We'll now be only sampling into the subset. 1.404 + sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize()); 1.405 + workingBitmap = ⊂ 1.406 + } else { 1.407 + workingBitmap = bm; 1.408 + } 1.409 + 1.410 + // bm is already locked, but if we had to take a subset, it must be locked also, 1.411 + // so that getPixels() will point to its pixels. 1.412 + SkAutoLockPixels alpWorking(*workingBitmap); 1.413 + 1.414 + if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) { 1.415 + return error_return(*bm, "Sampler failed to begin."); 1.416 + } 1.417 + 1.418 + // now decode each scanline 1.419 + if (gif->Image.Interlace) { 1.420 + // Iterate over the height of the source data. The sampler will 1.421 + // take care of skipping unneeded rows. 1.422 + GifInterlaceIter iter(innerHeight); 1.423 + for (int y = 0; y < innerHeight; y++) { 1.424 + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 1.425 + gif_warning(*bm, "interlace DGifGetLine"); 1.426 + memset(scanline, fillIndex, innerWidth); 1.427 + for (; y < innerHeight; y++) { 1.428 + sampler.sampleInterlaced(scanline, iter.currY()); 1.429 + iter.next(); 1.430 + } 1.431 + return true; 1.432 + } 1.433 + sampler.sampleInterlaced(scanline, iter.currY()); 1.434 + iter.next(); 1.435 + } 1.436 + } else { 1.437 + // easy, non-interlace case 1.438 + const int outHeight = workingBitmap->height(); 1.439 + skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); 1.440 + for (int y = 0; y < outHeight; y++) { 1.441 + if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 1.442 + gif_warning(*bm, "DGifGetLine"); 1.443 + memset(scanline, fillIndex, innerWidth); 1.444 + for (; y < outHeight; y++) { 1.445 + sampler.next(scanline); 1.446 + } 1.447 + return true; 1.448 + } 1.449 + // scanline now contains the raw data. Sample it. 1.450 + sampler.next(scanline); 1.451 + if (y < outHeight - 1) { 1.452 + skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1); 1.453 + } 1.454 + } 1.455 + // skip the rest of the rows (if any) 1.456 + int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1; 1.457 + SkASSERT(read <= innerHeight); 1.458 + skip_src_rows(gif, scanline, innerWidth, innerHeight - read); 1.459 + } 1.460 + sanitize_indexed_bitmap(bm); 1.461 + return true; 1.462 + } break; 1.463 + 1.464 + case EXTENSION_RECORD_TYPE: 1.465 +#if GIFLIB_MAJOR < 5 1.466 + if (DGifGetExtension(gif, &temp_save.Function, 1.467 + &extData) == GIF_ERROR) { 1.468 +#else 1.469 + if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { 1.470 +#endif 1.471 + return error_return(*bm, "DGifGetExtension"); 1.472 + } 1.473 + 1.474 + while (extData != NULL) { 1.475 + /* Create an extension block with our data */ 1.476 +#if GIFLIB_MAJOR < 5 1.477 + if (AddExtensionBlock(&temp_save, extData[0], 1.478 + &extData[1]) == GIF_ERROR) { 1.479 +#else 1.480 + if (GifAddExtensionBlock(&gif->ExtensionBlockCount, 1.481 + &gif->ExtensionBlocks, 1.482 + extFunction, 1.483 + extData[0], 1.484 + &extData[1]) == GIF_ERROR) { 1.485 +#endif 1.486 + return error_return(*bm, "AddExtensionBlock"); 1.487 + } 1.488 + if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { 1.489 + return error_return(*bm, "DGifGetExtensionNext"); 1.490 + } 1.491 +#if GIFLIB_MAJOR < 5 1.492 + temp_save.Function = 0; 1.493 +#endif 1.494 + } 1.495 + break; 1.496 + 1.497 + case TERMINATE_RECORD_TYPE: 1.498 + break; 1.499 + 1.500 + default: /* Should be trapped by DGifGetRecordType */ 1.501 + break; 1.502 + } 1.503 + } while (recType != TERMINATE_RECORD_TYPE); 1.504 + 1.505 + sanitize_indexed_bitmap(bm); 1.506 + return true; 1.507 +} 1.508 + 1.509 +/////////////////////////////////////////////////////////////////////////////// 1.510 +DEFINE_DECODER_CREATOR(GIFImageDecoder); 1.511 +/////////////////////////////////////////////////////////////////////////////// 1.512 + 1.513 +static bool is_gif(SkStreamRewindable* stream) { 1.514 + char buf[GIF_STAMP_LEN]; 1.515 + if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 1.516 + if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || 1.517 + memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || 1.518 + memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { 1.519 + return true; 1.520 + } 1.521 + } 1.522 + return false; 1.523 +} 1.524 + 1.525 +static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) { 1.526 + if (is_gif(stream)) { 1.527 + return SkNEW(SkGIFImageDecoder); 1.528 + } 1.529 + return NULL; 1.530 +} 1.531 + 1.532 +static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); 1.533 + 1.534 +static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { 1.535 + if (is_gif(stream)) { 1.536 + return SkImageDecoder::kGIF_Format; 1.537 + } 1.538 + return SkImageDecoder::kUnknown_Format; 1.539 +} 1.540 + 1.541 +static SkImageDecoder_FormatReg gFormatReg(get_format_gif);