SkImageDecoder_libgif.cpp revision 8552a21d334cbc7971b109f27d05aea9d7690e92
1/* libs/graphics/images/SkImageDecoder_libgif.cpp 2** 3** Copyright 2006, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include "SkImageDecoder.h" 19#include "SkColor.h" 20#include "SkColorPriv.h" 21#include "SkStream.h" 22#include "SkTemplates.h" 23#include "SkPackBits.h" 24 25#include "gif_lib.h" 26 27class SkGIFImageDecoder : public SkImageDecoder { 28public: 29 virtual Format getFormat() const { 30 return kGIF_Format; 31 } 32 33protected: 34 virtual bool onDecode(SkStream* stream, SkBitmap* bm, 35 SkBitmap::Config pref, Mode mode); 36}; 37 38static const uint8_t gStartingIterlaceYValue[] = { 39 0, 4, 2, 1 40}; 41static const uint8_t gDeltaIterlaceYValue[] = { 42 8, 8, 4, 2 43}; 44 45/* Implement the GIF interlace algorithm in an iterator. 46 1) grab every 8th line beginning at 0 47 2) grab every 8th line beginning at 4 48 3) grab every 4th line beginning at 2 49 4) grab every 2nd line beginning at 1 50*/ 51class GifInterlaceIter { 52public: 53 GifInterlaceIter(int height) : fHeight(height) { 54 fStartYPtr = gStartingIterlaceYValue; 55 fDeltaYPtr = gDeltaIterlaceYValue; 56 57 fCurrY = *fStartYPtr++; 58 fDeltaY = *fDeltaYPtr++; 59 } 60 61 int currY() const { 62 SkASSERT(fStartYPtr); 63 SkASSERT(fDeltaYPtr); 64 return fCurrY; 65 } 66 67 void next() { 68 SkASSERT(fStartYPtr); 69 SkASSERT(fDeltaYPtr); 70 71 int y = fCurrY + fDeltaY; 72 // We went from an if statement to a while loop so that we iterate 73 // through fStartYPtr until a valid row is found. This is so that images 74 // that are smaller than 5x5 will not trash memory. 75 while (y >= fHeight) { 76 if (gStartingIterlaceYValue + 77 SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { 78 // we done 79 SkDEBUGCODE(fStartYPtr = NULL;) 80 SkDEBUGCODE(fDeltaYPtr = NULL;) 81 y = 0; 82 } else { 83 y = *fStartYPtr++; 84 fDeltaY = *fDeltaYPtr++; 85 } 86 } 87 fCurrY = y; 88 } 89 90private: 91 const int fHeight; 92 int fCurrY; 93 int fDeltaY; 94 const uint8_t* fStartYPtr; 95 const uint8_t* fDeltaYPtr; 96}; 97 98/////////////////////////////////////////////////////////////////////////////// 99 100//#define GIF_STAMP "GIF" /* First chars in file - GIF stamp. */ 101//#define GIF_STAMP_LEN (sizeof(GIF_STAMP) - 1) 102 103static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, 104 int size) { 105 SkStream* stream = (SkStream*) fileType->UserData; 106 return (int) stream->read(out, size); 107} 108 109void CheckFreeExtension(SavedImage* Image) { 110 if (Image->ExtensionBlocks) { 111 FreeExtension(Image); 112 } 113} 114 115// return NULL on failure 116static const ColorMapObject* find_colormap(const GifFileType* gif) { 117 const ColorMapObject* cmap = gif->Image.ColorMap; 118 if (NULL == cmap) { 119 cmap = gif->SColorMap; 120 } 121 // some sanity checks 122 if ((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 157bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, 158 SkBitmap::Config prefConfig, Mode mode) { 159 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); 160 if (NULL == gif) { 161 return error_return(gif, *bm, "DGifOpen"); 162 } 163 164 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); 165 166 SavedImage temp_save; 167 temp_save.ExtensionBlocks=NULL; 168 temp_save.ExtensionBlockCount=0; 169 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); 170 171 int width, height; 172 GifRecordType recType; 173 GifByteType *extData; 174 175 do { 176 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { 177 return error_return(gif, *bm, "DGifGetRecordType"); 178 } 179 180 switch (recType) { 181 case IMAGE_DESC_RECORD_TYPE: { 182 if (DGifGetImageDesc(gif) == GIF_ERROR) { 183 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); 184 } 185 186 if (gif->ImageCount < 1) { // sanity check 187 return error_return(gif, *bm, "ImageCount < 1"); 188 } 189 190 width = gif->SWidth; 191 height = gif->SHeight; 192 if (width <= 0 || height <= 0 || 193 !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, 194 width, height)) { 195 return error_return(gif, *bm, "chooseFromOneChoice"); 196 } 197 198 bm->setConfig(SkBitmap::kIndex8_Config, width, height); 199 if (SkImageDecoder::kDecodeBounds_Mode == mode) 200 return true; 201 202 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; 203 const GifImageDesc& desc = image->ImageDesc; 204 205 // check for valid descriptor 206 if ( (desc.Top | desc.Left) < 0 || 207 desc.Left + desc.Width > width || 208 desc.Top + desc.Height > height) { 209 return error_return(gif, *bm, "TopLeft"); 210 } 211 212 // now we decode the colortable 213 int colorCount = 0; 214 { 215 const ColorMapObject* cmap = find_colormap(gif); 216 if (NULL == cmap) { 217 return error_return(gif, *bm, "null cmap"); 218 } 219 220 colorCount = cmap->ColorCount; 221 SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); 222 SkPMColor* colorPtr = ctable->lockColors(); 223 for (int index = 0; index < colorCount; index++) 224 colorPtr[index] = SkPackARGB32(0xFF, 225 cmap->Colors[index].Red, 226 cmap->Colors[index].Green, 227 cmap->Colors[index].Blue); 228 229 int transpIndex = find_transpIndex(temp_save, colorCount); 230 if (transpIndex < 0) 231 ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); 232 else 233 colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor 234 ctable->unlockColors(true); 235 236 SkAutoUnref aurts(ctable); 237 if (!this->allocPixelRef(bm, ctable)) { 238 return error_return(gif, *bm, "allocPixelRef"); 239 } 240 } 241 242 SkAutoLockPixels alp(*bm); 243 244 // time to decode the scanlines 245 // 246 uint8_t* scanline = bm->getAddr8(0, 0); 247 const int rowBytes = bm->rowBytes(); 248 const int innerWidth = desc.Width; 249 const int innerHeight = desc.Height; 250 251 // abort if either inner dimension is <= 0 252 if (innerWidth <= 0 || innerHeight <= 0) { 253 return error_return(gif, *bm, "non-pos inner width/height"); 254 } 255 256 // are we only a subset of the total bounds? 257 if ((desc.Top | desc.Left) > 0 || 258 innerWidth < width || innerHeight < height) 259 { 260 uint8_t fill = (uint8_t)gif->SBackGroundColor; 261 // check for valid fill index/color 262 if (fill >= (unsigned)colorCount) { 263 fill = 0; 264 } 265 memset(scanline, gif->SBackGroundColor, bm->getSize()); 266 // bump our starting address 267 scanline += desc.Top * rowBytes + desc.Left; 268 } 269 270 // now decode each scanline 271 if (gif->Image.Interlace) 272 { 273 GifInterlaceIter iter(innerHeight); 274 for (int y = 0; y < innerHeight; y++) 275 { 276 uint8_t* row = scanline + iter.currY() * rowBytes; 277 if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { 278 return error_return(gif, *bm, "interlace DGifGetLine"); 279 } 280 iter.next(); 281 } 282 } 283 else 284 { 285 // easy, non-interlace case 286 for (int y = 0; y < innerHeight; y++) { 287 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { 288 return error_return(gif, *bm, "DGifGetLine"); 289 } 290 scanline += rowBytes; 291 } 292 } 293 goto DONE; 294 } break; 295 296 case EXTENSION_RECORD_TYPE: 297 if (DGifGetExtension(gif, &temp_save.Function, 298 &extData) == GIF_ERROR) { 299 return error_return(gif, *bm, "DGifGetExtension"); 300 } 301 302 while (extData != NULL) { 303 /* Create an extension block with our data */ 304 if (AddExtensionBlock(&temp_save, extData[0], 305 &extData[1]) == GIF_ERROR) { 306 return error_return(gif, *bm, "AddExtensionBlock"); 307 } 308 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { 309 return error_return(gif, *bm, "DGifGetExtensionNext"); 310 } 311 temp_save.Function = 0; 312 } 313 break; 314 315 case TERMINATE_RECORD_TYPE: 316 break; 317 318 default: /* Should be trapped by DGifGetRecordType */ 319 break; 320 } 321 } while (recType != TERMINATE_RECORD_TYPE); 322 323DONE: 324 return true; 325} 326 327/////////////////////////////////////////////////////////////////////////////// 328 329#include "SkTRegistry.h" 330 331static SkImageDecoder* Factory(SkStream* stream) { 332 char buf[GIF_STAMP_LEN]; 333 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { 334 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || 335 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || 336 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { 337 return SkNEW(SkGIFImageDecoder); 338 } 339 } 340 return NULL; 341} 342 343static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory); 344