| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 | |
| 9 #include "SkColor.h" | 8 #include "SkColor.h" |
| 10 #include "SkColorPriv.h" | 9 #include "SkColorPriv.h" |
| 11 #include "SkColorTable.h" | 10 #include "SkColorTable.h" |
| 12 #include "SkImageDecoder.h" | 11 #include "SkImageDecoder.h" |
| 12 #include "SkRTConf.h" |
| 13 #include "SkScaledBitmapSampler.h" | 13 #include "SkScaledBitmapSampler.h" |
| 14 #include "SkStream.h" | 14 #include "SkStream.h" |
| 15 #include "SkTemplates.h" | 15 #include "SkTemplates.h" |
| 16 #include "SkUtils.h" |
| 16 | 17 |
| 17 #include "gif_lib.h" | 18 #include "gif_lib.h" |
| 18 | 19 |
| 19 class SkGIFImageDecoder : public SkImageDecoder { | 20 class SkGIFImageDecoder : public SkImageDecoder { |
| 20 public: | 21 public: |
| 21 virtual Format getFormat() const SK_OVERRIDE { | 22 virtual Format getFormat() const SK_OVERRIDE { |
| 22 return kGIF_Format; | 23 return kGIF_Format; |
| 23 } | 24 } |
| 24 | 25 |
| 25 protected: | 26 protected: |
| 26 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE
; | 27 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE
; |
| 27 | 28 |
| 28 private: | 29 private: |
| 29 typedef SkImageDecoder INHERITED; | 30 typedef SkImageDecoder INHERITED; |
| 30 }; | 31 }; |
| 31 | 32 |
| 32 static const uint8_t gStartingIterlaceYValue[] = { | 33 static const uint8_t gStartingIterlaceYValue[] = { |
| 33 0, 4, 2, 1 | 34 0, 4, 2, 1 |
| 34 }; | 35 }; |
| 35 static const uint8_t gDeltaIterlaceYValue[] = { | 36 static const uint8_t gDeltaIterlaceYValue[] = { |
| 36 8, 8, 4, 2 | 37 8, 8, 4, 2 |
| 37 }; | 38 }; |
| 38 | 39 |
| 40 SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings, |
| 41 "images.gif.suppressDecoderWarnings", true, |
| 42 "Suppress GIF warnings and errors when calling image decode " |
| 43 "functions."); |
| 44 |
| 45 |
| 39 /* Implement the GIF interlace algorithm in an iterator. | 46 /* Implement the GIF interlace algorithm in an iterator. |
| 40 1) grab every 8th line beginning at 0 | 47 1) grab every 8th line beginning at 0 |
| 41 2) grab every 8th line beginning at 4 | 48 2) grab every 8th line beginning at 4 |
| 42 3) grab every 4th line beginning at 2 | 49 3) grab every 4th line beginning at 2 |
| 43 4) grab every 2nd line beginning at 1 | 50 4) grab every 2nd line beginning at 1 |
| 44 */ | 51 */ |
| 45 class GifInterlaceIter { | 52 class GifInterlaceIter { |
| 46 public: | 53 public: |
| 47 GifInterlaceIter(int height) : fHeight(height) { | 54 GifInterlaceIter(int height) : fHeight(height) { |
| 48 fStartYPtr = gStartingIterlaceYValue; | 55 fStartYPtr = gStartingIterlaceYValue; |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 if (transpIndex >= colorCount) { | 145 if (transpIndex >= colorCount) { |
| 139 transpIndex = -1; | 146 transpIndex = -1; |
| 140 } | 147 } |
| 141 break; | 148 break; |
| 142 } | 149 } |
| 143 } | 150 } |
| 144 } | 151 } |
| 145 return transpIndex; | 152 return transpIndex; |
| 146 } | 153 } |
| 147 | 154 |
| 148 static bool error_return(GifFileType* gif, const SkBitmap& bm, | 155 static bool error_return(const SkBitmap& bm, const char msg[]) { |
| 149 const char msg[]) { | 156 if (!c_suppressGIFImageDecoderWarnings) { |
| 150 #if 0 | 157 SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n", |
| 151 SkDebugf("libgif error <%s> bitmap [%d %d] pixels %p colortable %p\n", | 158 msg, bm.width(), bm.height(), bm.getPixels(), |
| 152 msg, bm.width(), bm.height(), bm.getPixels(), bm.getColorTable()); | 159 bm.getColorTable()); |
| 153 #endif | 160 } |
| 154 return false; | 161 return false; |
| 155 } | 162 } |
| 163 static void gif_warning(const SkBitmap& bm, const char msg[]) { |
| 164 if (!c_suppressGIFImageDecoderWarnings) { |
| 165 SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n", |
| 166 msg, bm.width(), bm.height(), bm.getPixels(), |
| 167 bm.getColorTable()); |
| 168 } |
| 169 } |
| 156 | 170 |
| 157 /** | 171 /** |
| 158 * Skip rows in the source gif image. | 172 * Skip rows in the source gif image. |
| 159 * @param gif Source image. | 173 * @param gif Source image. |
| 160 * @param dst Scratch output needed by gif library call. Must be >= width bytes
. | 174 * @param dst Scratch output needed by gif library call. Must be >= width bytes
. |
| 161 * @param width Bytes per row in the source image. | 175 * @param width Bytes per row in the source image. |
| 162 * @param rowsToSkip Number of rows to skip. | 176 * @param rowsToSkip Number of rows to skip. |
| 163 * @return True on success, false on GIF_ERROR. | 177 * @return True on success, false on GIF_ERROR. |
| 164 */ | 178 */ |
| 165 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToS
kip) { | 179 static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToS
kip) { |
| 166 for (int i = 0; i < rowsToSkip; i++) { | 180 for (int i = 0; i < rowsToSkip; i++) { |
| 167 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { | 181 if (DGifGetLine(gif, dst, width) == GIF_ERROR) { |
| 168 return false; | 182 return false; |
| 169 } | 183 } |
| 170 } | 184 } |
| 171 return true; | 185 return true; |
| 172 } | 186 } |
| 173 | 187 |
| 174 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { | 188 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
| 175 #if GIFLIB_MAJOR < 5 | 189 #if GIFLIB_MAJOR < 5 |
| 176 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); | 190 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); |
| 177 #else | 191 #else |
| 178 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); | 192 GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL); |
| 179 #endif | 193 #endif |
| 180 if (NULL == gif) { | 194 if (NULL == gif) { |
| 181 return error_return(gif, *bm, "DGifOpen"); | 195 return error_return(*bm, "DGifOpen"); |
| 182 } | 196 } |
| 183 | 197 |
| 184 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); | 198 SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif); |
| 185 | 199 |
| 186 SavedImage temp_save; | 200 SavedImage temp_save; |
| 187 temp_save.ExtensionBlocks=NULL; | 201 temp_save.ExtensionBlocks=NULL; |
| 188 temp_save.ExtensionBlockCount=0; | 202 temp_save.ExtensionBlockCount=0; |
| 189 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); | 203 SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); |
| 190 | 204 |
| 191 int width, height; | 205 int width, height; |
| 192 GifRecordType recType; | 206 GifRecordType recType; |
| 193 GifByteType *extData; | 207 GifByteType *extData; |
| 194 #if GIFLIB_MAJOR >= 5 | 208 #if GIFLIB_MAJOR >= 5 |
| 195 int extFunction; | 209 int extFunction; |
| 196 #endif | 210 #endif |
| 197 int transpIndex = -1; // -1 means we don't have it (yet) | 211 int transpIndex = -1; // -1 means we don't have it (yet) |
| 212 int fillIndex = gif->SBackGroundColor; |
| 198 | 213 |
| 199 do { | 214 do { |
| 200 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { | 215 if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { |
| 201 return error_return(gif, *bm, "DGifGetRecordType"); | 216 return error_return(*bm, "DGifGetRecordType"); |
| 202 } | 217 } |
| 203 | 218 |
| 204 switch (recType) { | 219 switch (recType) { |
| 205 case IMAGE_DESC_RECORD_TYPE: { | 220 case IMAGE_DESC_RECORD_TYPE: { |
| 206 if (DGifGetImageDesc(gif) == GIF_ERROR) { | 221 if (DGifGetImageDesc(gif) == GIF_ERROR) { |
| 207 return error_return(gif, *bm, "IMAGE_DESC_RECORD_TYPE"); | 222 return error_return(*bm, "IMAGE_DESC_RECORD_TYPE"); |
| 208 } | 223 } |
| 209 | 224 |
| 210 if (gif->ImageCount < 1) { // sanity check | 225 if (gif->ImageCount < 1) { // sanity check |
| 211 return error_return(gif, *bm, "ImageCount < 1"); | 226 return error_return(*bm, "ImageCount < 1"); |
| 212 } | 227 } |
| 213 | 228 |
| 214 width = gif->SWidth; | 229 width = gif->SWidth; |
| 215 height = gif->SHeight; | 230 height = gif->SHeight; |
| 216 if (width <= 0 || height <= 0) { | 231 |
| 217 return error_return(gif, *bm, "invalid dimensions"); | 232 SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; |
| 233 const GifImageDesc& desc = image->ImageDesc; |
| 234 |
| 235 int imageLeft = desc.Left; |
| 236 int imageTop = desc.Top; |
| 237 const int innerWidth = desc.Width; |
| 238 const int innerHeight = desc.Height; |
| 239 if (innerWidth <= 0 || innerHeight <= 0) { |
| 240 return error_return(*bm, "invalid dimensions"); |
| 241 } |
| 242 |
| 243 // check for valid descriptor |
| 244 if (innerWidth > width) { |
| 245 gif_warning(*bm, "image too wide, expanding output to size"); |
| 246 width = innerWidth; |
| 247 imageLeft = 0; |
| 248 } else if (imageLeft + innerWidth > width) { |
| 249 gif_warning(*bm, "shifting image left to fit"); |
| 250 imageLeft = width - innerWidth; |
| 251 } else if (imageLeft < 0) { |
| 252 gif_warning(*bm, "shifting image right to fit"); |
| 253 imageLeft = 0; |
| 254 } |
| 255 |
| 256 |
| 257 if (innerHeight > height) { |
| 258 gif_warning(*bm, "image too tall, expanding output to size"); |
| 259 height = innerHeight; |
| 260 imageTop = 0; |
| 261 } else if (imageTop + innerHeight > height) { |
| 262 gif_warning(*bm, "shifting image up to fit"); |
| 263 imageTop = height - innerHeight; |
| 264 } else if (imageTop < 0) { |
| 265 gif_warning(*bm, "shifting image down to fit"); |
| 266 imageTop = 0; |
| 218 } | 267 } |
| 219 | 268 |
| 220 // FIXME: We could give the caller a choice of images or configs. | 269 // FIXME: We could give the caller a choice of images or configs. |
| 221 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, heig
ht)) { | 270 if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, heig
ht)) { |
| 222 return error_return(gif, *bm, "chooseFromOneChoice"); | 271 return error_return(*bm, "chooseFromOneChoice"); |
| 223 } | 272 } |
| 224 | 273 |
| 225 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); | 274 SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); |
| 226 | 275 |
| 227 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), | 276 bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), |
| 228 sampler.scaledHeight()); | 277 sampler.scaledHeight()); |
| 229 | 278 |
| 230 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | 279 if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
| 231 return true; | 280 return true; |
| 232 } | 281 } |
| 233 | 282 |
| 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 | 283 |
| 244 // now we decode the colortable | 284 // now we decode the colortable |
| 245 int colorCount = 0; | 285 int colorCount = 0; |
| 246 { | 286 { |
| 287 // Declare colorPtr here for scope. |
| 288 SkPMColor colorPtr[256]; // storage for worst-case |
| 247 const ColorMapObject* cmap = find_colormap(gif); | 289 const ColorMapObject* cmap = find_colormap(gif); |
| 248 if (NULL == cmap) { | 290 SkAlphaType alphaType = kOpaque_SkAlphaType; |
| 249 return error_return(gif, *bm, "null cmap"); | 291 if (cmap != NULL) { |
| 292 colorCount = cmap->ColorCount; |
| 293 if (colorCount > 256) { |
| 294 colorCount = 256; // our kIndex8 can't support more |
| 295 } |
| 296 for (int index = 0; index < colorCount; index++) { |
| 297 colorPtr[index] = SkPackARGB32(0xFF, |
| 298 cmap->Colors[index].Red, |
| 299 cmap->Colors[index].Green
, |
| 300 cmap->Colors[index].Blue)
; |
| 301 } |
| 302 } else { |
| 303 // find_colormap() returned NULL. Some (rare, broken) |
| 304 // GIFs don't have a color table, so we force one. |
| 305 gif_warning(*bm, "missing colormap"); |
| 306 colorCount = 256; |
| 307 sk_memset32(colorPtr, SK_ColorWHITE, colorCount); |
| 250 } | 308 } |
| 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); | 309 transpIndex = find_transpIndex(temp_save, colorCount); |
| 266 bool reallyHasAlpha = transpIndex >= 0; | 310 if (transpIndex >= 0) { |
| 267 if (reallyHasAlpha) { | |
| 268 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a tra
nsparent SkPMColor | 311 colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a tra
nsparent SkPMColor |
| 269 alphaType = kPremul_SkAlphaType; | 312 alphaType = kPremul_SkAlphaType; |
| 313 fillIndex = transpIndex; |
| 314 } else if (fillIndex >= colorCount) { |
| 315 // gif->SBackGroundColor should be less than colorCount. |
| 316 fillIndex = 0; // If not, fix it. |
| 270 } | 317 } |
| 271 | 318 |
| 272 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, | 319 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, |
| 273 (colorPtr, colorCount, | 320 (colorPtr, colorCount, |
| 274 alphaType))); | 321 alphaType))); |
| 275 if (!this->allocPixelRef(bm, ctable)) { | 322 if (!this->allocPixelRef(bm, ctable)) { |
| 276 return error_return(gif, *bm, "allocPixelRef"); | 323 return error_return(*bm, "allocPixelRef"); |
| 277 } | 324 } |
| 278 } | 325 } |
| 279 | 326 |
| 280 const int innerWidth = desc.Width; | |
| 281 const int innerHeight = desc.Height; | |
| 282 | |
| 283 // abort if either inner dimension is <= 0 | 327 // abort if either inner dimension is <= 0 |
| 284 if (innerWidth <= 0 || innerHeight <= 0) { | 328 if (innerWidth <= 0 || innerHeight <= 0) { |
| 285 return error_return(gif, *bm, "non-pos inner width/height"); | 329 return error_return(*bm, "non-pos inner width/height"); |
| 286 } | 330 } |
| 287 | 331 |
| 288 SkAutoLockPixels alp(*bm); | 332 SkAutoLockPixels alp(*bm); |
| 289 | 333 |
| 290 SkAutoMalloc storage(innerWidth); | 334 SkAutoMalloc storage(innerWidth); |
| 291 uint8_t* scanline = (uint8_t*) storage.get(); | 335 uint8_t* scanline = (uint8_t*) storage.get(); |
| 292 | 336 |
| 293 // GIF has an option to store the scanlines of an image, plus a larg
er background, | 337 // GIF has an option to store the scanlines of an image, plus a larg
er background, |
| 294 // filled by a fill color. In this case, we will use a subset of the
larger bitmap | 338 // filled by a fill color. In this case, we will use a subset of the
larger bitmap |
| 295 // for sampling. | 339 // for sampling. |
| 296 SkBitmap subset; | 340 SkBitmap subset; |
| 297 SkBitmap* workingBitmap; | 341 SkBitmap* workingBitmap; |
| 298 // are we only a subset of the total bounds? | 342 // are we only a subset of the total bounds? |
| 299 if ((desc.Top | desc.Left) > 0 || | 343 if ((imageTop | imageLeft) > 0 || |
| 300 innerWidth < width || innerHeight < height) { | 344 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. | 345 // Fill the background. |
| 313 memset(bm->getPixels(), fill, bm->getSize()); | 346 memset(bm->getPixels(), fillIndex, bm->getSize()); |
| 314 | 347 |
| 315 // Create a subset of the bitmap. | 348 // Create a subset of the bitmap. |
| 316 SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX()
, | 349 SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX()
, |
| 317 desc.Top / sampler.srcDY(), | 350 imageTop / sampler.srcDY(), |
| 318 innerWidth / sampler.srcDX(
), | 351 innerWidth / sampler.srcDX(
), |
| 319 innerHeight / sampler.srcDY
())); | 352 innerHeight / sampler.srcDY
())); |
| 320 if (!bm->extractSubset(&subset, subsetRect)) { | 353 if (!bm->extractSubset(&subset, subsetRect)) { |
| 321 return error_return(gif, *bm, "Extract failed."); | 354 return error_return(*bm, "Extract failed."); |
| 322 } | 355 } |
| 323 // Update the sampler. We'll now be only sampling into the subse
t. | 356 // Update the sampler. We'll now be only sampling into the subse
t. |
| 324 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->g
etSampleSize()); | 357 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->g
etSampleSize()); |
| 325 workingBitmap = ⊂ | 358 workingBitmap = ⊂ |
| 326 } else { | 359 } else { |
| 327 workingBitmap = bm; | 360 workingBitmap = bm; |
| 328 } | 361 } |
| 329 | 362 |
| 330 // bm is already locked, but if we had to take a subset, it must be
locked also, | 363 // 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. | 364 // so that getPixels() will point to its pixels. |
| 332 SkAutoLockPixels alpWorking(*workingBitmap); | 365 SkAutoLockPixels alpWorking(*workingBitmap); |
| 333 | 366 |
| 334 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *th
is)) { | 367 if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *th
is)) { |
| 335 return error_return(gif, *bm, "Sampler failed to begin."); | 368 return error_return(*bm, "Sampler failed to begin."); |
| 336 } | 369 } |
| 337 | 370 |
| 338 // now decode each scanline | 371 // now decode each scanline |
| 339 if (gif->Image.Interlace) { | 372 if (gif->Image.Interlace) { |
| 340 // Iterate over the height of the source data. The sampler will | 373 // Iterate over the height of the source data. The sampler will |
| 341 // take care of skipping unneeded rows. | 374 // take care of skipping unneeded rows. |
| 342 GifInterlaceIter iter(innerHeight); | 375 GifInterlaceIter iter(innerHeight); |
| 343 for (int y = 0; y < innerHeight; y++){ | 376 for (int y = 0; y < innerHeight; y++) { |
| 344 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 377 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
| 345 return error_return(gif, *bm, "interlace DGifGetLine"); | 378 gif_warning(*bm, "interlace DGifGetLine"); |
| 379 memset(scanline, fillIndex, innerWidth); |
| 380 for (; y < innerHeight; y++) { |
| 381 sampler.sampleInterlaced(scanline, iter.currY()); |
| 382 iter.next(); |
| 383 } |
| 384 return true; |
| 346 } | 385 } |
| 347 sampler.sampleInterlaced(scanline, iter.currY()); | 386 sampler.sampleInterlaced(scanline, iter.currY()); |
| 348 iter.next(); | 387 iter.next(); |
| 349 } | 388 } |
| 350 } else { | 389 } else { |
| 351 // easy, non-interlace case | 390 // easy, non-interlace case |
| 352 const int outHeight = workingBitmap->height(); | 391 const int outHeight = workingBitmap->height(); |
| 353 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); | 392 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); |
| 354 for (int y = 0; y < outHeight; y++) { | 393 for (int y = 0; y < outHeight; y++) { |
| 355 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { | 394 if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
| 356 return error_return(gif, *bm, "DGifGetLine"); | 395 gif_warning(*bm, "DGifGetLine"); |
| 396 memset(scanline, fillIndex, innerWidth); |
| 397 for (; y < outHeight; y++) { |
| 398 sampler.next(scanline); |
| 399 } |
| 400 return true; |
| 357 } | 401 } |
| 358 // scanline now contains the raw data. Sample it. | 402 // scanline now contains the raw data. Sample it. |
| 359 sampler.next(scanline); | 403 sampler.next(scanline); |
| 360 if (y < outHeight - 1) { | 404 if (y < outHeight - 1) { |
| 361 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY()
- 1); | 405 skip_src_rows(gif, scanline, innerWidth, sampler.srcDY()
- 1); |
| 362 } | 406 } |
| 363 } | 407 } |
| 364 // skip the rest of the rows (if any) | 408 // skip the rest of the rows (if any) |
| 365 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() +
1; | 409 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() +
1; |
| 366 SkASSERT(read <= innerHeight); | 410 SkASSERT(read <= innerHeight); |
| 367 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); | 411 skip_src_rows(gif, scanline, innerWidth, innerHeight - read); |
| 368 } | 412 } |
| 369 goto DONE; | 413 return true; |
| 370 } break; | 414 } break; |
| 371 | 415 |
| 372 case EXTENSION_RECORD_TYPE: | 416 case EXTENSION_RECORD_TYPE: |
| 373 #if GIFLIB_MAJOR < 5 | 417 #if GIFLIB_MAJOR < 5 |
| 374 if (DGifGetExtension(gif, &temp_save.Function, | 418 if (DGifGetExtension(gif, &temp_save.Function, |
| 375 &extData) == GIF_ERROR) { | 419 &extData) == GIF_ERROR) { |
| 376 #else | 420 #else |
| 377 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { | 421 if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { |
| 378 #endif | 422 #endif |
| 379 return error_return(gif, *bm, "DGifGetExtension"); | 423 return error_return(*bm, "DGifGetExtension"); |
| 380 } | 424 } |
| 381 | 425 |
| 382 while (extData != NULL) { | 426 while (extData != NULL) { |
| 383 /* Create an extension block with our data */ | 427 /* Create an extension block with our data */ |
| 384 #if GIFLIB_MAJOR < 5 | 428 #if GIFLIB_MAJOR < 5 |
| 385 if (AddExtensionBlock(&temp_save, extData[0], | 429 if (AddExtensionBlock(&temp_save, extData[0], |
| 386 &extData[1]) == GIF_ERROR) { | 430 &extData[1]) == GIF_ERROR) { |
| 387 #else | 431 #else |
| 388 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, | 432 if (GifAddExtensionBlock(&gif->ExtensionBlockCount, |
| 389 &gif->ExtensionBlocks, | 433 &gif->ExtensionBlocks, |
| 390 extFunction, | 434 extFunction, |
| 391 extData[0], | 435 extData[0], |
| 392 &extData[1]) == GIF_ERROR) { | 436 &extData[1]) == GIF_ERROR) { |
| 393 #endif | 437 #endif |
| 394 return error_return(gif, *bm, "AddExtensionBlock"); | 438 return error_return(*bm, "AddExtensionBlock"); |
| 395 } | 439 } |
| 396 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { | 440 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { |
| 397 return error_return(gif, *bm, "DGifGetExtensionNext"); | 441 return error_return(*bm, "DGifGetExtensionNext"); |
| 398 } | 442 } |
| 399 #if GIFLIB_MAJOR < 5 | 443 #if GIFLIB_MAJOR < 5 |
| 400 temp_save.Function = 0; | 444 temp_save.Function = 0; |
| 401 #endif | 445 #endif |
| 402 } | 446 } |
| 403 break; | 447 break; |
| 404 | 448 |
| 405 case TERMINATE_RECORD_TYPE: | 449 case TERMINATE_RECORD_TYPE: |
| 406 break; | 450 break; |
| 407 | 451 |
| 408 default: /* Should be trapped by DGifGetRecordType */ | 452 default: /* Should be trapped by DGifGetRecordType */ |
| 409 break; | 453 break; |
| 410 } | 454 } |
| 411 } while (recType != TERMINATE_RECORD_TYPE); | 455 } while (recType != TERMINATE_RECORD_TYPE); |
| 412 | 456 |
| 413 DONE: | |
| 414 return true; | 457 return true; |
| 415 } | 458 } |
| 416 | 459 |
| 417 /////////////////////////////////////////////////////////////////////////////// | 460 /////////////////////////////////////////////////////////////////////////////// |
| 418 DEFINE_DECODER_CREATOR(GIFImageDecoder); | 461 DEFINE_DECODER_CREATOR(GIFImageDecoder); |
| 419 /////////////////////////////////////////////////////////////////////////////// | 462 /////////////////////////////////////////////////////////////////////////////// |
| 420 | 463 |
| 421 static bool is_gif(SkStreamRewindable* stream) { | 464 static bool is_gif(SkStreamRewindable* stream) { |
| 422 char buf[GIF_STAMP_LEN]; | 465 char buf[GIF_STAMP_LEN]; |
| 423 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { | 466 if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 440 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); | 483 static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); |
| 441 | 484 |
| 442 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { | 485 static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { |
| 443 if (is_gif(stream)) { | 486 if (is_gif(stream)) { |
| 444 return SkImageDecoder::kGIF_Format; | 487 return SkImageDecoder::kGIF_Format; |
| 445 } | 488 } |
| 446 return SkImageDecoder::kUnknown_Format; | 489 return SkImageDecoder::kUnknown_Format; |
| 447 } | 490 } |
| 448 | 491 |
| 449 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); | 492 static SkImageDecoder_FormatReg gFormatReg(get_format_gif); |
| OLD | NEW |