From 5af4e0bc8fd17944f3c0527462aeba367f6d590a Mon Sep 17 00:00:00 2001 From: msarett Date: Tue, 17 Nov 2015 11:18:03 -0800 Subject: Make SkAndroidCodec support gif Involves a few bug fixes in SkCodec_libgif and a bit more complexity in SkSwizzler. BUG=skia:4405 Review URL: https://codereview.chromium.org/1445313002 --- src/codec/SkAndroidCodec.cpp | 1 + src/codec/SkCodec_libgif.cpp | 54 +++++----------------- src/codec/SkSampledCodec.cpp | 3 +- src/codec/SkSwizzler.cpp | 47 +++++++++++++------ src/codec/SkSwizzler.h | 106 ++++++++++++++++++++++++++++++++++++++----- 5 files changed, 140 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp index b0a78d0b17..86c067558f 100644 --- a/src/codec/SkAndroidCodec.cpp +++ b/src/codec/SkAndroidCodec.cpp @@ -33,6 +33,7 @@ SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream) { case kJPEG_SkEncodedFormat: case kWBMP_SkEncodedFormat: case kBMP_SkEncodedFormat: + case kGIF_SkEncodedFormat: return new SkSampledCodec(codec.detach()); default: // FIXME: SkSampledCodec is temporarily disabled for other formats diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp index 03107aba2e..65503558aa 100644 --- a/src/codec/SkCodec_libgif.cpp +++ b/src/codec/SkCodec_libgif.cpp @@ -436,12 +436,16 @@ SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo // Initialize color table and copy to the client if necessary this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); - return kSuccess; + + return this->initializeSwizzler(dstInfo, opts); } SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); - fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts)); + const SkIRect* frameRect = fFrameIsSubset ? &fFrameRect : nullptr; + fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts, + frameRect)); + if (nullptr != fSwizzler.get()) { return kSuccess; } @@ -472,29 +476,14 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, // Initialize the swizzler if (fFrameIsSubset) { - const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); - if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } - // Fill the background SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()), opts.fZeroInitialized); - - // Modify the dst pointer - const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType()); - dst = SkTAddOffset(dst, dstRowBytes * fFrameRect.top() + - dstBytesPerPixel * fFrameRect.left()); - } else { - if (kSuccess != this->initializeSwizzler(dstInfo, opts)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } } // Iterate over rows of the input - uint32_t height = fFrameRect.height(); - for (uint32_t y = 0; y < height; y++) { + for (int y = fFrameRect.top(); y < fFrameRect.bottom(); y++) { if (!this->readRow()) { *rowsDecoded = y; return gif_error("Could not decode line.\n", kIncompleteInput); @@ -514,25 +503,7 @@ uint32_t SkGifCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) { - - Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options()); - if (kSuccess != result) { - return result; - } - - // Initialize the swizzler - if (fFrameIsSubset) { - const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameRect.width(), fFrameRect.height()); - if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } - } else { - if (kSuccess != this->initializeSwizzler(dstInfo, opts)) { - return gif_error("Could not initialize swizzler.\n", kUnimplemented); - } - } - - return kSuccess; + return this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options()); } int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { @@ -544,7 +515,7 @@ int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType(), this->dstInfo().alphaType()); - SkSampler::Fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized); + fSwizzler->fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized); // Do nothing for rows before the image frame rowsBeforeFrame = SkTMax(0, fFrameRect.top() - this->INHERITED::nextScanline()); @@ -555,10 +526,6 @@ int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { rowsAfterFrame = SkTMax(0, this->INHERITED::nextScanline() + rowsInFrame - fFrameRect.bottom()); rowsInFrame = SkTMax(0, rowsInFrame - rowsAfterFrame); - - // Adjust dst pointer for left offset - int offset = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameRect.left(); - dst = SkTAddOffset(dst, offset); } for (int i = 0; i < rowsInFrame; i++) { @@ -584,7 +551,8 @@ int SkGifCodec::onOutputScanline(int inputScanline) const { if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bottom()) { return inputScanline; } - return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height()); + return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height()) + + fFrameRect.top(); } return inputScanline; } diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp index 08c4f2a3ab..52e5648742 100644 --- a/src/codec/SkSampledCodec.cpp +++ b/src/codec/SkSampledCodec.cpp @@ -226,8 +226,9 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix } return SkCodec::kSuccess; } + case SkCodec::kOutOfOrder_SkScanlineOrder: case SkCodec::kBottomUp_SkScanlineOrder: { - // Note that this mode does not support subsetting. + // Note that these modes do not support subsetting. SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); int y; for (y = 0; y < nativeSize.height(); y++) { diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp index 242866db44..d783380294 100644 --- a/src/codec/SkSwizzler.cpp +++ b/src/codec/SkSwizzler.cpp @@ -594,7 +594,8 @@ static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow, SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable, const SkImageInfo& dstInfo, - const SkCodec::Options& options) { + const SkCodec::Options& options, + const SkIRect* frame) { if (dstInfo.colorType() == kUnknown_SkColorType || kUnknown == sc) { return nullptr; } @@ -776,43 +777,59 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } // Store bpp in bytes if it is an even multiple, otherwise use bits - int bpp = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc); + int srcBPP = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc); + int dstBPP = SkColorTypeBytesPerPixel(dstInfo.colorType()); int srcOffset = 0; int srcWidth = dstInfo.width(); + int dstOffset = 0; + int dstWidth = srcWidth; if (options.fSubset) { + // We do not currently support subset decodes for image types that may have + // frames (gif). + SkASSERT(!frame); srcOffset = options.fSubset->left(); srcWidth = options.fSubset->width(); + dstWidth = srcWidth; + } else if (frame) { + dstOffset = frame->left(); + srcWidth = frame->width(); } - return new SkSwizzler(proc, ctable, srcOffset, srcWidth, bpp); + return new SkSwizzler(proc, ctable, srcOffset, srcWidth, dstOffset, dstWidth, srcBPP, dstBPP); } -SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int subsetWidth, - int bpp) +SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, + int dstOffset, int dstWidth, int srcBPP, int dstBPP) : fRowProc(proc) , fColorTable(ctable) , fSrcOffset(srcOffset) - , fX0(srcOffset) - , fSubsetWidth(subsetWidth) - , fDstWidth(subsetWidth) + , fDstOffset(dstOffset) + , fSrcOffsetUnits(srcOffset * srcBPP) + , fDstOffsetBytes(dstOffset * dstBPP) + , fSrcWidth(srcWidth) + , fDstWidth(dstWidth) + , fSwizzleWidth(srcWidth) + , fAllocatedWidth(dstWidth) , fSampleX(1) - , fBPP(bpp) + , fSrcBPP(srcBPP) + , fDstBPP(dstBPP) {} int SkSwizzler::onSetSampleX(int sampleX) { SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be // way to report failure? fSampleX = sampleX; - fX0 = get_start_coord(sampleX) + fSrcOffset; - fDstWidth = get_scaled_dimension(fSubsetWidth, sampleX); + fSrcOffsetUnits = (get_start_coord(sampleX) + fSrcOffset) * fSrcBPP; + fDstOffsetBytes = (fDstOffset / sampleX) * fDstBPP; + fSwizzleWidth = get_scaled_dimension(fSrcWidth, sampleX); + fAllocatedWidth = get_scaled_dimension(fDstWidth, sampleX); - // check that fX0 is valid - SkASSERT(fX0 >= 0); - return fDstWidth; + return fAllocatedWidth; } SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { SkASSERT(nullptr != dst && nullptr != src); - return fRowProc(dst, src, fDstWidth, fBPP, fSampleX * fBPP, fX0 * fBPP, fColorTable); + return fRowProc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, + fSampleX * fSrcBPP, fSrcOffsetUnits, fColorTable); } diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h index 691dc77aa6..7f5bbc6209 100644 --- a/src/codec/SkSwizzler.h +++ b/src/codec/SkSwizzler.h @@ -126,11 +126,19 @@ public: * @param options Indicates if dst is zero-initialized. The * implementation may choose to skip writing zeroes * if set to kYes_ZeroInitialized. - * Contains subset information. + * Contains partial scanline information. + * @param frame Is non-NULL if the source pixels are part of an image + * frame that is a subset of the full image. + * + * Note that a deeper discussion of partial scanline subsets and image frame + * subsets is below. Currently, we do not support both simultaneously. If + * options->fSubset is non-NULL, frame must be NULL. + * * @return A new SkSwizzler or nullptr on failure. */ static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable, - const SkImageInfo& dstInfo, const SkCodec::Options&); + const SkImageInfo& dstInfo, const SkCodec::Options&, + const SkIRect* frame = nullptr); /** * Swizzle a line. Generally this will be called height times, once @@ -151,7 +159,7 @@ public: */ void fill(const SkImageInfo& info, void* dst, size_t rowBytes, uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit) override { - const SkImageInfo fillInfo = info.makeWH(fDstWidth, info.height()); + const SkImageInfo fillInfo = info.makeWH(fAllocatedWidth, info.height()); SkSampler::Fill(fillInfo, dst, rowBytes, colorOrIndex, zeroInit); } @@ -176,19 +184,93 @@ private: const RowProc fRowProc; const SkPMColor* fColorTable; // Unowned pointer - const int fSrcOffset; // Offset of the src in pixels, allows for partial - // scanline decodes. - int fX0; // Start coordinate for the src, may be different than - // fSrcOffset if we are sampling. - const int fSubsetWidth; // Width of the subset of the source before any sampling. - int fDstWidth; // Width of dst, which may differ with sampling. - int fSampleX; // step between X samples - const int fBPP; // if bitsPerPixel % 8 == 0 + + // Subset Swizzles + // There are two types of subset swizzles that we support. We do not + // support both at the same time. + // TODO: If we want to support partial scanlines for gifs (which may + // use frame subsets), we will need to support both subsetting + // modes at the same time. + // (1) Partial Scanlines + // The client only wants to write a subset of the source pixels + // to the destination. This subset is specified to CreateSwizzler + // using options->fSubset. We will store subset information in + // the following fields. + // + // fSrcOffset: The starting pixel of the source. + // fSrcOffsetUnits: Derived from fSrcOffset with two key + // differences: + // (1) This takes the size of source pixels into + // account by multiplying by fSrcBPP. This may + // be measured in bits or bytes depending on + // which is natural for the SrcConfig. + // (2) If we are sampling, this will be larger + // than fSrcOffset * fSrcBPP, since sampling + // implies that we will skip some pixels. + // fDstOffset: Will be zero. There is no destination offset + // for this type of subset. + // fDstOffsetBytes: Will be zero. + // fSrcWidth: The width of the desired subset of source + // pixels, before any sampling is performed. + // fDstWidth: Will be equal to fSrcWidth, since this is also + // calculated before any sampling is performed. + // For this type of subset, the destination width + // matches the desired subset of the source. + // fSwizzleWidth: The actual number of pixels that will be + // written by the RowProc. This is a scaled + // version of fSrcWidth/fDstWidth. + // fAllocatedWidth: Will be equal to fSwizzleWidth. For this type + // of subset, the number of pixels written is the + // same as the actual width of the destination. + // (2) Frame Subset + // The client will decode the entire width of the source into a + // subset of destination memory. This subset is specified to + // CreateSwizzler in the "frame" parameter. We store subset + // information in the following fields. + // + // fSrcOffset: Will be zero. The starting pixel of the source. + // fSrcOffsetUnits: Will only be non-zero if we are sampling, + // since sampling implies that we will skip some + // pixels. Note that this is measured in bits + // or bytes depending on which is natural for + // SrcConfig. + // fDstOffset: First pixel to write in destination. + // fDstOffsetBytes: fDstOffset * fDstBPP. + // fSrcWidth: The entire width of the source pixels, before + // any sampling is performed. + // fDstWidth: The entire width of the destination memory, + // before any sampling is performed. + // fSwizzleWidth: The actual number of pixels that will be + // written by the RowProc. This is a scaled + // version of fSrcWidth. + // fAllocatedWidth: The actual number of pixels in destination + // memory. This is a scaled version of + // fDstWidth. + // + // If we are not subsetting, these fields are more straightforward. + // fSrcOffset = fDstOffet = fDstOffsetBytes = 0 + // fSrcOffsetUnits may be non-zero (we will skip the first few pixels when sampling) + // fSrcWidth = fDstWidth = Full original width + // fSwizzleWidth = fAllcoatedWidth = Scaled width (if we are sampling) + const int fSrcOffset; + const int fDstOffset; + int fSrcOffsetUnits; + int fDstOffsetBytes; + const int fSrcWidth; + const int fDstWidth; + int fSwizzleWidth; + int fAllocatedWidth; + + int fSampleX; // Step between X samples + const int fSrcBPP; // Bits/bytes per pixel for the SrcConfig + // if bitsPerPixel % 8 == 0 // fBPP is bytesPerPixel // else // fBPP is bitsPerPixel + const int fDstBPP; // Bytes per pixel for the destination color type - SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int subsetWidth, int bpp); + SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, int dstOffset, + int dstWidth, int srcBPP, int dstBPP); int onSetSampleX(int) override; -- cgit v1.2.3