SkImageDecoder_libgif.cpp revision 9864c24e40448ef64b1fe367eee1d63a872e51b8
1/* 2 * Copyright 2006 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 9#include "SkColor.h" 10#include "SkColorPriv.h" 11#include "SkColorTable.h" 12#include "SkImageDecoder.h" 13#include "SkScaledBitmapSampler.h" 14#include "SkStream.h" 15#include "SkTemplates.h" 16 17#include "gif_lib.h" 18 19class SkGIFImageDecoder : public SkImageDecoder { 20public: 21 virtual Format getFormat() const SK_OVERRIDE { 22 return kGIF_Format; 23 } 24 25protected: 26 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; 27 28private: 29 typedef SkImageDecoder INHERITED; 30}; 31 32static const uint8_t gStartingIterlaceYValue[] = { 33 0, 4, 2, 1 34}; 35static const uint8_t gDeltaIterlaceYValue[] = { 36 8, 8, 4, 2 37}; 38 39/* Implement the GIF interlace algorithm in an iterator. 40 1) grab every 8th line beginning at 0 41 2) grab every 8th line beginning at 4 42 3) grab every 4th line beginning at 2 43 4) grab every 2nd line beginning at 1 44*/ 45class GifInterlaceIter { 46public: 47 GifInterlaceIter(int height) : fHeight(height) { 48 fStartYPtr = gStartingIterlaceYValue; 49 fDeltaYPtr = gDeltaIterlaceYValue; 50 51 fCurrY = *fStartYPtr++; 52 fDeltaY = *fDeltaYPtr++; 53 } 54 55 int currY() const { 56 SkASSERT(fStartYPtr); 57 SkASSERT(fDeltaYPtr); 58 return fCurrY; 59 } 60 61 void next() { 62 SkASSERT(fStartYPtr); 63 SkASSERT(fDeltaYPtr); 64 65 int y = fCurrY + fDeltaY; 66 // We went from an if statement to a while loop so that we iterate 67 // through fStartYPtr until a valid row is found. This is so that images 68 // that are smaller than 5x5 will not trash memory. 69 while (y >= fHeight) { 70 if (gStartingIterlaceYValue + 71 SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { 72 // we done 73 SkDEBUGCODE(fStartYPtr = NULL;) 74 SkDEBUGCODE(fDeltaYPtr = NULL;) 75 y = 0; 76 } else { 77 y = *fStartYPtr++; 78 fDeltaY = *fDeltaYPtr++; 79 } 80 } 81 fCurrY = y; 82 } 83 84private: 85 const int fHeight; 86 int fCurrY; 87 int fDeltaY; 88 const uint8_t* fStartYPtr; 89 const uint8_t* fDeltaYPtr; 90}; 91 92/////////////////////////////////////////////////////////////////////////////// 93 94static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, 95 int size) { 96 SkStream* stream = (SkStream*) fileType->UserData; 97 return (int) stream->read(out, size); 98} 99 100void CheckFreeExtension(SavedImage* Image) { 101 if (Image->ExtensionBlocks) { 102#if GIFLIB_MAJOR < 5 103 FreeExtension(Image); 104#else 105 GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks); 106#endif 107 } 108} 109 110// return NULL on failure 111static const ColorMapObject* find_colormap(const GifFileType* gif) { 112 const ColorMapObject* cmap = gif->Image.ColorMap; 113 if (NULL == cmap) { 114 cmap = gif->SColorMap; 115 } 116 117 if (NULL == cmap) { 118 // no colormap found 119 return NULL; 120 } 121 // some sanity checks 122 if (cmap && ((unsigned)cmap->ColorCount > 256 || 123 cmap->ColorCount != (1 << cmap->BitsPerPixel))) { 124 cmap = NULL; 125 } 126 return cmap; 127} 128 129// return -1 if not found (i.e. we're completely opaque) 130static int find_transpIndex(const SavedImage& image, int colorCount) { 131 int transpIndex = -1; 132 for (int i = 0; i < image.ExtensionBlockCount; ++i) { 133 const ExtensionBlock* eb = image.ExtensionBlocks + i; 134 if (eb->Function == 0xF9 && eb->ByteCount == 4) { 135 if (eb->Bytes[0] & 1) { 136 transpIndex = (unsigned char)eb->Bytes[3]; 137 // check for valid transpIndex 138 if (transpIndex >= colorCount) { 139 transpIndex = -1; 140 } 141 break; 142 } 143 } 144 } 145 return transpIndex; 146} 147 148static bool error_return(GifFileType* gif, const SkBitmap& bm, 149 const char msg[]) { 150#if 0 151 SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n", 152 msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); 153#endif 154 return false; 155} 156 157/** 158 * Skip rows in the source gif image. 159 * @param gif Source image. 160 * @param dst Scratch output needed by gif library call. Must be >= width bytes. 161 * @param width Bytes per row in the source image. 162 * @param rowsToSkip Number of rows to skip. 163 * @return True on success, false on GIF_ERROR. 164 */ 165static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) { 166 for (int i = 0; i < rowsToSkip; i++) { 167 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { 168 return false; 169 } 170 } 171 return true; 172} 173 174bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { 175#if GIFLIB_MAJOR < 5 176 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); 177#else 178 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); 179#endif 180 if (NULL == gif) { 181 return error_return(gif, *bm, "DGifOpen"); 182 } 183 184 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); 185 186 SavedImage temp_save; 187 temp_save.ExtensionBlocks=NULL; 188 temp_save.ExtensionBlockCount=0; 189 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); 190 191 int width, height; 192 GifRecordType recType; 193 GifByteType *extData; 194#if GIFLIB_MAJOR >= 5 195 int extFunction; 196#endif 197 int transpIndex = -1; // -1 means we don't have it (yet) 198 199 do { 200 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { 201 return error_return(gif, *bm, "DGifGetRecordType"); 202 } 203 204 switch (recType) { 205 case IMAGE_DESC_RECORD_TYPE: { 206 if (DGifGetImageDesc(gif) == GIF_ERROR) { 207 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); 208 } 209 210 if (gif->ImageCount < 1) { // sanity check 211 return error_return(gif, *bm, "ImageCount < 1"); 212 } 213 214 width = gif->SWidth; 215 height = gif->SHeight; 216 if (width <= 0 || height <= 0) { 217 return error_return(gif, *bm, "invalid dimensions"); 218 } 219 220 // FIXME: We could give the caller a choice of images or configs. 221 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) { 222 return error_return(gif, *bm, "chooseFromOneChoice"); 223 } 224 225 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); 226 227 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), 228 sampler.scaledHeight()); 229 230 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 231 return true; 232 } 233 234 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; 235 const GifImageDesc& desc = image->ImageDesc; 236 237 // check for valid descriptor 238 if ( (desc.Top | desc.Left) < 0 || 239 desc.Left + desc.Width > width || 240 desc.Top + desc.Height > height) { 241 return error_return(gif, *bm, "TopLeft"); 242 } 243 244 // now we decode the colortable 245 int colorCount = 0; 246 { 247 const ColorMapObject* cmap = find_colormap(gif); 248 if (NULL == cmap) { 249 return error_return(gif, *bm, "null cmap"); 250 } 251 colorCount = cmap->ColorCount; 252 if (colorCount > 256) { 253 colorCount = 256; // our kIndex8 can't support more 254 } 255 256 SkPMColor colorPtr[256]; // storage for worst-case 257 SkAlphaType alphaType = kOpaque_SkAlphaType; 258 for (int index = 0; index < colorCount; index++) { 259 colorPtr[index] = SkPackARGB32(0xFF, 260 cmap->Colors[index].Red, 261 cmap->Colors[index].Green, 262 cmap->Colors[index].Blue); 263 } 264 265 transpIndex = find_transpIndex(temp_save, colorCount); 266 bool reallyHasAlpha = transpIndex >= 0; 267 if (reallyHasAlpha) { 268 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor 269 alphaType = kPremul_SkAlphaType; 270 } 271 272 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, 273 (colorPtr, colorCount, 274 alphaType))); 275 if (!this->allocPixelRef(bm, ctable)) { 276 return error_return(gif, *bm, "allocPixelRef"); 277 } 278 } 279 280 const int innerWidth = desc.Width; 281 const int innerHeight = desc.Height; 282 283 // abort if either inner dimension is <= 0 284 if (innerWidth <= 0 || innerHeight <= 0) { 285 return error_return(gif, *bm, "non-pos inner width/height"); 286 } 287 288 SkAutoLockPixels alp(*bm); 289 290 SkAutoMalloc storage(innerWidth); 291 uint8_t* scanline = (uint8_t*) storage.get(); 292 293 // GIF has an option to store the scanlines of an image, plus a larger background, 294 // filled by a fill color. In this case, we will use a subset of the larger bitmap 295 // for sampling. 296 SkBitmap subset; 297 SkBitmap* workingBitmap; 298 // are we only a subset of the total bounds? 299 if ((desc.Top | desc.Left) > 0 || 300 innerWidth < width || innerHeight < height) { 301 int fill; 302 if (transpIndex >= 0) { 303 fill = transpIndex; 304 } else { 305 fill = gif->SBackGroundColor; 306 } 307 // check for valid fill index/color 308 if (static_cast<unsigned>(fill) >= 309 static_cast<unsigned>(colorCount)) { 310 fill = 0; 311 } 312 // Fill the background. 313 memset(bm->getPixels(), fill, bm->getSize()); 314 315 // Create a subset of the bitmap. 316 SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX(), 317 desc.Top / sampler.srcDY(), 318 innerWidth / sampler.srcDX(), 319 innerHeight / sampler.srcDY())); 320 if (!bm->extractSubset(&subset, subsetRect)) { 321 return error_return(gif, *bm, "Extract failed."); 322 } 323 // Update the sampler. We'll now be only sampling into the subset. 324 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize()); 325 workingBitmap = ⊂ 326 } else { 327 workingBitmap = bm; 328 } 329 330 // bm is already locked, but if we had to take a subset, it must be locked also, 331 // so that getPixels() will point to its pixels. 332 SkAutoLockPixels alpWorking(*workingBitmap); 333 334 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) { 335 return error_return(gif, *bm, "Sampler failed to begin."); 336 } 337 338 // now decode each scanline 339 if (gif->Image.Interlace) { 340 // Iterate over the height of the source data. The sampler will 341 // take care of skipping unneeded rows. 342 GifInterlaceIter iter(innerHeight); 343 for (int y = 0; y < innerHeight; y++){ 344 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 345 return error_return(gif, *bm, "interlace DGifGetLine"); 346 } 347 sampler.sampleInterlaced(scanline, iter.currY()); 348 iter.next(); 349 } 350 } else { 351 // easy, non-interlace case 352 const int outHeight = workingBitmap->height(); 353 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); 354 for (int y = 0; y < outHeight; y++) { 355 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 356 return error_return(gif, *bm, "DGifGetLine"); 357 } 358 // scanline now contains the raw data. Sample it. 359 sampler.next(scanline); 360 if (y < outHeight - 1) { 361 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1); 362 } 363 } 364 // skip the rest of the rows (if any) 365 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1; 366 SkASSERT(read <= innerHeight); 367 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); 368 } 369 goto DONE; 370 } break; 371 372 case EXTENSION_RECORD_TYPE: 373#if GIFLIB_MAJOR < 5 374 if (DGifGetExtension(gif, &temp_save.Function, 375 &extData) == GIF_ERROR) { 376#else 377 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { 378#endif 379 return error_return(gif, *bm, "DGifGetExtension"); 380 } 381 382 while (extData != NULL) { 383 /* Create an extension block with our data */ 384#if GIFLIB_MAJOR < 5 385 if (AddExtensionBlock(&temp_save, extData[0], 386 &extData[1]) == GIF_ERROR) { 387#else 388 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, 389 &gif->ExtensionBlocks, 390 extFunction, 391 extData[0], 392 &extData[1]) == GIF_ERROR) { 393#endif 394 return error_return(gif, *bm, "AddExtensionBlock"); 395 } 396 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { 397 return error_return(gif, *bm, "DGifGetExtensionNext"); 398 } 399#if GIFLIB_MAJOR < 5 400 temp_save.Function = 0; 401#endif 402 } 403 break; 404 405 case TERMINATE_RECORD_TYPE: 406 break; 407 408 default: /* Should be trapped by DGifGetRecordType */ 409 break; 410 } 411 } while (recType != TERMINATE_RECORD_TYPE); 412 413DONE: 414 return true; 415} 416 417/////////////////////////////////////////////////////////////////////////////// 418DEFINE_DECODER_CREATOR(GIFImageDecoder); 419/////////////////////////////////////////////////////////////////////////////// 420 421static bool is_gif(SkStreamRewindable* stream) { 422 char buf[GIF_STAMP_LEN]; 423 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 424 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || 425 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || 426 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { 427 return true; 428 } 429 } 430 return false; 431} 432 433static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) { 434 if (is_gif(stream)) { 435 return SkNEW(SkGIFImageDecoder); 436 } 437 return NULL; 438} 439 440static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); 441 442static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { 443 if (is_gif(stream)) { 444 return SkImageDecoder::kGIF_Format; 445 } 446 return SkImageDecoder::kUnknown_Format; 447} 448 449static SkImageDecoder_FormatReg gFormatReg(get_format_gif); 450