From: jcgregorio Date: Sat, 5 Sep 2015 17:16:28 +0000 (-0700) Subject: Revert of Scanline decoding for gifs (patchset #15 id:380001 of https://codereview... X-Git-Tag: submit/tizen/20180928.044319~972 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e3330cf2cf361c146af7009301612b4010f82195;p=platform%2Fupstream%2FlibSkiaSharp.git Revert of Scanline decoding for gifs (patchset #15 id:380001 of https://codereview.chromium.org/1305123002/ ) Reason for revert: Breaks the build with SkScanlineDecoder.h not found: http://build.chromium.org/p/client.skia/builders/Linux%20Builder/builds/3722/steps/compile/logs/stdio ../../third_party/skia/src/utils/SkBitmapRegionCanvas.h:10:10: fatal error: 'SkScanlineDecoder.h' file not found #include "SkScanlineDecoder.h" Original issue's description: > Scanline decoding for gifs > > BUG=skia: > > Committed: https://skia.googlesource.com/skia/+/e9c10b9121887e8c300bd41357461418e061984d TBR=scroggo@google.com,djsollen@google.com,msarett@google.com NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=skia: Review URL: https://codereview.chromium.org/1320273004 --- diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 981e47d6c0..e31a249f70 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -352,28 +352,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const { return Error::Nonfatal("Could not start scanline decoder"); } - SkCodec::Result result = SkCodec::kUnimplemented; - switch (scanlineDecoder->getScanlineOrder()) { - case SkScanlineDecoder::kTopDown_SkScanlineOrder: - case SkScanlineDecoder::kBottomUp_SkScanlineOrder: - case SkScanlineDecoder::kNone_SkScanlineOrder: - result = scanlineDecoder->getScanlines(bitmap.getAddr(0, 0), - decodeInfo.height(), bitmap.rowBytes()); - break; - case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: { - for (int y = 0; y < decodeInfo.height(); y++) { - int dstY = scanlineDecoder->getY(); - void* dstPtr = bitmap.getAddr(0, dstY); - result = scanlineDecoder->getScanlines(dstPtr, 1, bitmap.rowBytes()); - if (SkCodec::kSuccess != result && SkCodec::kIncompleteInput != result) { - return SkStringPrintf("%s failed with error message %d", - fPath.c_str(), (int) result); - } - } - break; - } - } - + const SkCodec::Result result = scanlineDecoder->getScanlines( + bitmap.getAddr(0, 0), decodeInfo.height(), bitmap.rowBytes()); switch (result) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h index 2769cec1cd..726074195d 100644 --- a/src/codec/SkCodecPriv.h +++ b/src/codec/SkCodecPriv.h @@ -30,17 +30,6 @@ #define COMPUTE_RESULT_ALPHA \ SkSwizzler::GetResult(zeroAlpha, maxAlpha); -/* - * returns a scaled dimension based on the original dimension and the sampleSize - * NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder - */ -static int get_scaled_dimension(int srcDimension, int sampleSize) { - if (sampleSize > srcDimension) { - return 1; - } - return srcDimension / sampleSize; -} - /* * Returns the first coordinate that we will keep during a scaled decode. * The output can be interpreted as an x-coordinate or a y-coordinate. @@ -148,7 +137,7 @@ static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* co SkASSERT(nullptr != inputColorPtr); SkASSERT(nullptr != inputColorCount); SkASSERT(nullptr != colorTable); - memcpy(inputColorPtr, colorTable->readColors(), *inputColorCount * sizeof(SkPMColor)); + memcpy(inputColorPtr, colorTable->readColors(), *inputColorCount * 4); } } diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp index 03980b586a..5f1bec0dde 100644 --- a/src/codec/SkCodec_libgif.cpp +++ b/src/codec/SkCodec_libgif.cpp @@ -9,7 +9,6 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkColorTable.h" -#include "SkScaledCodec.h" #include "SkStream.h" #include "SkSwizzler.h" #include "SkUtils.h" @@ -61,6 +60,24 @@ static GifFileType* open_gif(SkStream* stream) { return DGifOpen(stream, read_bytes_callback, nullptr); } + /* + * This function cleans up the gif object after the decode completes + * It is used in a SkAutoTCallIProc template + */ +void SkGifCodec::CloseGif(GifFileType* gif) { + DGifCloseFile(gif, nullptr); +} + +/* + * This function free extension data that has been saved to assist the image + * decoder + */ +void SkGifCodec::FreeExtension(SavedImage* image) { + if (nullptr != image->ExtensionBlocks) { + GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); + } +} + /* * Check if a there is an index of the color table for a transparent pixel */ @@ -78,10 +95,12 @@ static uint32_t find_trans_index(const SavedImage& image) { // is the transparent index (if it exists), so we need at least four // bytes. if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function && extBlock.ByteCount >= 4) { + // Check the transparent color flag which indicates whether a // transparent index exists. It is the least significant bit of // the first byte of the extension block. if (1 == (extBlock.Bytes[0] & 1)) { + // Use uint32_t to prevent sign extending return extBlock.Bytes[3]; } @@ -121,24 +140,6 @@ static uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) return 1 + 2 * (encodedRow - ceil_div(height, 2)); } -/* - * This function cleans up the gif object after the decode completes - * It is used in a SkAutoTCallIProc template - */ -void SkGifCodec::CloseGif(GifFileType* gif) { - DGifCloseFile(gif, NULL); -} - -/* - * This function free extension data that has been saved to assist the image - * decoder - */ -void SkGifCodec::FreeExtension(SavedImage* image) { - if (NULL != image->ExtensionBlocks) { - GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); - } -} - /* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. @@ -169,22 +170,6 @@ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** return false; } - // Read through gif extensions to get to the image data. Set the - // transparent index based on the extension data. - uint32_t transIndex; - SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); - if (kSuccess != result){ - return false; - } - - // Read the image descriptor - if (GIF_ERROR == DGifGetImageDesc(gif)) { - return false; - } - // If reading the image descriptor is successful, the image count will be - // incremented. - SkASSERT(gif->ImageCount >= 1); - if (nullptr != codecOut) { // Get fields from header const int32_t width = gif->SWidth; @@ -194,24 +179,20 @@ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** return false; } - // 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 - // table, since some gifs have color tables with less than 256 colors. If - // there might be a valid transparent index, we must indicate that the image has - // alpha. - // In the case where we must support alpha, we have the option to set the - // suggested alpha type to kPremul or kUnpremul. Both are valid since the alpha - // component will always be 0xFF or the entire 32-bit pixel will be set to zero. - // We prefer kPremul because we support kPremul, and it is more efficient to use - // kPremul directly even when kUnpremul is supported. - SkAlphaType alphaType = (transIndex < 256) ? kPremul_SkAlphaType : kOpaque_SkAlphaType; - // Return the codec // kIndex is the most natural color type for gifs, so we set this as // the default. + // Many gifs specify a color table index for transparent pixels. Every + // other pixel is guaranteed to be opaque. Despite this, because of the + // possiblity of transparent pixels, we cannot assume that the image is + // opaque. We have the option to set the alpha type as kPremul or + // kUnpremul. Both are valid since the alpha component will always be + // 0xFF or the entire 32-bit pixel will be set to zero. We prefer + // kPremul because we support kPremul, and it is more efficient to + // use kPremul directly even when kUnpremul is supported. const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, - kIndex_8_SkColorType, alphaType); - *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex); + kIndex_8_SkColorType, kPremul_SkAlphaType); + *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach()); } else { SkASSERT(nullptr != gifOut); streamDeleter.detach(); @@ -233,22 +214,9 @@ SkCodec* SkGifCodec::NewFromStream(SkStream* stream) { return nullptr; } -SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif, - uint32_t transIndex) +SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif) : INHERITED(srcInfo, stream) , fGif(gif) - , fSrcBuffer(new uint8_t[this->getInfo().width()]) - // If it is valid, fTransIndex will be used to set fFillIndex. We don't know if - // fTransIndex is valid until we process the color table, since fTransIndex may - // be greater than the size of the color table. - , fTransIndex(transIndex) - // 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) - , fColorTable(NULL) - , fSwizzler(NULL) {} bool SkGifCodec::onRewind() { @@ -262,7 +230,31 @@ bool SkGifCodec::onRewind() { return true; } -SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) { +/* + * Initiates the gif decode + */ +SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, + void* dst, size_t dstRowBytes, + const Options& opts, + SkPMColor* inputColorPtr, + int* inputColorCount) { + // Rewind if necessary + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; + } + + // Check for valid input parameters + if (opts.fSubset) { + // Subsets are not supported. + return kUnimplemented; + } + if (dstInfo.dimensions() != this->getInfo().dimensions()) { + return gif_error("Scaling not supported.\n", kInvalidScale); + } + if (!conversion_possible(dstInfo, this->getInfo())) { + return gif_error("Cannot convert input type to output type.\n", kInvalidConversion); + } + // Use this as a container to hold information about any gif extension // blocks. This generally stores transparency and animation instructions. SavedImage saveExt; @@ -275,15 +267,196 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans // We will loop over components of gif images until we find an image. Once // we find an image, we will decode and return it. While many gif files // contain more than one image, we will simply decode the first image. + const int32_t width = dstInfo.width(); + const int32_t height = dstInfo.height(); GifRecordType recordType; do { // Get the current record type - if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) { + if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) { return gif_error("DGifGetRecordType failed.\n", kInvalidInput); } + switch (recordType) { case IMAGE_DESC_RECORD_TYPE: { - *transIndex = find_trans_index(saveExt); + // Read the image descriptor + if (GIF_ERROR == DGifGetImageDesc(fGif)) { + return gif_error("DGifGetImageDesc failed.\n", kInvalidInput); + } + + // If reading the image descriptor is successful, the image + // count will be incremented + SkASSERT(fGif->ImageCount >= 1); + SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1]; + + // Process the descriptor + const GifImageDesc& desc = image->ImageDesc; + int32_t imageLeft = desc.Left; + int32_t imageTop = desc.Top; + int32_t innerWidth = desc.Width; + int32_t innerHeight = desc.Height; + // Fail on non-positive dimensions + if (innerWidth <= 0 || innerHeight <= 0) { + return gif_error("Invalid dimensions for inner image.\n", kInvalidInput); + } + // Treat the following cases as warnings and try to fix + if (innerWidth > width) { + gif_warning("Inner image too wide, shrinking.\n"); + innerWidth = width; + imageLeft = 0; + } else if (imageLeft + innerWidth > width) { + gif_warning("Shifting inner image to left to fit.\n"); + imageLeft = width - innerWidth; + } else if (imageLeft < 0) { + gif_warning("Shifting image to right to fit\n"); + imageLeft = 0; + } + if (innerHeight > height) { + gif_warning("Inner image too tall, shrinking.\n"); + innerHeight = height; + imageTop = 0; + } else if (imageTop + innerHeight > height) { + gif_warning("Shifting inner image up to fit.\n"); + imageTop = height - innerHeight; + } else if (imageTop < 0) { + gif_warning("Shifting image down to fit\n"); + imageTop = 0; + } + + // Create a color table to store colors the giflib colorMap + SkPMColor alternateColorPtr[256]; + SkPMColor* colorTable; + SkColorType dstColorType = dstInfo.colorType(); + if (kIndex_8_SkColorType == dstColorType) { + SkASSERT(nullptr != inputColorPtr); + SkASSERT(nullptr != inputColorCount); + colorTable = inputColorPtr; + } else { + colorTable = alternateColorPtr; + } + + // Set up the color table + uint32_t colorCount = 0; + // Allocate maximum storage to deal with invalid indices safely + const uint32_t maxColors = 256; + ColorMapObject* colorMap = fGif->Image.ColorMap; + // If there is no local color table, use the global color table + if (nullptr == colorMap) { + colorMap = fGif->SColorMap; + } + if (nullptr != colorMap) { + colorCount = colorMap->ColorCount; + SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel))); + SkASSERT(colorCount <= 256); + for (uint32_t i = 0; i < colorCount; i++) { + colorTable[i] = SkPackARGB32(0xFF, + colorMap->Colors[i].Red, + colorMap->Colors[i].Green, + colorMap->Colors[i].Blue); + } + } + + // This is used to fill unspecified pixels in the image data. + uint32_t fillIndex = fGif->SBackGroundColor; + ZeroInitialized zeroInit = opts.fZeroInitialized; + + // Gifs have the option to specify the color at a single + // index of the color table as transparent. + { + // Get the transparent index. If the return value of this + // function is greater than the colorCount, we know that + // there is no valid transparent color in the color table. + // This occurs if there is no graphics control extension or + // if the index specified by the graphics control extension + // is out of range. + uint32_t transIndex = find_trans_index(saveExt); + + if (transIndex < colorCount) { + colorTable[transIndex] = SK_ColorTRANSPARENT; + // If there is a transparent index, we also use this as + // the fill index. + fillIndex = transIndex; + } else if (fillIndex >= colorCount) { + // If the fill index is invalid, we default to 0. This + // behavior is unspecified but matches SkImageDecoder. + fillIndex = 0; + } + } + + // Fill in the color table for indices greater than color count. + // This allows for predictable, safe behavior. + for (uint32_t i = colorCount; i < maxColors; i++) { + colorTable[i] = colorTable[fillIndex]; + } + + // Check if image is only a subset of the image frame + SkAutoTDelete swizzler(nullptr); + if (innerWidth < width || innerHeight < height) { + + // Modify the destination info + const SkImageInfo subsetDstInfo = dstInfo.makeWH(innerWidth, innerHeight); + + // Fill the destination with the fill color + // FIXME: This may not be the behavior that we want for + // animated gifs where we draw on top of the + // previous frame. + SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, fillIndex, colorTable, + zeroInit); + + // Modify the dst pointer + const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstColorType); + dst = SkTAddOffset(dst, + dstRowBytes * imageTop + + dstBytesPerPixel * imageLeft); + + // Create the subset swizzler + swizzler.reset(SkSwizzler::CreateSwizzler( + SkSwizzler::kIndex, colorTable, subsetDstInfo, + zeroInit, this->getInfo())); + } else { + // Create the fully dimensional swizzler + swizzler.reset(SkSwizzler::CreateSwizzler( + SkSwizzler::kIndex, colorTable, dstInfo, + zeroInit, this->getInfo())); + } + + // Stores output from dgiflib and input to the swizzler + SkAutoTDeleteArray buffer(new uint8_t[innerWidth]); + + // Check the interlace flag and iterate over rows of the input + if (fGif->Image.Interlace) { + for (int32_t y = 0; y < innerHeight; y++) { + if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWidth)) { + // Recover from error by filling remainder of image + memset(buffer.get(), fillIndex, innerWidth); + for (; y < innerHeight; y++) { + void* dstRow = SkTAddOffset(dst, dstRowBytes * + get_output_row_interlaced(y, innerHeight)); + swizzler->swizzle(dstRow, buffer.get()); + } + return gif_error(SkStringPrintf( + "Could not decode line %d of %d.\n", + y, height - 1).c_str(), kIncompleteInput); + } + void* dstRow = SkTAddOffset(dst, + dstRowBytes * get_output_row_interlaced(y, innerHeight)); + swizzler->swizzle(dstRow, buffer.get()); + } + } else { + // Standard mode + void* dstRow = dst; + for (int32_t y = 0; y < innerHeight; y++) { + if (GIF_ERROR == DGifGetLine(fGif, buffer.get(), innerWidth)) { + SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, innerHeight - y, + fillIndex, colorTable, zeroInit); + return gif_error(SkStringPrintf( + "Could not decode line %d of %d.\n", + y, height - 1).c_str(), kIncompleteInput); + } + swizzler->swizzle(dstRow, buffer.get()); + dstRow = SkTAddOffset(dstRow, dstRowBytes); + } + } + // 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 @@ -307,11 +480,12 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans // test case that expects this behavior. return kSuccess; } + // Extensions are used to specify special properties of the image // such as transparency or animation. case EXTENSION_RECORD_TYPE: // Read extension data - if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) { + if (GIF_ERROR == DGifGetExtension(fGif, &extFunction, &extData)) { return gif_error("Could not get extension.\n", kIncompleteInput); } @@ -325,7 +499,7 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans return gif_error("Could not add extension block.\n", kIncompleteInput); } // Move to the next block - if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) { + if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) { return gif_error("Could not get next extension.\n", kIncompleteInput); } } @@ -336,9 +510,8 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans break; default: - // DGifGetRecordType returns an error if the record type does - // not match one of the above cases. This should not be - // reached. + // giflib returns an error code if the record type is not known. + // We should catch this error immediately. SkASSERT(false); break; } @@ -346,360 +519,3 @@ 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) { - 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; - } - - return true; -} - -void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, - int* inputColorCount) { - // Set up our own color table - const uint32_t maxColors = 256; - SkPMColor colorPtr[256]; - if (NULL != inputColorCount) { - // We set the number of colors to maxColors in order to ensure - // safe memory accesses. Otherwise, an invalid pixel could - // access memory outside of our color table array. - *inputColorCount = maxColors; - } - - // Get local color table - ColorMapObject* colorMap = fGif->Image.ColorMap; - // If there is no local color table, use the global color table - if (NULL == colorMap) { - colorMap = fGif->SColorMap; - } - - uint32_t colorCount = 0; - if (NULL != colorMap) { - colorCount = colorMap->ColorCount; - // giflib guarantees these properties - SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel))); - SkASSERT(colorCount <= 256); - for (uint32_t i = 0; i < colorCount; i++) { - colorPtr[i] = SkPackARGB32(0xFF, colorMap->Colors[i].Red, - colorMap->Colors[i].Green, colorMap->Colors[i].Blue); - } - } - - // Gifs have the option to specify the color at a single index of the color - // table as transparent. If the transparent index is greater than the - // colorCount, we know that there is no valid transparent color in the color - // table. If there is not valid transparent index, we will try to use the - // backgroundIndex as the fill index. If the backgroundIndex is also not - // valid, we will let fFillIndex default to 0 (it is set to zero in the - // constructor). This behavior is not specified but matches - // SkImageDecoder_libgif. - uint32_t backgroundIndex = fGif->SBackGroundColor; - if (fTransIndex < colorCount) { - colorPtr[fTransIndex] = SK_ColorTRANSPARENT; - fFillIndex = fTransIndex; - } else if (backgroundIndex < colorCount) { - fFillIndex = backgroundIndex; - } - - // Fill in the color table for indices greater than color count. - // This allows for predictable, safe behavior. - for (uint32_t i = colorCount; i < maxColors; i++) { - colorPtr[i] = colorPtr[fFillIndex]; - } - - fColorTable.reset(new SkColorTable(colorPtr, maxColors)); - copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); -} - -SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, - int* inputColorCount, const Options& opts) { - // Rewind if necessary - if (!this->rewindIfNeeded()) { - return kCouldNotRewind; - } - - // Check for valid input parameters - if (opts.fSubset) { - // Subsets are not supported. - return kUnimplemented; - } - if (!conversion_possible(dstInfo, this->getInfo())) { - return gif_error("Cannot convert input type to output type.\n", - 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; -} - -SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, - ZeroInitialized zeroInit) { - const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); - fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, - colorPtr, dstInfo, zeroInit, this->getInfo())); - if (nullptr != fSwizzler.get()) { - return kSuccess; - } - return kUnimplemented; -} - -SkCodec::Result SkGifCodec::readRow() { - if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) { - return kIncompleteInput; - } - return kSuccess; -} - -/* - * Initiates the gif decode - */ -SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, - void* dst, size_t dstRowBytes, - const Options& opts, - SkPMColor* inputColorPtr, - int* inputColorCount) { - Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts); - if (kSuccess != result) { - return result; - } - - if (dstInfo.dimensions() != this->getInfo().dimensions()) { - return gif_error("Scaling not supported.\n", kInvalidScale); - } - - // Initialize the swizzler - if (fFrameIsSubset) { - const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFrameDims.height()); - if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } - - // Fill the background - const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); - SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(), - fFillIndex, colorPtr, opts.fZeroInitialized); - - // Modify the dst pointer - const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType()); - dst = SkTAddOffset(dst, dstRowBytes * fFrameDims.top() + - dstBytesPerPixel * fFrameDims.left()); - } else { - if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } - } - - // Check the interlace flag and iterate over rows of the input - uint32_t width = fFrameDims.width(); - uint32_t height = fFrameDims.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 - // rearrange the decoded rows. - for (uint32_t y = 0; y < height; y++) { - if (kSuccess != this->readRow()) { - // Recover from error by filling remainder of image - memset(fSrcBuffer.get(), fFillIndex, width); - for (; y < height; y++) { - void* dstRow = SkTAddOffset(dst, - dstRowBytes * get_output_row_interlaced(y, height)); - fSwizzler->swizzle(dstRow, fSrcBuffer.get()); - } - return gif_error("Could not decode line.\n", kIncompleteInput); - } - void* dstRow = SkTAddOffset(dst, - dstRowBytes * get_output_row_interlaced(y, height)); - fSwizzler->swizzle(dstRow, fSrcBuffer.get()); - } - } else { - // Standard mode - void* dstRow = dst; - for (uint32_t y = 0; y < height; y++) { - if (kSuccess != this->readRow()) { - const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); - SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, - height - y, fFillIndex, colorPtr, opts.fZeroInitialized); - return gif_error("Could not decode line\n", kIncompleteInput); - } - fSwizzler->swizzle(dstRow, fSrcBuffer.get()); - dstRow = SkTAddOffset(dstRow, dstRowBytes); - } - } - return kSuccess; -} - -// TODO (msarett): skbug.com/3582 -// Should we implement reallyHasAlpha? Or should we read extension blocks in the -// header? Or should we do both? - -class SkGifScanlineDecoder : public SkScanlineDecoder { -public: - SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec) - : INHERITED(srcInfo) - , fCodec(codec) - {} - - SkEncodedFormat onGetEncodedFormat() const override { - return kGIF_SkEncodedFormat; - } - - SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts, - SkPMColor inputColorPtr[], int* inputColorCount) override { - SkCodec::Result result = fCodec->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, - this->options()); - if (SkCodec::kSuccess != result) { - return result; - } - - // Check to see if scaling was requested. - if (dstInfo.dimensions() != this->getInfo().dimensions()) { - if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return gif_error("Scaling not supported.\n", SkCodec::kInvalidScale); - } - } - - // Initialize the swizzler - if (fCodec->fFrameIsSubset) { - int sampleX; - SkScaledCodec::ComputeSampleSize(dstInfo, fCodec->getInfo(), &sampleX, NULL); - const SkImageInfo subsetDstInfo = dstInfo.makeWH( - get_scaled_dimension(fCodec->fFrameDims.width(), sampleX), - fCodec->fFrameDims.height()); - if (SkCodec::kSuccess != fCodec->initializeSwizzler(subsetDstInfo, - opts.fZeroInitialized)) { - return gif_error("Could not initialize swizzler.\n", SkCodec::kUnimplemented); - } - } else { - if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZeroInitialized)) { - return gif_error("Could not initialize swizzler.\n", SkCodec::kUnimplemented); - } - } - - return SkCodec::kSuccess; - } - - SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { - if (fCodec->fFrameIsSubset) { - // Fill the requested rows - const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()); - SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFillIndex, - colorPtr, this->options().fZeroInitialized); - - // Do nothing for rows before the image frame - int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY(); - if (rowsBeforeFrame > 0) { - count = SkTMin(0, count - rowsBeforeFrame); - dst = SkTAddOffset(dst, rowBytes * rowsBeforeFrame); - } - - // Do nothing for rows after the image frame - int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims.bottom(); - if (rowsAfterFrame > 0) { - count = SkTMin(0, count - rowsAfterFrame); - } - - // Adjust dst pointer for left offset - dst = SkTAddOffset(dst, SkColorTypeBytesPerPixel( - this->dstInfo().colorType()) * fCodec->fFrameDims.left()); - } - - for (int i = 0; i < count; i++) { - if (SkCodec::kSuccess != fCodec->readRow()) { - const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()); - SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, - count - i, fCodec->fFillIndex, colorPtr, - this->options().fZeroInitialized); - return gif_error("Could not decode line\n", SkCodec::kIncompleteInput); - } - fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get()); - dst = SkTAddOffset(dst, rowBytes); - } - return SkCodec::kSuccess; - } - - SkScanlineOrder onGetScanlineOrder() const override { - if (fCodec->fGif->Image.Interlace) { - return kOutOfOrder_SkScanlineOrder; - } else { - return kTopDown_SkScanlineOrder; - } - } - - int onGetY() const override { - if (fCodec->fGif->Image.Interlace) { - return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo().height()); - } else { - return INHERITED::onGetY(); - } - } - -private: - SkAutoTDelete fCodec; - - typedef SkScanlineDecoder INHERITED; -}; - -SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete codec (static_cast(SkGifCodec::NewFromStream(stream))); - if (!codec) { - return NULL; - } - - const SkImageInfo& srcInfo = codec->getInfo(); - - return SkNEW_ARGS(SkGifScanlineDecoder, (srcInfo, codec.detach())); -} diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h index 8845536851..df01088951 100644 --- a/src/codec/SkCodec_libgif.h +++ b/src/codec/SkCodec_libgif.h @@ -6,10 +6,7 @@ */ #include "SkCodec.h" -#include "SkColorTable.h" #include "SkImageInfo.h" -#include "SkScanlineDecoder.h" -#include "SkSwizzler.h" #include "gif_lib.h" @@ -32,8 +29,7 @@ public: * Reads enough of the stream to determine the image format */ static SkCodec* NewFromStream(SkStream*); - - static SkScanlineDecoder* NewSDFromStream(SkStream* stream); + protected: @@ -56,11 +52,10 @@ protected: * Ownership is unchanged when we returned a gifOut. * */ - static bool ReadHeader(SkStream* stream, SkCodec** codecOut, - GifFileType** gifOut); + static bool ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut); /* - * Performs the full gif decode + * Initiates the gif decode */ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int32_t*) override; @@ -73,67 +68,6 @@ protected: private: - /* - * A gif can contain multiple image frames. We will only decode the first - * frame. This function reads up to the first image frame, processing - * transparency and/or animation information that comes before the image - * data. - * - * @param gif Pointer to the library type that manages the gif decode - * @param transIndex This call will set the transparent index based on the - * extension data. - */ - static SkCodec::Result ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex); - - /* - * 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. It then sets fFrameDims to the corrected - * dimensions. - * - * @param desc The image frame descriptor - */ - bool setFrameDimensions(const GifImageDesc& desc); - - /* - * Initializes the color table that we will use for decoding. - * - * @param dstInfo Contains the requested dst color type. - * @param inputColorPtr Copies the encoded color table to the client's - * input color table if the client requests kIndex8. - * @param inputColorCount If the client requests kIndex8, sets - * inputColorCount to 256. Since gifs always - * contain 8-bit indices, we need a 256 entry color - * table to ensure that indexing is always in - * bounds. - */ - void initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* colorPtr, - int* inputColorCount); - - /* - * Checks for invalid inputs and calls rewindIfNeeded(), setFramDimensions(), and - * initializeColorTable() in the proper sequence. - */ - SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, - int* inputColorCount, const Options& opts); - - /* - * Initializes the swizzler. - * - * @param dstInfo Output image information. Dimensions may have been - * adjusted if the image frame size does not match the size - * indicated in the header. - * @param zeroInit Indicates if destination memory is zero initialized. - */ - SkCodec::Result initializeSwizzler(const SkImageInfo& dstInfo, - ZeroInitialized zeroInit); - - /* - * @return kSuccess if the read is successful and kIncompleteInput if the - * read fails. - */ - SkCodec::Result readRow(); - /* * This function cleans up the gif object after the decode completes * It is used in a SkAutoTCallIProc template @@ -154,21 +88,10 @@ private: * @param stream the stream of image data * @param gif pointer to library type that manages gif decode * takes ownership - * @param transIndex The transparent index. An invalid value - * indicates that there is no transparent index. */ - SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif, uint32_t transIndex); + SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif); SkAutoTCallVProc fGif; // owned - SkAutoTDeleteArray fSrcBuffer; - SkIRect fFrameDims; - const uint32_t fTransIndex; - uint32_t fFillIndex; - bool fFrameIsSubset; - SkAutoTDelete fSwizzler; - SkAutoTUnref fColorTable; - - friend class SkGifScanlineDecoder; typedef SkCodec INHERITED; }; diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp index cf678db856..a9e067eaaf 100644 --- a/src/codec/SkScaledCodec.cpp +++ b/src/codec/SkScaledCodec.cpp @@ -44,6 +44,15 @@ SkScaledCodec::SkScaledCodec(SkScanlineDecoder* scanlineDecoder) SkScaledCodec::~SkScaledCodec() {} +// returns a scaled dimension based on the original dimension and the sampleSize +// NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder +static int get_scaled_dimension(int srcDimension, int sampleSize) { + if (sampleSize > srcDimension) { + return 1; + } + return srcDimension / sampleSize; +} + static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims, const SkISize& scaledCodecDims, float desiredScale) { if (nativeDims == scaledCodecDims) { @@ -188,25 +197,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi Result result = fScanlineDecoder->start(requestedInfo, &options, ctable, ctableCount); if (kSuccess == result) { // native decode supported - switch (fScanlineDecoder->getScanlineOrder()) { - case SkScanlineDecoder::kTopDown_SkScanlineOrder: - case SkScanlineDecoder::kBottomUp_SkScanlineOrder: - case SkScanlineDecoder::kNone_SkScanlineOrder: - return fScanlineDecoder->getScanlines(dst, requestedInfo.height(), rowBytes); - case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: { - for (int y = 0; y < requestedInfo.height(); y++) { - int dstY = fScanlineDecoder->getY(); - void* dstPtr = SkTAddOffset(dst, rowBytes * dstY); - result = fScanlineDecoder->getScanlines(dstPtr, 1, rowBytes); - // FIXME (msarett): Make the SkCodec base class take care of filling - // uninitialized pixels so we can return immediately on kIncompleteInput. - if (kSuccess != result && kIncompleteInput != result) { - return result; - } - } - return result; - } - } + return fScanlineDecoder->getScanlines(dst, requestedInfo.height(), rowBytes); } if (kInvalidScale != result) { @@ -255,7 +246,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi } dst = SkTAddOffset(dst, rowBytes); } - return result; + return kSuccess; } case SkScanlineDecoder::kBottomUp_SkScanlineOrder: case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: { @@ -274,13 +265,13 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi } } } - return result; + return kSuccess; } case SkScanlineDecoder::kNone_SkScanlineOrder: { SkAutoMalloc storage(srcHeight * rowBytes); uint8_t* storagePtr = static_cast(storage.get()); result = fScanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes); - if (kSuccess != result && kIncompleteInput != result) { + if (kSuccess != result) { return result; } storagePtr += Y0 * rowBytes; @@ -289,7 +280,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi storagePtr += sampleY * rowBytes; dst = SkTAddOffset(dst, rowBytes); } - return result; + return kSuccess; } default: SkASSERT(false); diff --git a/src/codec/SkScanlineDecoder.cpp b/src/codec/SkScanlineDecoder.cpp index e011507f80..6a8ce30c98 100644 --- a/src/codec/SkScanlineDecoder.cpp +++ b/src/codec/SkScanlineDecoder.cpp @@ -7,7 +7,6 @@ #include "SkScanlineDecoder.h" #include "SkBmpCodec.h" -#include "SkCodec_libgif.h" #include "SkCodec_libpng.h" #include "SkCodec_wbmp.h" #include "SkCodecPriv.h" @@ -25,7 +24,6 @@ static const DecoderProc gDecoderProcs[] = { #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK { SkJpegCodec::IsJpeg, SkJpegCodec::NewSDFromStream }, #endif - { SkGifCodec::IsGif, SkGifCodec::NewSDFromStream }, { SkBmpCodec::IsBmp, SkBmpCodec::NewSDFromStream }, { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewSDFromStream }, }; diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp index 041e71c67f..27c48fb4b8 100644 --- a/tests/CodexTest.cpp +++ b/tests/CodexTest.cpp @@ -221,9 +221,9 @@ DEF_TEST(Codec, r) { check(r, "google_chrome.ico", SkISize::Make(256, 256), false, false); // GIF - check(r, "box.gif", SkISize::Make(200, 55), true, false); - check(r, "color_wheel.gif", SkISize::Make(128, 128), true, false); - check(r, "randPixels.gif", SkISize::Make(8, 8), true, false); + check(r, "box.gif", SkISize::Make(200, 55), false, false); + check(r, "color_wheel.gif", SkISize::Make(128, 128), false, false); + check(r, "randPixels.gif", SkISize::Make(8, 8), false, false); // JPG check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, false);