Index: src/images/SkImageDecoder_libgif.cpp |
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp |
index 1d25120da9c7585abbd002a16d724932d57d1595..08f37efced9fc8bdee36b286dc52ad44f06ac028 100644 |
--- a/src/images/SkImageDecoder_libgif.cpp |
+++ b/src/images/SkImageDecoder_libgif.cpp |
@@ -1,4 +1,3 @@ |
- |
/* |
* Copyright 2006 The Android Open Source Project |
* |
@@ -7,12 +6,13 @@ |
*/ |
-#include "SkImageDecoder.h" |
#include "SkColor.h" |
#include "SkColorPriv.h" |
+#include "SkColorTable.h" |
+#include "SkImageDecoder.h" |
+#include "SkScaledBitmapSampler.h" |
#include "SkStream.h" |
#include "SkTemplates.h" |
-#include "SkPackBits.h" |
#include "gif_lib.h" |
@@ -154,6 +154,23 @@ static bool error_return(GifFileType* gif, const SkBitmap& bm, |
return false; |
} |
+/** |
+ * Skip rows in the source gif image. |
+ * @param gif Source image. |
+ * @param dst Scratch output needed by gif library call. Must be >= width bytes. |
+ * @param width Bytes per row in the source image. |
+ * @param rowsToSkip Number of rows to skip. |
+ * @return True on success, false on GIF_ERROR. |
+ */ |
+static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) { |
+ for (int i = 0; i < rowsToSkip; i++) { |
+ if (DGifGetLine(gif, dst, width) == GIF_ERROR) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
#if GIFLIB_MAJOR < 5 |
GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); |
@@ -196,13 +213,20 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
width = gif->SWidth; |
height = gif->SHeight; |
- if (width <= 0 || height <= 0 || |
- !this->chooseFromOneChoice(SkBitmap::kIndex8_Config, |
- width, height)) { |
+ if (width <= 0 || height <= 0) { |
+ return error_return(gif, *bm, "invalid dimensions"); |
+ } |
+ |
+ // FIXME: We could give the caller a choice of images or configs. |
+ if (!this->chooseFromOneChoice(SkBitmap::kIndex8_Config, width, height)) { |
return error_return(gif, *bm, "chooseFromOneChoice"); |
} |
- bm->setConfig(SkBitmap::kIndex8_Config, width, height); |
+ SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); |
+ |
+ bm->setConfig(SkBitmap::kIndex8_Config, sampler.scaledWidth(), |
+ sampler.scaledHeight()); |
+ |
if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
return true; |
} |
@@ -226,33 +250,28 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
} |
colorCount = cmap->ColorCount; |
- SkColorTable* ctable = SkNEW_ARGS(SkColorTable, (colorCount)); |
- SkPMColor* colorPtr = ctable->lockColors(); |
- for (int index = 0; index < colorCount; index++) |
+ SkAutoTMalloc<SkPMColor> colorStorage(colorCount); |
+ SkPMColor* colorPtr = colorStorage.get(); |
+ for (int index = 0; index < colorCount; index++) { |
colorPtr[index] = SkPackARGB32(0xFF, |
cmap->Colors[index].Red, |
cmap->Colors[index].Green, |
cmap->Colors[index].Blue); |
+ } |
transpIndex = find_transpIndex(temp_save, colorCount); |
- if (transpIndex < 0) |
- ctable->setFlags(ctable->getFlags() | SkColorTable::kColorsAreOpaque_Flag); |
- else |
- colorPtr[transpIndex] = 0; // ram in a transparent SkPMColor |
- ctable->unlockColors(true); |
+ bool reallyHasAlpha = transpIndex >= 0; |
+ if (reallyHasAlpha) { |
+ colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor |
+ } |
- SkAutoUnref aurts(ctable); |
+ SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable, (colorPtr, colorCount))); |
+ ctable->setIsOpaque(!reallyHasAlpha); |
if (!this->allocPixelRef(bm, ctable)) { |
return error_return(gif, *bm, "allocPixelRef"); |
} |
} |
- SkAutoLockPixels alp(*bm); |
- |
- // time to decode the scanlines |
- // |
- uint8_t* scanline = bm->getAddr8(0, 0); |
- const int rowBytes = bm->rowBytes(); |
const int innerWidth = desc.Width; |
const int innerHeight = desc.Height; |
@@ -261,10 +280,19 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
return error_return(gif, *bm, "non-pos inner width/height"); |
} |
+ SkAutoLockPixels alp(*bm); |
+ |
+ SkAutoMalloc storage(innerWidth); |
+ uint8_t* scanline = (uint8_t*) storage.get(); |
+ |
+ // GIF has an option to store the scanlines of an image, plus a larger background, |
+ // filled by a fill color. In this case, we will use a subset of the larger bitmap |
+ // for sampling. |
+ SkBitmap subset; |
+ SkBitmap* workingBitmap; |
// are we only a subset of the total bounds? |
if ((desc.Top | desc.Left) > 0 || |
- innerWidth < width || innerHeight < height) |
- { |
+ innerWidth < width || innerHeight < height) { |
int fill; |
if (transpIndex >= 0) { |
fill = transpIndex; |
@@ -276,33 +304,62 @@ bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { |
static_cast<unsigned>(colorCount)) { |
fill = 0; |
} |
- memset(scanline, fill, bm->getSize()); |
- // bump our starting address |
- scanline += desc.Top * rowBytes + desc.Left; |
+ // Fill the background. |
+ memset(bm->getPixels(), fill, bm->getSize()); |
+ |
+ // Create a subset of the bitmap. |
+ SkIRect subsetRect(SkIRect::MakeXYWH(desc.Left / sampler.srcDX(), |
+ desc.Top / sampler.srcDY(), |
+ innerWidth / sampler.srcDX(), |
+ innerHeight / sampler.srcDY())); |
+ if (!bm->extractSubset(&subset, subsetRect)) { |
+ return error_return(gif, *bm, "Extract failed."); |
+ } |
+ // Update the sampler. We'll now be only sampling into the subset. |
+ sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize()); |
+ workingBitmap = ⊂ |
+ } else { |
+ workingBitmap = bm; |
+ } |
+ |
+ // bm is already locked, but if we had to take a subset, it must be locked also, |
+ // so that getPixels() will point to its pixels. |
+ SkAutoLockPixels alpWorking(*workingBitmap); |
+ |
+ if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) { |
+ return error_return(gif, *bm, "Sampler failed to begin."); |
} |
// now decode each scanline |
- if (gif->Image.Interlace) |
- { |
+ if (gif->Image.Interlace) { |
+ // Iterate over the height of the source data. The sampler will |
+ // take care of skipping unneeded rows. |
GifInterlaceIter iter(innerHeight); |
- for (int y = 0; y < innerHeight; y++) |
- { |
- uint8_t* row = scanline + iter.currY() * rowBytes; |
- if (DGifGetLine(gif, row, innerWidth) == GIF_ERROR) { |
+ for (int y = 0; y < innerHeight; y++){ |
+ if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
return error_return(gif, *bm, "interlace DGifGetLine"); |
} |
+ sampler.sampleInterlaced(scanline, iter.currY()); |
iter.next(); |
} |
- } |
- else |
- { |
+ } else { |
// easy, non-interlace case |
- for (int y = 0; y < innerHeight; y++) { |
+ const int outHeight = workingBitmap->height(); |
+ skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); |
+ for (int y = 0; y < outHeight; y++) { |
if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { |
return error_return(gif, *bm, "DGifGetLine"); |
} |
- scanline += rowBytes; |
+ // scanline now contains the raw data. Sample it. |
+ sampler.next(scanline); |
+ if (y < outHeight - 1) { |
+ skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1); |
+ } |
} |
+ // skip the rest of the rows (if any) |
+ int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1; |
+ SkASSERT(read <= innerHeight); |
+ skip_src_rows(gif, scanline, innerWidth, innerHeight - read); |
} |
goto DONE; |
} break; |