Index: src/codec/SkCodec_libgif.cpp |
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp |
index 6134e96337a4dc3761e3cec6e42a45b168843e06..7b380473fc667a6f5ccf544a143dc047da4cf69b 100644 |
--- a/src/codec/SkCodec_libgif.cpp |
+++ b/src/codec/SkCodec_libgif.cpp |
@@ -185,13 +185,12 @@ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** |
SkASSERT(gif->ImageCount >= 1); |
if (nullptr != codecOut) { |
- // Get fields from header |
- const int32_t width = gif->SWidth; |
- const int32_t height = gif->SHeight; |
- if (width <= 0 || height <= 0) { |
- gif_error("Invalid dimensions.\n"); |
- return false; |
+ SkISize size; |
+ SkIRect frameRect; |
+ if (!GetDimensions(gif, &size, &frameRect)) { |
+ gif_error("Invalid gif size.\n"); |
} |
+ bool frameIsSubset = (size != frameRect.size()); |
// Determine the recommended alpha type. The transIndex might be valid if it less |
// than 256. We are not certain that the index is valid until we process the color |
@@ -208,9 +207,10 @@ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** |
// Return the codec |
// kIndex is the most natural color type for gifs, so we set this as |
// the default. |
- const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, |
- kIndex_8_SkColorType, alphaType); |
- *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex); |
+ SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType, |
+ alphaType); |
+ *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex, |
+ frameRect, frameIsSubset); |
} else { |
SkASSERT(nullptr != gifOut); |
streamDeleter.detach(); |
@@ -233,7 +233,7 @@ SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { |
} |
SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif, |
- uint32_t transIndex) |
+ uint32_t transIndex, const SkIRect& frameRect, bool frameIsSubset) |
: INHERITED(srcInfo, stream) |
, fGif(gif) |
, fSrcBuffer(new uint8_t[this->getInfo().width()]) |
@@ -244,8 +244,8 @@ SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType |
// Default fFillIndex is 0. We will overwrite this if fTransIndex is valid, or if |
// there is a valid background color. |
, fFillIndex(0) |
- , fFrameDims(SkIRect::MakeEmpty()) |
- , fFrameIsSubset(false) |
+ , fFrameRect(frameRect) |
+ , fFrameIsSubset(frameIsSubset) |
, fColorTable(NULL) |
, fSwizzler(NULL) |
{} |
@@ -283,6 +283,7 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans |
switch (recordType) { |
case IMAGE_DESC_RECORD_TYPE: { |
*transIndex = find_trans_index(saveExt); |
+ |
// FIXME: Gif files may have multiple images stored in a single |
// file. This is most commonly used to enable |
// animations. Since we are leaving animated gifs as a |
@@ -346,53 +347,31 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans |
return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput); |
} |
-/* |
- * A gif may contain many image frames, all of different sizes. |
- * This function checks if the frame dimensions are valid and corrects them if |
- * necessary. |
- */ |
-bool SkGifCodec::setFrameDimensions(const GifImageDesc& desc) { |
- // Fail on non-positive dimensions |
- int32_t frameLeft = desc.Left; |
- int32_t frameTop = desc.Top; |
- int32_t frameWidth = desc.Width; |
- int32_t frameHeight = desc.Height; |
- int32_t height = this->getInfo().height(); |
- int32_t width = this->getInfo().width(); |
- if (frameWidth <= 0 || frameHeight <= 0) { |
+bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) { |
+ // Get the encoded dimension values |
+ SavedImage* image = &gif->SavedImages[gif->ImageCount - 1]; |
+ const GifImageDesc& desc = image->ImageDesc; |
+ int frameLeft = desc.Left; |
+ int frameTop = desc.Top; |
+ int frameWidth = desc.Width; |
+ int frameHeight = desc.Height; |
+ int width = gif->SWidth; |
+ int height = gif->SHeight; |
+ |
+ // Ensure that the decode dimensions are large enough to contain the frame |
+ width = SkTMax(width, frameWidth + frameLeft); |
+ height = SkTMax(height, frameHeight + frameTop); |
+ |
+ // All of these dimensions should be positive, as they are encoded as unsigned 16-bit integers. |
+ // It is unclear why giflib casts them to ints. We will go ahead and check that they are |
+ // in fact positive. |
+ if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || width <= 0 || |
+ height <= 0) { |
return false; |
} |
- // Treat the following cases as warnings and try to fix |
- if (frameWidth > width) { |
- gif_warning("Image frame too wide, shrinking.\n"); |
- frameWidth = width; |
- frameLeft = 0; |
- } else if (frameLeft + frameWidth > width) { |
- gif_warning("Shifting image frame to left to fit.\n"); |
- frameLeft = width - frameWidth; |
- } else if (frameLeft < 0) { |
- gif_warning("Shifting image frame to right to fit\n"); |
- frameLeft = 0; |
- } |
- if (frameHeight > height) { |
- gif_warning("Image frame too tall, shrinking.\n"); |
- frameHeight = height; |
- frameTop = 0; |
- } else if (frameTop + frameHeight > height) { |
- gif_warning("Shifting image frame up to fit.\n"); |
- frameTop = height - frameHeight; |
- } else if (frameTop < 0) { |
- gif_warning("Shifting image frame down to fit\n"); |
- frameTop = 0; |
- } |
- fFrameDims.setXYWH(frameLeft, frameTop, frameWidth, frameHeight); |
- |
- // Indicate if the frame dimensions do not match the header dimensions |
- if (this->getInfo().dimensions() != fFrameDims.size()) { |
- fFrameIsSubset = true; |
- } |
- |
+ frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight); |
+ size->set(width, height); |
return true; |
} |
@@ -465,16 +444,6 @@ SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo |
kInvalidConversion); |
} |
- |
- // We have asserted that the image count is at least one in ReadHeader(). |
- SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; |
- const GifImageDesc& desc = image->ImageDesc; |
- |
- // Check that the frame dimensions are valid and set them |
- if(!this->setFrameDimensions(desc)) { |
- return gif_error("Invalid dimensions for image frame.\n", kInvalidInput); |
- } |
- |
// Initialize color table and copy to the client if necessary |
this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); |
return kSuccess; |
@@ -492,7 +461,7 @@ SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, |
} |
SkCodec::Result SkGifCodec::readRow() { |
- if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) { |
+ if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width())) { |
return kIncompleteInput; |
} |
return kSuccess; |
@@ -517,7 +486,7 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
// Initialize the swizzler |
if (fFrameIsSubset) { |
- const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFrameDims.height()); |
+ const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); |
if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) { |
return gif_error("Could not initialize swizzler.\n", kUnimplemented); |
} |
@@ -529,8 +498,8 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
// Modify the dst pointer |
const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType()); |
- dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() + |
- dstBytesPerPixel * fFrameDims.left()); |
+ dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameRect.top() + |
+ dstBytesPerPixel * fFrameRect.left()); |
} else { |
if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) { |
return gif_error("Could not initialize swizzler.\n", kUnimplemented); |
@@ -538,8 +507,8 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
} |
// Check the interlace flag and iterate over rows of the input |
- uint32_t width = fFrameDims.width(); |
- uint32_t height = fFrameDims.height(); |
+ uint32_t width = fFrameRect.width(); |
+ uint32_t height = fFrameRect.height(); |
if (fGif->Image.Interlace) { |
// In interlace mode, the rows of input are rearranged in |
// the output image. We a helper function to help us |
@@ -586,7 +555,7 @@ SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, |
// Initialize the swizzler |
if (fFrameIsSubset) { |
- const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFrameDims.height()); |
+ const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); |
if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) { |
return gif_error("Could not initialize swizzler.\n", kUnimplemented); |
} |
@@ -607,37 +576,32 @@ SkCodec::Result SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes |
colorPtr, this->options().fZeroInitialized); |
// Do nothing for rows before the image frame |
- // FIXME: nextScanline is not virtual, so using "INHERITED" does not change |
- // behavior. Was the intent to call this->INHERITED::onNextScanline()? Same |
- // for the next call down below. |
- int rowsBeforeFrame = fFrameDims.top() - this->INHERITED::nextScanline(); |
- if (rowsBeforeFrame > 0) { |
- count = SkTMin(0, count - rowsBeforeFrame); |
- dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); |
- } |
+ int rowsBeforeFrame = SkTMax(0, fFrameRect.top() - this->INHERITED::onNextScanline()); |
+ count = SkTMax(0, count - rowsBeforeFrame); |
+ dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); |
// Do nothing for rows after the image frame |
- int rowsAfterFrame = this->INHERITED::nextScanline() + count - fFrameDims.bottom(); |
- if (rowsAfterFrame > 0) { |
- count = SkTMin(0, count - rowsAfterFrame); |
- } |
+ int rowsAfterFrame = SkTMax(0, |
+ this->INHERITED::onNextScanline() + count - fFrameRect.bottom()); |
+ count = SkTMax(0, count - rowsAfterFrame); |
- // Adjust dst pointer for left offset |
- int bpp = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameDims.left(); |
- dst = SkTAddOffset<void>(dst, bpp); |
+ // Adjust dst pointer for left offset |
+ int offset = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameRect.left(); |
+ dst = SkTAddOffset<void>(dst, offset); |
} |
for (int i = 0; i < count; i++) { |
if (kSuccess != this->readRow()) { |
const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
- SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count - i, fFillIndex, colorPtr, |
- this->options().fZeroInitialized); |
- return gif_error("Could not decode line\n", SkCodec::kIncompleteInput); |
+ SkSwizzler::Fill(dst, this->dstInfo().makeWH(fFrameRect.width(), |
+ this->dstInfo().height()), rowBytes, count - i, fFillIndex, colorPtr, |
+ this->options().fZeroInitialized); |
+ return kIncompleteInput; |
} |
fSwizzler->swizzle(dst, fSrcBuffer.get()); |
dst = SkTAddOffset<void>(dst, rowBytes); |
} |
- return SkCodec::kSuccess; |
+ return kSuccess; |
} |
SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const { |
@@ -649,10 +613,13 @@ SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const { |
} |
int SkGifCodec::onNextScanline() const { |
+ int nextScanline = this->INHERITED::onNextScanline(); |
if (fGif->Image.Interlace) { |
- return get_output_row_interlaced(this->INHERITED::onNextScanline(), this->dstInfo().height()); |
- } else { |
- return this->INHERITED::onNextScanline(); |
+ if (nextScanline < fFrameRect.top() || nextScanline >= fFrameRect.bottom()) { |
+ return nextScanline; |
+ } |
+ return get_output_row_interlaced(nextScanline - fFrameRect.top(), fFrameRect.height()); |
} |
+ return nextScanline; |
} |