1 2 /* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 #include "SkImageDecoder.h" 11 #include "SkColor.h" 12 #include "SkColorPriv.h" 13 #include "SkStream.h" 14 #include "SkTemplates.h" 15 #include "SkPackBits.h" 16 17 #include "gif_lib.h" 18 19 class SkGIFImageDecoder : public SkImageDecoder { 20 public: 21 virtual Format getFormat() const SK_OVERRIDE { 22 return kGIF_Format; 23 } 24 25 protected: 26 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; 27 28 private: 29 typedef SkImageDecoder INHERITED; 30 }; 31 32 static const uint8_t gStartingIterlaceYValue[] = { 33 0, 4, 2, 1 34 }; 35 static 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 */ 45 class GifInterlaceIter { 46 public: 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 84 private: 85 const int fHeight; 86 int fCurrY; 87 int fDeltaY; 88 const uint8_t* fStartYPtr; 89 const uint8_t* fDeltaYPtr; 90 }; 91 92 /////////////////////////////////////////////////////////////////////////////// 93 94 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, 95 int size) { 96 SkStream* stream = (SkStream*) fileType->UserData; 97 return (int) stream->read(out, size); 98 } 99 100 void 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 111 static 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) 130 static 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 148 static 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 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { 158 #if GIFLIB_MAJOR < 5 159 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); 160 #else 161 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); 162 #endif 163 if (NULL == gif) { 164 return error_return(gif, *bm, "DGifOpen"); 165 } 166 167 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); 168 169 SavedImage temp_save; 170 temp_save.ExtensionBlocks=NULL; 171 temp_save.ExtensionBlockCount=0; 172 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); 173 174 int width, height; 175 GifRecordType recType; 176 GifByteType *extData; 177 #if GIFLIB_MAJOR >= 5 178 int extFunction; 179 #endif 180 int transpIndex = -1; // -1 means we don't have it (yet) 181 182 do { 183 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { 184 return error_return(gif, *bm, "DGifGetRecordType"); 185 } 186 187 switch (recType) { 188 case IMAGE_DESC_RECORD_TYPE: { 189 if (DGifGetImageDesc(gif) == GIF_ERROR) { 190 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); 191 } 192 193 if (gif->ImageCount < 1) { // sanity check 194 return error_return(gif, *bm, "ImageCount < 1"); 195 } 196 197 width = gif->SWidth; 198 height = gif->SHeight; 199 if (width <= 0 || height <= 0 || 200 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, 201 width, height)) { 202 return error_return(gif, *bm, "chooseFromOneChoice"); 203 } 204 205 bm->setConfig(SkBitmap::kIndex8_Config, width, height); 206 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 207 return true; 208 } 209 210 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; 211 const GifImageDesc& desc = image->ImageDesc; 212 213 // check for valid descriptor 214 if ( (desc.Top | desc.Left) < 0 || 215 desc.Left + desc.Width > width || 216 desc.Top + desc.Height > height) { 217 return error_return(gif, *bm, "TopLeft"); 218 } 219 220 // now we decode the colortable 221 int colorCount = 0; 222 { 223 const ColorMapObject* cmap = find_colormap(gif); 224 if (NULL == cmap) { 225 return error_return(gif, *bm, "null cmap"); 226 } 227 228 colorCount = cmap->ColorCount; 229 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); 230 SkPMColor* colorPtr = ctable->lockColors(); 231 for (int index = 0; index < colorCount; index++) 232 colorPtr[index] = SkPackARGB32(0xFF, 233 cmap->Colors[index].Red, 234 cmap->Colors[index].Green, 235 cmap->Colors[index].Blue); 236 237 transpIndex = find_transpIndex(temp_save, colorCount); 238 if (transpIndex < 0) 239 ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); 240 else 241 colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor 242 ctable->unlockColors(true); 243 244 SkAutoUnref aurts(ctable); 245 if (!this->allocPixelRef(bm, ctable)) { 246 return error_return(gif, *bm, "allocPixelRef"); 247 } 248 } 249 250 SkAutoLockPixels alp(*bm); 251 252 // time to decode the scanlines 253 // 254 uint8_t* scanline = bm->getAddr8(0, 0); 255 const int rowBytes = bm->rowBytes(); 256 const int innerWidth = desc.Width; 257 const int innerHeight = desc.Height; 258 259 // abort if either inner dimension is <= 0 260 if (innerWidth <= 0 || innerHeight <= 0) { 261 return error_return(gif, *bm, "non-pos inner width/height"); 262 } 263 264 // are we only a subset of the total bounds? 265 if ((desc.Top | desc.Left) > 0 || 266 innerWidth < width || innerHeight < height) 267 { 268 int fill; 269 if (transpIndex >= 0) { 270 fill = transpIndex; 271 } else { 272 fill = gif->SBackGroundColor; 273 } 274 // check for valid fill index/color 275 if (static_cast<unsigned>(fill) >= 276 static_cast<unsigned>(colorCount)) { 277 fill = 0; 278 } 279 memset(scanline, fill, bm->getSize()); 280 // bump our starting address 281 scanline += desc.Top * rowBytes + desc.Left; 282 } 283 284 // now decode each scanline 285 if (gif->Image.Interlace) 286 { 287 GifInterlaceIter iter(innerHeight); 288 for (int y = 0; y < innerHeight; y++) 289 { 290 uint8_t* row = scanline + iter.currY() * rowBytes; 291 if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { 292 return error_return(gif, *bm, "interlace DGifGetLine"); 293 } 294 iter.next(); 295 } 296 } 297 else 298 { 299 // easy, non-interlace case 300 for (int y = 0; y < innerHeight; y++) { 301 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 302 return error_return(gif, *bm, "DGifGetLine"); 303 } 304 scanline += rowBytes; 305 } 306 } 307 goto DONE; 308 } break; 309 310 case EXTENSION_RECORD_TYPE: 311 #if GIFLIB_MAJOR < 5 312 if (DGifGetExtension(gif, &temp_save.Function, 313 &extData) == GIF_ERROR) { 314 #else 315 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { 316 #endif 317 return error_return(gif, *bm, "DGifGetExtension"); 318 } 319 320 while (extData != NULL) { 321 /* Create an extension block with our data */ 322 #if GIFLIB_MAJOR < 5 323 if (AddExtensionBlock(&temp_save, extData[0], 324 &extData[1]) == GIF_ERROR) { 325 #else 326 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, 327 &gif->ExtensionBlocks, 328 extFunction, 329 extData[0], 330 &extData[1]) == GIF_ERROR) { 331 #endif 332 return error_return(gif, *bm, "AddExtensionBlock"); 333 } 334 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { 335 return error_return(gif, *bm, "DGifGetExtensionNext"); 336 } 337 #if GIFLIB_MAJOR < 5 338 temp_save.Function = 0; 339 #endif 340 } 341 break; 342 343 case TERMINATE_RECORD_TYPE: 344 break; 345 346 default: /* Should be trapped by DGifGetRecordType */ 347 break; 348 } 349 } while (recType != TERMINATE_RECORD_TYPE); 350 351 DONE: 352 return true; 353 } 354 355 /////////////////////////////////////////////////////////////////////////////// 356 DEFINE_DECODER_CREATOR(GIFImageDecoder); 357 /////////////////////////////////////////////////////////////////////////////// 358 359 static bool is_gif(SkStream* stream) { 360 char buf[GIF_STAMP_LEN]; 361 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 362 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || 363 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || 364 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { 365 return true; 366 } 367 } 368 return false; 369 } 370 371 #include "SkTRegistry.h" 372 373 static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) { 374 if (is_gif(stream)) { 375 return SkNEW(SkGIFImageDecoder); 376 } 377 return NULL; 378 } 379 380 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory); 381 382 static SkImageDecoder::Format get_format_gif(SkStream* stream) { 383 if (is_gif(stream)) { 384 return SkImageDecoder::kGIF_Format; 385 } 386 return SkImageDecoder::kUnknown_Format; 387 } 388 389 static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_gif); 390