| OLD | NEW |
| (Empty) |
| 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 | |
| 27 class SkGIFImageDecoder : public SkImageDecoder { | |
| 28 public: | |
| 29 virtual Format getFormat() const { | |
| 30 return kGIF_Format; | |
| 31 } | |
| 32 | |
| 33 protected: | |
| 34 virtual bool onDecode(SkStream* stream, SkBitmap* bm, | |
| 35 SkBitmap::Config pref, Mode mode); | |
| 36 }; | |
| 37 | |
| 38 static const uint8_t gStartingIterlaceYValue[] = { | |
| 39 0, 4, 2, 1 | |
| 40 }; | |
| 41 static 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 */ | |
| 51 class GifInterlaceIter { | |
| 52 public: | |
| 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 | |
| 90 private: | |
| 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 | |
| 103 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, | |
| 104 int size) { | |
| 105 SkStream* stream = (SkStream*) fileType->UserData; | |
| 106 return (int) stream->read(out, size); | |
| 107 } | |
| 108 | |
| 109 void CheckFreeExtension(SavedImage* Image) { | |
| 110 if (Image->ExtensionBlocks) { | |
| 111 FreeExtension(Image); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 // return NULL on failure | |
| 116 static const ColorMapObject* find_colormap(const GifFileType* gif) { | |
| 117 const ColorMapObject* cmap = gif->SColorMap; | |
| 118 if (NULL == cmap) { | |
| 119 cmap = gif->Image.ColorMap; | |
| 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) | |
| 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, | |
| 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::kColorsA
reOpaque_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 | |
| 323 DONE: | |
| 324 return true; | |
| 325 } | |
| 326 | |
| 327 /////////////////////////////////////////////////////////////////////////////// | |
| 328 | |
| 329 SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream* stream) { | |
| 330 char buf[GIF_STAMP_LEN]; | |
| 331 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | |
| 332 if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
| 333 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || | |
| 334 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { | |
| 335 return SkNEW(SkGIFImageDecoder); | |
| 336 } | |
| 337 } | |
| 338 return NULL; | |
| 339 } | |
| 340 | |
| OLD | NEW |