diff options
34 files changed, 1034 insertions, 1133 deletions
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index a1db0acd80..900ba4dfa2 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -38,7 +38,6 @@ #include "SkOSFile.h" #include "SkPictureRecorder.h" #include "SkPictureUtils.h" -#include "SkScanlineDecoder.h" #include "SkString.h" #include "SkSurface.h" #include "SkTaskGroup.h" @@ -522,9 +521,7 @@ static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool int colorCount; const SkImageInfo info = codec->getInfo().makeColorType(colorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromData(encoded)); - if (nullptr == scanlineDecoder || scanlineDecoder->start(info, nullptr, - colors, &colorCount) != SkCodec::kSuccess) + if (codec->startScanlineDecode(info, nullptr, colors, &colorCount) != SkCodec::kSuccess) { SkDebugf("Could not create scanline decoder for %s with color type %s. " "Skipping bench.\n", path.c_str(), color_type_to_str(colorType)); diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp index 2e5703f508..e409cf7ed8 100644 --- a/bench/subset/SubsetSingleBench.cpp +++ b/bench/subset/SubsetSingleBench.cpp @@ -12,7 +12,6 @@ #include "SkCodec.h" #include "SkImageDecoder.h" #include "SkOSFile.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" /* @@ -65,20 +64,19 @@ void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( - SkScanlineDecoder::NewFromStream(fStream->duplicate())); - const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); + const SkImageInfo info = codec->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); - scanlineDecoder->start(info, nullptr, colors, &colorCount); + codec->startScanlineDecode(info, nullptr, colors, &colorCount); SkBitmap bitmap; SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); - scanlineDecoder->skipScanlines(fOffsetTop); + codec->skipScanlines(fOffsetTop); uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < fSubsetHeight; y++) { - scanlineDecoder->getScanlines(row.get(), 1, 0); + codec->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp, fSubsetWidth * bpp); } diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp index bed3580853..a5c4d46655 100644 --- a/bench/subset/SubsetTranslateBench.cpp +++ b/bench/subset/SubsetTranslateBench.cpp @@ -12,7 +12,6 @@ #include "SkCodec.h" #include "SkImageDecoder.h" #include "SkOSFile.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" /* @@ -61,11 +60,10 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( - SkScanlineDecoder::NewFromStream(fStream->duplicate())); - const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); + const SkImageInfo info = codec->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); - scanlineDecoder->start(info, nullptr, colors, &colorCount); + codec->startScanlineDecode(info, nullptr, colors, &colorCount); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. @@ -75,7 +73,7 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { for (int x = 0; x < info.width(); x += fSubsetWidth) { for (int y = 0; y < info.height(); y += fSubsetHeight) { - scanlineDecoder->skipScanlines(y); + codec->skipScanlines(y); const uint32_t currSubsetWidth = x + (int) fSubsetWidth > info.width() ? info.width() - x : fSubsetWidth; @@ -84,7 +82,7 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { info.height() - y : fSubsetHeight; const uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < currSubsetHeight; y++) { - scanlineDecoder->getScanlines(row.get(), 1, 0); + codec->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + x * bpp, currSubsetWidth * bpp); } diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp index 655285fdf8..869e93043d 100644 --- a/bench/subset/SubsetZoomBench.cpp +++ b/bench/subset/SubsetZoomBench.cpp @@ -12,7 +12,6 @@ #include "SkCodec.h" #include "SkImageDecoder.h" #include "SkOSFile.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" /* @@ -61,11 +60,10 @@ void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( - SkScanlineDecoder::NewFromStream(fStream->duplicate())); - const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); + const SkImageInfo info = codec->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); - scanlineDecoder->start(info, nullptr, colors, &colorCount); + codec->startScanlineDecode(info, nullptr, colors, &colorCount); const int centerX = info.width() / 2; const int centerY = info.height() / 2; @@ -83,9 +81,9 @@ void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) { alloc_pixels(&bitmap, subsetInfo, colors, colorCount); uint32_t bpp = info.bytesPerPixel(); - scanlineDecoder->skipScanlines(subsetStartY); + codec->skipScanlines(subsetStartY); for (int y = 0; y < subsetHeight; y++) { - scanlineDecoder->getScanlines(row.get(), 1, 0); + codec->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp, subsetWidth * bpp); } @@ -311,6 +311,12 @@ static void push_codec_srcs(Path path) { } } + if (path.endsWith(".ico") || path.endsWith(".ICO")) { + // FIXME: skbug.com/4404: ICO does not have the ability to decode scanlines, so we cannot + // use SkScaledCodec with it. + return; + } + // SkScaledCodec Scales // The native scales are included to make sure that SkScaledCodec defaults to the native // scaling strategy when possible. diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 96a0ab1a3e..2e22556335 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -25,7 +25,6 @@ #include "SkRecorder.h" #include "SkSVGCanvas.h" #include "SkScaledCodec.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" #include "SkTLogic.h" #include "SkXMLWriter.h" @@ -238,18 +237,6 @@ bool CodecSrc::veto(SinkFlags flags) const { || flags.approach != SinkFlags::kDirect; } -SkScanlineDecoder* start_scanline_decoder(SkData* encoded, const SkImageInfo& info, - SkPMColor* colorPtr, int* colorCountPtr) { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromData(encoded)); - if (nullptr == scanlineDecoder) { - return nullptr; - } - if (SkCodec::kSuccess != scanlineDecoder->start(info, NULL, colorPtr, colorCountPtr)) { - return nullptr; - } - return scanlineDecoder.detach(); -} - Error CodecSrc::draw(SkCanvas* canvas) const { SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); if (!encoded) { @@ -348,25 +335,24 @@ Error CodecSrc::draw(SkCanvas* canvas) const { break; } case kScanline_Mode: { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( - start_scanline_decoder(encoded.get(), decodeInfo, colorPtr, colorCountPtr)); - if (nullptr == scanlineDecoder) { + if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr, + colorCountPtr)) { 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), + switch (codec->getScanlineOrder()) { + case SkCodec::kTopDown_SkScanlineOrder: + case SkCodec::kBottomUp_SkScanlineOrder: + case SkCodec::kNone_SkScanlineOrder: + result = codec->getScanlines(bitmap.getAddr(0, 0), decodeInfo.height(), bitmap.rowBytes()); break; - case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: { + case SkCodec::kOutOfOrder_SkScanlineOrder: { for (int y = 0; y < decodeInfo.height(); y++) { - int dstY = scanlineDecoder->getY(); + int dstY = codec->nextScanline(); void* dstPtr = bitmap.getAddr(0, dstY); - result = scanlineDecoder->getScanlines(dstPtr, 1, bitmap.rowBytes()); + result = codec->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); @@ -432,11 +418,10 @@ Error CodecSrc::draw(SkCanvas* canvas) const { subsetHeight + extraY : subsetHeight; const int y = row * subsetHeight; //create scanline decoder for each subset - SkAutoTDelete<SkScanlineDecoder> decoder(start_scanline_decoder(encoded.get(), - decodeInfo, colorPtr, colorCountPtr)); - // TODO (msarett): Support this mode for all scanline orderings. - if (nullptr == decoder || SkScanlineDecoder::kTopDown_SkScanlineOrder != - decoder->getScanlineOrder()) { + if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr, + colorCountPtr) + // TODO (msarett): Support this mode for all scanline orderings. + || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) { if (x == 0 && y == 0) { //first try, image may not be compatible return Error::Nonfatal("Could not start top-down scanline decoder"); @@ -445,7 +430,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } } //skip to first line of subset - const SkCodec::Result skipResult = decoder->skipScanlines(y); + const SkCodec::Result skipResult = codec->skipScanlines(y); switch (skipResult) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: @@ -461,7 +446,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds)); SkAutoLockPixels autlockSubsetBm(subsetBm, true); const SkCodec::Result subsetResult = - decoder->getScanlines(buffer, currentSubsetHeight, rowBytes); + codec->getScanlines(buffer, currentSubsetHeight, rowBytes); switch (subsetResult) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: @@ -501,10 +486,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const { const int numStripes = (height + stripeHeight - 1) / stripeHeight; // Decode odd stripes - SkAutoTDelete<SkScanlineDecoder> decoder( - start_scanline_decoder(encoded.get(), decodeInfo, colorPtr, colorCountPtr)); - if (nullptr == decoder || - SkScanlineDecoder::kTopDown_SkScanlineOrder != decoder->getScanlineOrder()) { + if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr, + colorCountPtr) + || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) { // This mode was designed to test the new skip scanlines API in libjpeg-turbo. // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting // to run this test for image types that do not have this scanline ordering. @@ -513,7 +497,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { for (int i = 0; i < numStripes; i += 2) { // Skip a stripe const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight); - SkCodec::Result result = decoder->skipScanlines(linesToSkip); + SkCodec::Result result = codec->skipScanlines(linesToSkip); switch (result) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: @@ -526,7 +510,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { const int startY = (i + 1) * stripeHeight; const int linesToRead = SkTMin(stripeHeight, height - startY); if (linesToRead > 0) { - result = decoder->getScanlines(bitmap.getAddr(0, startY), + result = codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes()); switch (result) { case SkCodec::kSuccess: @@ -539,8 +523,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } // Decode even stripes - const SkCodec::Result startResult = decoder->start(decodeInfo, nullptr, colorPtr, - colorCountPtr); + const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr, + colorPtr, colorCountPtr); if (SkCodec::kSuccess != startResult) { return "Failed to restart scanline decoder with same parameters."; } @@ -548,7 +532,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { // Read a stripe const int startY = i * stripeHeight; const int linesToRead = SkTMin(stripeHeight, height - startY); - SkCodec::Result result = decoder->getScanlines(bitmap.getAddr(0, startY), + SkCodec::Result result = codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes()); switch (result) { case SkCodec::kSuccess: @@ -561,7 +545,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const { // Skip a stripe const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight); if (linesToSkip > 0) { - result = decoder->skipScanlines(linesToSkip); + result = codec->skipScanlines(linesToSkip); switch (result) { case SkCodec::kSuccess: case SkCodec::kIncompleteInput: diff --git a/gyp/codec.gyp b/gyp/codec.gyp index a99707e4ea..205ff917cc 100644 --- a/gyp/codec.gyp +++ b/gyp/codec.gyp @@ -47,7 +47,6 @@ '../src/codec/SkMaskSwizzler.cpp', '../src/codec/SkMasks.cpp', '../src/codec/SkScaledCodec.cpp', - '../src/codec/SkScanlineDecoder.cpp', '../src/codec/SkSwizzler.cpp', '../src/codec/SkWebpCodec.cpp', ], diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h index bdfa2326fb..01fd4fa62a 100644 --- a/include/codec/SkCodec.h +++ b/include/codec/SkCodec.h @@ -45,7 +45,7 @@ public: /** * Return the ImageInfo associated with this codec. */ - const SkImageInfo& getInfo() const { return fInfo; } + const SkImageInfo& getInfo() const { return fSrcInfo; } /** * Return a size that approximately supports the desired scale factor. @@ -131,7 +131,12 @@ public: */ kCouldNotRewind, /** - * This method is not implemented by this generator. + * startScanlineDecode() was not called before calling getScanlines. + */ + kScanlineDecodingNotStarted, + /** + * This method is not implemented by this codec. + * FIXME: Perhaps this should be kUnsupported? */ kUnimplemented, }; @@ -202,6 +207,9 @@ public: * If info is not kIndex8_SkColorType, then the last two parameters may be NULL. If ctableCount * is not null, it will be set to 0. * + * If a scanline decode is in progress, scanline mode will end, requiring the client to call + * startScanlineDecode() in order to return to decoding scanlines. + * * @return Result kSuccess, or another value explaining the type of failure. */ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*, @@ -225,6 +233,147 @@ public: return this->onReallyHasAlpha(); } + /** + * The remaining functions revolve around decoding scanlines. + */ + + /** + * Prepare for a scanline decode with the specified options. + * + * After this call, this class will be ready to decode the first scanline. + * + * This must be called in order to call getScanlines or skipScanlines. + * + * This may require rewinding the stream. + * + * Not all SkCodecs support this. + * + * @param dstInfo Info of the destination. If the dimensions do not match + * those of getInfo, this implies a scale. + * @param options Contains decoding options, including if memory is zero + * initialized. + * @param ctable A pointer to a color table. When dstInfo.colorType() is + * kIndex8, this should be non-NULL and have enough storage for 256 + * colors. The color table will be populated after decoding the palette. + * @param ctableCount A pointer to the size of the color table. When + * dstInfo.colorType() is kIndex8, this should be non-NULL. It will + * be modified to the true size of the color table (<= 256) after + * decoding the palette. + * @return Enum representing success or reason for failure. + */ + Result startScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options* options, + SkPMColor ctable[], int* ctableCount); + + /** + * Simplified version of startScanlineDecode() that asserts that info is NOT + * kIndex8_SkColorType and uses the default Options. + */ + Result startScanlineDecode(const SkImageInfo& dstInfo); + + /** + * Write the next countLines scanlines into dst. + * + * Not valid to call before calling startScanlineDecode(). + * + * @param dst Must be non-null, and large enough to hold countLines + * scanlines of size rowBytes. + * @param countLines Number of lines to write. + * @param rowBytes Number of bytes per row. Must be large enough to hold + * a scanline based on the SkImageInfo used to create this object. + */ + Result getScanlines(void* dst, int countLines, size_t rowBytes); + + /** + * Skip count scanlines. + * + * Not valid to call before calling startScanlineDecode(). + * + * The default version just calls onGetScanlines and discards the dst. + * NOTE: If skipped lines are the only lines with alpha, this default + * will make reallyHasAlpha return true, when it could have returned + * false. + */ + Result skipScanlines(int countLines); + + /** + * The order in which rows are output from the scanline decoder is not the + * same for all variations of all image types. This explains the possible + * output row orderings. + */ + enum SkScanlineOrder { + /* + * By far the most common, this indicates that the image can be decoded + * reliably using the scanline decoder, and that rows will be output in + * the logical order. + */ + kTopDown_SkScanlineOrder, + + /* + * This indicates that the scanline decoder reliably outputs rows, but + * they will be returned in reverse order. If the scanline format is + * kBottomUp, the nextScanline() API can be used to determine the actual + * y-coordinate of the next output row, but the client is not forced + * to take advantage of this, given that it's not too tough to keep + * track independently. + * + * For full image decodes, it is safe to get all of the scanlines at + * once, since the decoder will handle inverting the rows as it + * decodes. + * + * For subset decodes and sampling, it is simplest to get and skip + * scanlines one at a time, using the nextScanline() API. It is + * possible to ask for larger chunks at a time, but this should be used + * with caution. As with full image decodes, the decoder will handle + * inverting the requested rows, but rows will still be delivered + * starting from the bottom of the image. + * + * Upside down bmps are an example. + */ + kBottomUp_SkScanlineOrder, + + /* + * This indicates that the scanline decoder reliably outputs rows, but + * they will not be in logical order. If the scanline format is + * kOutOfOrder, the nextScanline() API should be used to determine the + * actual y-coordinate of the next output row. + * + * For this scanline ordering, it is advisable to get and skip + * scanlines one at a time. + * + * Interlaced gifs are an example. + */ + kOutOfOrder_SkScanlineOrder, + + /* + * Indicates that the entire image must be decoded in order to output + * any amount of scanlines. In this case, it is a REALLY BAD IDEA to + * request scanlines 1-by-1 or in small chunks. The client should + * determine which scanlines are needed and ask for all of them in + * a single call to getScanlines(). + * + * Interlaced pngs are an example. + */ + kNone_SkScanlineOrder, + }; + + /** + * An enum representing the order in which scanlines will be returned by + * the scanline decoder. + */ + SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); } + + /** + * Returns the y-coordinate of the next row to be returned by the scanline + * decoder. This will be overridden in the case of + * kOutOfOrder_SkScanlineOrder and should be unnecessary in the case of + * kNone_SkScanlineOrder. + * + * Results are undefined when not in scanline decoding mode. + */ + int nextScanline() const { + return this->onNextScanline(); + } + protected: SkCodec(const SkImageInfo&, SkStream*); @@ -275,9 +424,59 @@ protected: return fStream.get(); } + /** + * The remaining functions revolve around decoding scanlines. + */ + + /** + * Most images types will be kTopDown and will not need to override this function. + */ + virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; } + + /** + * Most images will be kTopDown and will not need to override this function. + */ + virtual int onNextScanline() const { return fCurrScanline; } + + /** + * Update the next scanline. Used by interlaced png. + */ + void updateNextScanline(int newY) { fCurrScanline = newY; } + + const SkImageInfo& dstInfo() const { return fDstInfo; } + + const SkCodec::Options& options() const { return fOptions; } + private: - const SkImageInfo fInfo; + const SkImageInfo fSrcInfo; SkAutoTDelete<SkStream> fStream; bool fNeedsRewind; + // These fields are only meaningful during scanline decodes. + SkImageInfo fDstInfo; + SkCodec::Options fOptions; + int fCurrScanline; + + // Methods for scanline decoding. + virtual SkCodec::Result onStartScanlineDecode(const SkImageInfo& dstInfo, + const SkCodec::Options& options, SkPMColor ctable[], int* ctableCount) { + return kUnimplemented; + } + + // Naive default version just calls onGetScanlines on temp memory. + virtual SkCodec::Result onSkipScanlines(int countLines) { + SkAutoMalloc storage(fDstInfo.minRowBytes()); + // Note that we pass 0 to rowBytes so we continue to use the same memory. + // Also note that while getScanlines checks that rowBytes is big enough, + // onGetScanlines bypasses that check. + // Calling the virtual method also means we do not double count + // countLines. + return this->onGetScanlines(storage.get(), countLines, 0); + } + + virtual SkCodec::Result onGetScanlines(void* dst, int countLines, + size_t rowBytes) { + return kUnimplemented; + } + }; #endif // SkCodec_DEFINED diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h index 1bcdf085b2..20428d8d73 100644 --- a/include/codec/SkScaledCodec.h +++ b/include/codec/SkScaledCodec.h @@ -8,9 +8,7 @@ #define SkScaledCodec_DEFINED #include "SkCodec.h" -#include "SkScanlineDecoder.h" -class SkScanlineDecoder; class SkStream; /** @@ -52,18 +50,18 @@ protected: Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*) override; SkEncodedFormat onGetEncodedFormat() const override { - return fScanlineDecoder->getEncodedFormat(); + return fCodec->getEncodedFormat(); } bool onReallyHasAlpha() const override { - return fScanlineDecoder->reallyHasAlpha(); + return fCodec->reallyHasAlpha(); } private: - SkAutoTDelete<SkScanlineDecoder> fScanlineDecoder; + SkAutoTDelete<SkCodec> fCodec; - explicit SkScaledCodec(SkScanlineDecoder*); + explicit SkScaledCodec(SkCodec*); typedef SkCodec INHERITED; }; diff --git a/include/codec/SkScanlineDecoder.h b/include/codec/SkScanlineDecoder.h deleted file mode 100644 index 61184d644a..0000000000 --- a/include/codec/SkScanlineDecoder.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkScanlineDecoder_DEFINED -#define SkScanlineDecoder_DEFINED - -#include "../private/SkTemplates.h" -#include "SkCodec.h" -#include "SkImageInfo.h" -#include "SkTypes.h" - -class SkScanlineDecoder : public SkNoncopyable { -public: - /** - * If this stream represents an encoded image that we know how to decode - * in scanlines, return an SkScanlineDecoder that can decode it. Otherwise - * return NULL. - * - * start() must be called in order to decode any scanlines. - * - * If NULL is returned, the stream is deleted immediately. Otherwise, the - * SkScanlineDecoder takes ownership of it, and will delete it when done - * with it. - */ - static SkScanlineDecoder* NewFromStream(SkStream*); - - /** - * Similar to NewFromStream, but reads from an SkData. - * - * Will take a ref if it returns a scanline decoder, else will not affect - * the data. - */ - static SkScanlineDecoder* NewFromData(SkData*); - - /** - * Clean up after reading/skipping scanlines. - * - * It is possible that not all scanlines will have been read/skipped. In - * fact, in the case of subset decodes, it is likely that there will be - * scanlines at the bottom of the image that have been ignored. - */ - virtual ~SkScanlineDecoder() {} - - /** - * Return a size that approximately supports the desired scale factor. - * The codec may not be able to scale efficiently to the exact scale - * factor requested, so return a size that approximates that scale. - * The returned value is the codec's suggestion for the closest valid - * scale that it can natively support - * FIXME: share this with SkCodec - */ - SkISize getScaledDimensions(float desiredScale) { - return this->onGetScaledDimensions(desiredScale); - } - - /** - * Returns the default info, corresponding to the encoded data. - */ - const SkImageInfo& getInfo() { return fSrcInfo; } - - /** - * Initialize on the first scanline, with the specified options. - * - * This must be called in order to call getScanlnies or skipScanlines. If - * it has been called before, this will require rewinding the stream. - * - * @param dstInfo Info of the destination. If the dimensions do not match - * those of getInfo, this implies a scale. - * @param options Contains decoding options, including if memory is zero - * initialized. - * @param ctable A pointer to a color table. When dstInfo.colorType() is - * kIndex8, this should be non-NULL and have enough storage for 256 - * colors. The color table will be populated after decoding the palette. - * @param ctableCount A pointer to the size of the color table. When - * dstInfo.colorType() is kIndex8, this should be non-NULL. It will - * be modified to the true size of the color table (<= 256) after - * decoding the palette. - * @return Enum representing success or reason for failure. - */ - SkCodec::Result start(const SkImageInfo& dstInfo, const SkCodec::Options* options, - SkPMColor ctable[], int* ctableCount); - - /** - * Simplified version of start() that asserts that info is NOT - * kIndex8_SkColorType and uses the default Options. - */ - SkCodec::Result start(const SkImageInfo& dstInfo); - - /** - * Write the next countLines scanlines into dst. - * - * Not valid to call before calling start(). - * - * @param dst Must be non-null, and large enough to hold countLines - * scanlines of size rowBytes. - * @param countLines Number of lines to write. - * @param rowBytes Number of bytes per row. Must be large enough to hold - * a scanline based on the SkImageInfo used to create this object. - */ - SkCodec::Result getScanlines(void* dst, int countLines, size_t rowBytes) { - SkASSERT(!fDstInfo.isEmpty()); - if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0 - || fCurrScanline + countLines > fDstInfo.height()) { - return SkCodec::kInvalidParameters; - } - const SkCodec::Result result = this->onGetScanlines(dst, countLines, rowBytes); - fCurrScanline += countLines; - return result; - } - - /** - * Skip count scanlines. - * - * Not valid to call before calling start(). - * - * The default version just calls onGetScanlines and discards the dst. - * NOTE: If skipped lines are the only lines with alpha, this default - * will make reallyHasAlpha return true, when it could have returned - * false. - */ - SkCodec::Result skipScanlines(int countLines) { - SkASSERT(!fDstInfo.isEmpty()); - if (fCurrScanline + countLines > fDstInfo.height()) { - // Arguably, we could just skip the scanlines which are remaining, - // and return kSuccess. We choose to return invalid so the client - // can catch their bug. - return SkCodec::kInvalidParameters; - } - const SkCodec::Result result = this->onSkipScanlines(countLines); - fCurrScanline += countLines; - return result; - } - - /** - * Some images may initially report that they have alpha due to the format - * of the encoded data, but then never use any colors which have alpha - * less than 100%. This function can be called *after* decoding to - * determine if such an image truly had alpha. Calling it before decoding - * is undefined. - * FIXME: see skbug.com/3582. - */ - bool reallyHasAlpha() const { - return this->onReallyHasAlpha(); - } - - /** - * Format of the encoded data. - */ - SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); } - - /** - * The order in which rows are output from the scanline decoder is not the - * same for all variations of all image types. This explains the possible - * output row orderings. - */ - enum SkScanlineOrder { - /* - * By far the most common, this indicates that the image can be decoded - * reliably using the scanline decoder, and that rows will be output in - * the logical order. - */ - kTopDown_SkScanlineOrder, - - /* - * This indicates that the scanline decoder reliably outputs rows, but - * they will be returned in reverse order. If the scanline format is - * kBottomUp, the getY() API can be used to determine the actual - * y-coordinate of the next output row, but the client is not forced - * to take advantage of this, given that it's not too tough to keep - * track independently. - * - * For full image decodes, it is safe to get all of the scanlines at - * once, since the decoder will handle inverting the rows as it - * decodes. - * - * For subset decodes and sampling, it is simplest to get and skip - * scanlines one at a time, using the getY() API. It is possible to - * ask for larger chunks at a time, but this should be used with - * caution. As with full image decodes, the decoder will handle - * inverting the requested rows, but rows will still be delivered - * starting from the bottom of the image. - * - * Upside down bmps are an example. - */ - kBottomUp_SkScanlineOrder, - - /* - * This indicates that the scanline decoder reliably outputs rows, but - * they will not be in logical order. If the scanline format is - * kOutOfOrder, the getY() API should be used to determine the actual - * y-coordinate of the next output row. - * - * For this scanline ordering, it is advisable to get and skip - * scanlines one at a time. - * - * Interlaced gifs are an example. - */ - kOutOfOrder_SkScanlineOrder, - - /* - * Indicates that the entire image must be decoded in order to output - * any amount of scanlines. In this case, it is a REALLY BAD IDEA to - * request scanlines 1-by-1 or in small chunks. The client should - * determine which scanlines are needed and ask for all of them in - * a single call to getScanlines(). - * - * Interlaced pngs are an example. - */ - kNone_SkScanlineOrder, - }; - - /** - * An enum representing the order in which scanlines will be returned by - * the scanline decoder. - */ - SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); } - - /** - * Returns the y-coordinate of the next row to be returned by the scanline - * decoder. This will be overridden in the case of - * kOutOfOrder_SkScanlineOrder and should be unnecessary in the case of - * kNone_SkScanlineOrder. - */ - int getY() const { - SkASSERT(kNone_SkScanlineOrder != this->getScanlineOrder()); - return this->onGetY(); - } - -protected: - SkScanlineDecoder(const SkImageInfo& srcInfo) - : fSrcInfo(srcInfo) - , fDstInfo() - , fOptions() - , fCurrScanline(0) {} - - virtual SkISize onGetScaledDimensions(float /* desiredScale */) { - // By default, scaling is not supported. - return this->getInfo().dimensions(); - } - - virtual SkEncodedFormat onGetEncodedFormat() const = 0; - - virtual bool onReallyHasAlpha() const { return false; } - - /** - * Most images types will be kTopDown and will not need to override this function. - */ - virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; } - - /** - * Most images will be kTopDown and will not need to override this function. - */ - virtual int onGetY() const { return fCurrScanline; } - - const SkImageInfo& dstInfo() const { return fDstInfo; } - - const SkCodec::Options& options() const { return fOptions; } - -private: - const SkImageInfo fSrcInfo; - SkImageInfo fDstInfo; - SkCodec::Options fOptions; - int fCurrScanline; - - virtual SkCodec::Result onStart(const SkImageInfo& dstInfo, - const SkCodec::Options& options, - SkPMColor ctable[], int* ctableCount) = 0; - - // Naive default version just calls onGetScanlines on temp memory. - virtual SkCodec::Result onSkipScanlines(int countLines) { - SkAutoMalloc storage(fDstInfo.minRowBytes()); - // Note that we pass 0 to rowBytes so we continue to use the same memory. - // Also note that while getScanlines checks that rowBytes is big enough, - // onGetScanlines bypasses that check. - // Calling the virtual method also means we do not double count - // countLines. - return this->onGetScanlines(storage.get(), countLines, 0); - } - - virtual SkCodec::Result onGetScanlines(void* dst, int countLines, - size_t rowBytes) = 0; - -}; -#endif // SkScanlineDecoder_DEFINED diff --git a/resources/plane_interlaced.png b/resources/plane_interlaced.png Binary files differnew file mode 100644 index 0000000000..3b217f205d --- /dev/null +++ b/resources/plane_interlaced.png diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp index a55cb8cf55..4e801f0be1 100644 --- a/src/codec/SkBmpCodec.cpp +++ b/src/codec/SkBmpCodec.cpp @@ -262,10 +262,10 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { } // Check for valid dimensions from header - SkScanlineDecoder::SkScanlineOrder rowOrder = SkScanlineDecoder::kBottomUp_SkScanlineOrder; + SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder; if (height < 0) { height = -height; - rowOrder = SkScanlineDecoder::kTopDown_SkScanlineOrder; + rowOrder = SkCodec::kTopDown_SkScanlineOrder; } // The height field for bmp in ico is double the actual height because they // contain an XOR mask followed by an AND mask @@ -531,7 +531,7 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) { } SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream, - uint16_t bitsPerPixel, SkScanlineDecoder::SkScanlineOrder rowOrder) + uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder) : INHERITED(info, stream) , fBitsPerPixel(bitsPerPixel) , fRowOrder(rowOrder) @@ -541,11 +541,11 @@ bool SkBmpCodec::onRewind() { return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr); } -int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) { - if (SkScanlineDecoder::kTopDown_SkScanlineOrder == fRowOrder) { +int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const { + if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) { return y; } - SkASSERT(SkScanlineDecoder::kBottomUp_SkScanlineOrder == fRowOrder); + SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder); return height - y - 1; } @@ -557,7 +557,7 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) { * filling at the top of the image. */ void* SkBmpCodec::getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const { - return (SkScanlineDecoder::kTopDown_SkScanlineOrder == fRowOrder) ? + return (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) ? SkTAddOffset<void*>(dst, y * dstRowBytes) : dst; } @@ -574,71 +574,36 @@ uint32_t SkBmpCodec::computeNumColors(uint32_t numColors) { return numColors; } -/* - * Scanline decoder for bmps - */ -class SkBmpScanlineDecoder : public SkScanlineDecoder { -public: - SkBmpScanlineDecoder(SkBmpCodec* codec) - : INHERITED(codec->getInfo()) - , fCodec(codec) - {} - - SkEncodedFormat onGetEncodedFormat() const override { - return kBMP_SkEncodedFormat; - } - - SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, - SkPMColor inputColorPtr[], int* inputColorCount) override { - if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; - } - if (options.fSubset) { - // Subsets are not supported. - return SkCodec::kUnimplemented; - } - if (dstInfo.dimensions() != this->getInfo().dimensions()) { - if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return SkCodec::kInvalidScale; - } - } - if (!conversion_possible(dstInfo, this->getInfo())) { - SkCodecPrintf("Error: cannot convert input type to output type.\n"); - return SkCodec::kInvalidConversion; - } - - return fCodec->prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount); +SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, + const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; } - - SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { - // Create a new image info representing the portion of the image to decode - SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); - - // Decode the requested rows - return fCodec->decodeRows(rowInfo, dst, rowBytes, this->options()); + if (options.fSubset) { + // Subsets are not supported. + return kUnimplemented; } - - SkScanlineOrder onGetScanlineOrder() const override { - return fCodec->fRowOrder; + if (dstInfo.dimensions() != this->getInfo().dimensions()) { + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return SkCodec::kInvalidScale; + } } - - int onGetY() const override { - return fCodec->getDstRow(this->INHERITED::onGetY(), this->dstInfo().height()); + if (!conversion_possible(dstInfo, this->getInfo())) { + SkCodecPrintf("Error: cannot convert input type to output type.\n"); + return kInvalidConversion; } - // TODO(msarett): Override default skipping with something more clever. - -private: - SkAutoTDelete<SkBmpCodec> fCodec; + return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount); +} - typedef SkScanlineDecoder INHERITED; -}; +SkCodec::Result SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { + // Create a new image info representing the portion of the image to decode + SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); -SkScanlineDecoder* SkBmpCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete<SkBmpCodec> codec(static_cast<SkBmpCodec*>(SkBmpCodec::NewFromStream(stream))); - if (!codec) { - return NULL; - } + // Decode the requested rows + return this->decodeRows(rowInfo, dst, rowBytes, this->options()); +} - return new SkBmpScanlineDecoder(codec.detach()); +int SkBmpCodec::onNextScanline() const { + return this->getDstRow(this->INHERITED::onNextScanline(), this->dstInfo().height()); } diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h index 4b2cd2a268..ea6f789407 100644 --- a/src/codec/SkBmpCodec.h +++ b/src/codec/SkBmpCodec.h @@ -11,7 +11,6 @@ #include "SkColorTable.h" #include "SkImageInfo.h" #include "SkMaskSwizzler.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" #include "SkSwizzler.h" #include "SkTypes.h" @@ -41,17 +40,10 @@ public: */ static SkCodec* NewFromIco(SkStream*); - /* - * Assumes IsBmp was called and returned true - * Creates a bmp scanline decoder - * Takes ownership of the stream - */ - static SkScanlineDecoder* NewSDFromStream(SkStream* stream); - protected: SkBmpCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, - SkScanlineDecoder::SkScanlineOrder rowOrder); + SkCodec::SkScanlineOrder rowOrder); SkEncodedFormat onGetEncodedFormat() const override { return kBMP_SkEncodedFormat; } @@ -87,7 +79,7 @@ protected: * when we want to decode the full or one when we are * sampling. */ - int32_t getDstRow(int32_t y, int32_t height); + int32_t getDstRow(int32_t y, int32_t height) const; /* * Get the destination row to start filling from @@ -107,7 +99,7 @@ protected: * Accessors used by subclasses */ uint16_t bitsPerPixel() const { return fBitsPerPixel; } - SkScanlineDecoder::SkScanlineOrder rowOrder() const { return fRowOrder; } + SkScanlineOrder onGetScanlineOrder() const override { return fRowOrder; } /* * To be overriden by bmp subclasses, which provide unique implementations. @@ -153,10 +145,17 @@ private: virtual Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) = 0; - const uint16_t fBitsPerPixel; - const SkScanlineDecoder::SkScanlineOrder fRowOrder; + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options&, + SkPMColor inputColorPtr[], int* inputColorCount) override; + + Result onGetScanlines(void* dst, int count, size_t rowBytes) override; + + int onNextScanline() const override; + + // TODO(msarett): Override default skipping with something more clever. - friend class SkBmpScanlineDecoder; + const uint16_t fBitsPerPixel; + const SkScanlineOrder fRowOrder; typedef SkCodec INHERITED; }; diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp index 3036f337b7..2e88d54f02 100644 --- a/src/codec/SkBmpMaskCodec.cpp +++ b/src/codec/SkBmpMaskCodec.cpp @@ -14,7 +14,7 @@ */ SkBmpMaskCodec::SkBmpMaskCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, SkMasks* masks, - SkScanlineDecoder::SkScanlineOrder rowOrder) + SkCodec::SkScanlineOrder rowOrder) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fMasks(masks) , fMaskSwizzler(nullptr) diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h index 58b2e6b507..0b1291266d 100644 --- a/src/codec/SkBmpMaskCodec.h +++ b/src/codec/SkBmpMaskCodec.h @@ -30,7 +30,7 @@ public: */ SkBmpMaskCodec(const SkImageInfo& srcInfo, SkStream* stream, uint16_t bitsPerPixel, SkMasks* masks, - SkScanlineDecoder::SkScanlineOrder rowOrder); + SkCodec::SkScanlineOrder rowOrder); protected: diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp index 58c060531e..79dd63307e 100644 --- a/src/codec/SkBmpRLECodec.cpp +++ b/src/codec/SkBmpRLECodec.cpp @@ -9,7 +9,6 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkScaledCodec.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" /* @@ -19,7 +18,7 @@ SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, uint32_t offset, - SkScanlineDecoder::SkScanlineOrder rowOrder, + SkCodec::SkScanlineOrder rowOrder, size_t RLEBytes) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(nullptr) diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h index aa5b061273..ea2c2b6bab 100644 --- a/src/codec/SkBmpRLECodec.h +++ b/src/codec/SkBmpRLECodec.h @@ -36,7 +36,7 @@ public: */ SkBmpRLECodec(const SkImageInfo& srcInfo, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, - uint32_t offset, SkScanlineDecoder::SkScanlineOrder rowOrder, + uint32_t offset, SkCodec::SkScanlineOrder rowOrder, size_t RLEBytes); protected: diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp index 59bc917481..02f2d4215a 100644 --- a/src/codec/SkBmpStandardCodec.cpp +++ b/src/codec/SkBmpStandardCodec.cpp @@ -8,7 +8,6 @@ #include "SkBmpStandardCodec.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" /* @@ -18,7 +17,7 @@ SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, uint32_t offset, - SkScanlineDecoder::SkScanlineOrder rowOrder, bool inIco) + SkCodec::SkScanlineOrder rowOrder, bool inIco) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(nullptr) , fNumColors(this->computeNumColors(numColors)) diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h index fcc246f353..0d81cbfee4 100644 --- a/src/codec/SkBmpStandardCodec.h +++ b/src/codec/SkBmpStandardCodec.h @@ -37,7 +37,7 @@ public: */ SkBmpStandardCodec(const SkImageInfo& srcInfo, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, - uint32_t offset, SkScanlineDecoder::SkScanlineOrder rowOrder, + uint32_t offset, SkCodec::SkScanlineOrder rowOrder, bool isIco); protected: diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp index d12de21f51..97bcf3e1d8 100644 --- a/src/codec/SkCodec.cpp +++ b/src/codec/SkCodec.cpp @@ -76,9 +76,12 @@ SkCodec* SkCodec::NewFromData(SkData* data) { } SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream) - : fInfo(info) + : fSrcInfo(info) , fStream(stream) , fNeedsRewind(false) + , fDstInfo() + , fOptions() + , fCurrScanline(-1) {} SkCodec::~SkCodec() {} @@ -92,6 +95,9 @@ bool SkCodec::rewindIfNeeded() { return true; } + // startScanlineDecode will need to be called before decoding scanlines. + fCurrScanline = -1; + if (!fStream->rewind()) { return false; } @@ -148,3 +154,75 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr); } + +SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo, + const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) { + // Reset fCurrScanline in case of failure. + fCurrScanline = -1; + // Ensure that valid color ptrs are passed in for kIndex8 color type + if (kIndex_8_SkColorType == dstInfo.colorType()) { + if (nullptr == ctable || nullptr == ctableCount) { + return SkCodec::kInvalidParameters; + } + } else { + if (ctableCount) { + *ctableCount = 0; + } + ctableCount = nullptr; + ctable = nullptr; + } + + // Set options. + Options optsStorage; + if (nullptr == options) { + options = &optsStorage; + } + + const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount); + if (result != SkCodec::kSuccess) { + return result; + } + + fCurrScanline = 0; + fDstInfo = dstInfo; + fOptions = *options; + return kSuccess; +} + +SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) { + return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr); +} + +SkCodec::Result SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) { + if (fCurrScanline < 0) { + return kScanlineDecodingNotStarted; + } + + SkASSERT(!fDstInfo.isEmpty()); + if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0 + || fCurrScanline + countLines > fDstInfo.height()) { + return kInvalidParameters; + } + + const Result result = this->onGetScanlines(dst, countLines, rowBytes); + fCurrScanline += countLines; + return result; +} + +SkCodec::Result SkCodec::skipScanlines(int countLines) { + if (fCurrScanline < 0) { + return kScanlineDecodingNotStarted; + } + + SkASSERT(!fDstInfo.isEmpty()); + if (fCurrScanline + countLines > fDstInfo.height()) { + // Arguably, we could just skip the scanlines which are remaining, + // and return kSuccess. We choose to return invalid so the client + // can catch their bug. + return SkCodec::kInvalidParameters; + } + + const Result result = this->onSkipScanlines(countLines); + fCurrScanline += countLines; + return result; +} diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp index d8889defaf..ad7fb5b578 100644 --- a/src/codec/SkCodec_libgif.cpp +++ b/src/codec/SkCodec_libgif.cpp @@ -582,124 +582,94 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, 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; - } +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; + } - // 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); - } + // 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); - } + // Initialize the swizzler + if (fFrameIsSubset) { + int sampleX; + SkScaledCodec::ComputeSampleSize(dstInfo, this->getInfo(), &sampleX, NULL); + const SkImageInfo subsetDstInfo = dstInfo.makeWH( + get_scaled_dimension(fFrameDims.width(), sampleX), + fFrameDims.height()); + if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) { + return gif_error("Could not initialize swizzler.\n", kUnimplemented); + } + } else { + if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) { + return gif_error("Could not initialize swizzler.\n", 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<void>(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); - } + return kSuccess; +} - // Adjust dst pointer for left offset - dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel( - this->dstInfo().colorType()) * fCodec->fFrameDims.left()); +SkCodec::Result SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { + if (fFrameIsSubset) { + // Fill the requested rows + const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); + SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fFillIndex, + 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); } - 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<void>(dst, rowBytes); + // Do nothing for rows after the image frame + int rowsAfterFrame = this->INHERITED::nextScanline() + count - fFrameDims.bottom(); + if (rowsAfterFrame > 0) { + count = SkTMin(0, count - rowsAfterFrame); } - return SkCodec::kSuccess; - } - SkScanlineOrder onGetScanlineOrder() const override { - if (fCodec->fGif->Image.Interlace) { - return kOutOfOrder_SkScanlineOrder; - } else { - return kTopDown_SkScanlineOrder; - } + // Adjust dst pointer for left offset + int bpp = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameDims.left(); + dst = SkTAddOffset<void>(dst, bpp); } - int onGetY() const override { - if (fCodec->fGif->Image.Interlace) { - return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo().height()); - } else { - return INHERITED::onGetY(); + 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); } + fSwizzler->swizzle(dst, fSrcBuffer.get()); + dst = SkTAddOffset<void>(dst, rowBytes); } + return SkCodec::kSuccess; +} -private: - SkAutoTDelete<SkGifCodec> fCodec; - - typedef SkScanlineDecoder INHERITED; -}; - -SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFromStream(stream))); - if (!codec) { - return NULL; +SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const { + if (fGif->Image.Interlace) { + return kOutOfOrder_SkScanlineOrder; + } else { + return kTopDown_SkScanlineOrder; } +} - const SkImageInfo& srcInfo = codec->getInfo(); - - return new SkGifScanlineDecoder(srcInfo, codec.detach()); +int SkGifCodec::onNextScanline() const { + if (fGif->Image.Interlace) { + return get_output_row_interlaced(this->INHERITED::onNextScanline(), this->dstInfo().height()); + } else { + return this->INHERITED::onNextScanline(); + } } + diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h index 8845536851..3999c41ede 100644 --- a/src/codec/SkCodec_libgif.h +++ b/src/codec/SkCodec_libgif.h @@ -8,7 +8,6 @@ #include "SkCodec.h" #include "SkColorTable.h" #include "SkImageInfo.h" -#include "SkScanlineDecoder.h" #include "SkSwizzler.h" #include "gif_lib.h" @@ -33,8 +32,6 @@ public: */ static SkCodec* NewFromStream(SkStream*); - static SkScanlineDecoder* NewSDFromStream(SkStream* stream); - protected: /* @@ -83,7 +80,7 @@ private: * @param transIndex This call will set the transparent index based on the * extension data. */ - static SkCodec::Result ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex); + static Result ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex); /* * A gif may contain many image frames, all of different sizes. @@ -114,7 +111,7 @@ private: * Checks for invalid inputs and calls rewindIfNeeded(), setFramDimensions(), and * initializeColorTable() in the proper sequence. */ - SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, + Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, int* inputColorCount, const Options& opts); /* @@ -125,14 +122,22 @@ private: * indicated in the header. * @param zeroInit Indicates if destination memory is zero initialized. */ - SkCodec::Result initializeSwizzler(const SkImageInfo& dstInfo, - ZeroInitialized zeroInit); + Result initializeSwizzler(const SkImageInfo& dstInfo, ZeroInitialized zeroInit); /* * @return kSuccess if the read is successful and kIncompleteInput if the * read fails. */ - SkCodec::Result readRow(); + Result readRow(); + + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opts, + SkPMColor inputColorPtr[], int* inputColorCount) override; + + Result onGetScanlines(void* dst, int count, size_t rowBytes) override; + + SkScanlineOrder onGetScanlineOrder() const override; + + int onNextScanline() const override; /* * This function cleans up the gif object after the decode completes @@ -168,7 +173,5 @@ private: SkAutoTDelete<SkSwizzler> fSwizzler; SkAutoTUnref<SkColorTable> fColorTable; - friend class SkGifScanlineDecoder; - typedef SkCodec INHERITED; }; diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp index 2ba2d5bb9f..faba79f6a2 100644 --- a/src/codec/SkCodec_libpng.cpp +++ b/src/codec/SkCodec_libpng.cpp @@ -12,7 +12,6 @@ #include "SkBitmap.h" #include "SkMath.h" #include "SkScaledCodec.h" -#include "SkScanlineDecoder.h" #include "SkSize.h" #include "SkStream.h" #include "SkSwizzler.h" @@ -163,7 +162,10 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { palette++; } - fReallyHasAlpha = transLessThanFF < 0; + if (transLessThanFF >= 0) { + // No transparent colors were found. + fAlphaState = kOpaque_AlphaState; + } for (; index < numPalette; index++) { *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); @@ -216,7 +218,8 @@ bool SkPngCodec::IsPng(SkStream* stream) { // png_destroy_read_struct. If it returns false, the passed in fields (except // stream) are unchanged. static bool read_header(SkStream* stream, png_structp* png_ptrp, - png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) { + png_infop* info_ptrp, SkImageInfo* imageInfo, + int* bitDepthPtr, int* numberPassesPtr) { // The image is known to be a PNG. Decode enough to know the SkImageInfo. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, sk_warning_fn); @@ -333,6 +336,11 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp, SkASSERT(false); } + int numberPasses = png_set_interlace_handling(png_ptr); + if (numberPassesPtr) { + *numberPassesPtr = numberPasses; + } + // FIXME: Also need to check for sRGB (skbug.com/3471). if (imageInfo) { @@ -349,29 +357,21 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp, return true; } -SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { - SkAutoTDelete<SkStream> streamDeleter(stream); - png_structp png_ptr; - png_infop info_ptr; - SkImageInfo imageInfo; - int bitDepth; - if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) { - return new SkPngCodec(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, bitDepth); - } - return nullptr; -} - -#define INVALID_NUMBER_PASSES -1 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, - png_structp png_ptr, png_infop info_ptr, int bitDepth) + png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) : INHERITED(info, stream) , fPng_ptr(png_ptr) , fInfo_ptr(info_ptr) , fSrcConfig(SkSwizzler::kUnknown) - , fNumberPasses(INVALID_NUMBER_PASSES) - , fReallyHasAlpha(false) + , fNumberPasses(numberPasses) , fBitDepth(bitDepth) -{} +{ + if (info.alphaType() == kOpaque_SkAlphaType) { + fAlphaState = kOpaque_AlphaState; + } else { + fAlphaState = kUnknown_AlphaState; + } +} SkPngCodec::~SkPngCodec() { this->destroyReadStruct(); @@ -401,13 +401,9 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, SkCodecPrintf("setjmp long jump!\n"); return kInvalidInput; } - fNumberPasses = png_set_interlace_handling(fPng_ptr); png_read_update_info(fPng_ptr, fInfo_ptr); - // Set to the default before calling decodePalette, which may change it. - fReallyHasAlpha = false; - - //srcColorType was determined in readHeader() which determined png color type + //srcColorType was determined in read_header() which determined png color type const SkColorType srcColorType = this->getInfo().colorType(); switch (srcColorType) { @@ -459,7 +455,7 @@ bool SkPngCodec::onRewind() { png_structp png_ptr; png_infop info_ptr; - if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr)) { + if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) { return false; } @@ -498,7 +494,8 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* return kInvalidInput; } - SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES); + bool hasAlpha = false; + // FIXME: We could split these out based on subclass. SkAutoMalloc storage; void* dstRow = dst; if (fNumberPasses > 1) { @@ -522,7 +519,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // Now swizzle it. uint8_t* srcRow = base; for (int y = 0; y < height; y++) { - fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); + hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); srcRow += srcRowBytes; } @@ -531,11 +528,18 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* uint8_t* srcRow = static_cast<uint8_t*>(storage.get()); for (int y = 0; y < requestedInfo.height(); y++) { png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); - fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); + // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines. + hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow)); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } } + if (hasAlpha) { + fAlphaState = kHasAlpha_AlphaState; + } else { + fAlphaState = kOpaque_AlphaState; + } + // FIXME: do we need substituteTranspColor? Note that we cannot do it for // scanline decoding, but we could do it here. Alternatively, we could do // it as we go, instead of in post-processing like SkPNGImageDecoder. @@ -547,136 +551,170 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // read rest of file, and get additional comment and time chunks in info_ptr png_read_end(fPng_ptr, fInfo_ptr); + return kSuccess; } -class SkPngScanlineDecoder : public SkScanlineDecoder { +bool SkPngCodec::onReallyHasAlpha() const { + switch (fAlphaState) { + case kOpaque_AlphaState: + return false; + case kUnknown_AlphaState: + // Maybe the subclass knows? + return this->alphaInScanlineDecode() == kHasAlpha_AlphaState; + case kHasAlpha_AlphaState: + switch (this->alphaInScanlineDecode()) { + case kUnknown_AlphaState: + // Scanline decoder must not have been used. Return our knowledge. + return true; + case kOpaque_AlphaState: + // Scanline decoder was used, and did not find alpha in its subset. + return false; + case kHasAlpha_AlphaState: + return true; + } + } + + // All valid AlphaStates have been covered, so this should not be reached. + SkASSERT(false); + return true; +} + +// Subclass of SkPngCodec which supports scanline decoding +class SkPngScanlineDecoder : public SkPngCodec { public: - SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) - : INHERITED(srcInfo) - , fCodec(codec) - , fHasAlpha(false) + SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, + png_structp png_ptr, png_infop info_ptr, int bitDepth) + : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1) + , fSrcRow(nullptr) + , fAlphaState(kUnknown_AlphaState) {} - SkCodec::Result onStart(const SkImageInfo& dstInfo, - const SkCodec::Options& options, - SkPMColor ctable[], int* ctableCount) override { - if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, + SkPMColor ctable[], int* ctableCount) override { + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; } if (!conversion_possible(dstInfo, this->getInfo())) { - return SkCodec::kInvalidConversion; + return kInvalidConversion; } // Check to see if scaling was requested. if (dstInfo.dimensions() != this->getInfo().dimensions()) { if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return SkCodec::kInvalidScale; + return kInvalidScale; } } - const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, - ctableCount); - if (result != SkCodec::kSuccess) { + const Result result = this->initializeSwizzler(dstInfo, options, ctable, + ctableCount); + if (result != kSuccess) { return result; } - fHasAlpha = false; - fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); + fAlphaState = kUnknown_AlphaState; + fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig())); fSrcRow = static_cast<uint8_t*>(fStorage.get()); - return SkCodec::kSuccess; + return kSuccess; } - SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { - if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { + Result onGetScanlines(void* dst, int count, size_t rowBytes) override { + if (setjmp(png_jmpbuf(this->png_ptr()))) { SkCodecPrintf("setjmp long jump!\n"); - return SkCodec::kInvalidInput; + return kInvalidInput; } void* dstRow = dst; + bool hasAlpha = false; for (int i = 0; i < count; i++) { - png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); - fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow)); + png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); + hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow)); dstRow = SkTAddOffset<void>(dstRow, rowBytes); } - return SkCodec::kSuccess; + + if (hasAlpha) { + fAlphaState = kHasAlpha_AlphaState; + } else { + if (kUnknown_AlphaState == fAlphaState) { + fAlphaState = kOpaque_AlphaState; + } + // Otherwise, the AlphaState is unchanged. + } + + return kSuccess; } - SkCodec::Result onSkipScanlines(int count) override { + Result onSkipScanlines(int count) override { // FIXME: Could we use the return value of setjmp to specify the type of // error? - if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { + if (setjmp(png_jmpbuf(this->png_ptr()))) { SkCodecPrintf("setjmp long jump!\n"); - return SkCodec::kInvalidInput; + return kInvalidInput; } //there is a potential tradeoff of memory vs speed created by putting this in a loop. //calling png_read_rows in a loop is insignificantly slower than calling it once with count //as png_read_rows has it's own loop which calls png_read_row count times. for (int i = 0; i < count; i++) { - png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1); + png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); } return SkCodec::kSuccess; } - bool onReallyHasAlpha() const override { return fHasAlpha; } - - SkEncodedFormat onGetEncodedFormat() const override { - return kPNG_SkEncodedFormat; + AlphaState alphaInScanlineDecode() const override { + return fAlphaState; } - private: - SkAutoTDelete<SkPngCodec> fCodec; - bool fHasAlpha; + AlphaState fAlphaState; SkAutoMalloc fStorage; uint8_t* fSrcRow; - typedef SkScanlineDecoder INHERITED; + typedef SkPngCodec INHERITED; }; -class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { +class SkPngInterlacedScanlineDecoder : public SkPngCodec { public: - SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) - : INHERITED(srcInfo) - , fCodec(codec) - , fHasAlpha(false) - , fCurrentRow(0) - , fHeight(srcInfo.height()) + SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, + png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) + : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses) + , fAlphaState(kUnknown_AlphaState) + , fHeight(-1) , fCanSkipRewind(false) - {} + { + SkASSERT(numberPasses != 1); + } - SkCodec::Result onStart(const SkImageInfo& dstInfo, - const SkCodec::Options& options, - SkPMColor ctable[], int* ctableCount) override + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, + SkPMColor ctable[], int* ctableCount) override { - if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; } if (!conversion_possible(dstInfo, this->getInfo())) { - return SkCodec::kInvalidConversion; + return kInvalidConversion; } // Check to see if scaling was requested. if (dstInfo.dimensions() != this->getInfo().dimensions()) { if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return SkCodec::kInvalidScale; + return kInvalidScale; } } - const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, - ctableCount); - if (result != SkCodec::kSuccess) { + const Result result = this->initializeSwizzler(dstInfo, options, ctable, + ctableCount); + if (result != kSuccess) { return result; } - fHasAlpha = false; - fCurrentRow = 0; + fAlphaState = kUnknown_AlphaState; fHeight = dstInfo.height(); - fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig); + // FIXME: This need not be called on a second call to onStartScanlineDecode. + fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()); fGarbageRow.reset(fSrcRowBytes); fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); fCanSkipRewind = true; @@ -684,73 +722,90 @@ public: return SkCodec::kSuccess; } - SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { + Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { // rewind stream if have previously called onGetScanlines, // since we need entire progressive image to get scanlines if (fCanSkipRewind) { - // We already rewound in onStart, so there is no reason to rewind. + // We already rewound in onStartScanlineDecode, so there is no reason to rewind. // Next time onGetScanlines is called, we will need to rewind. fCanSkipRewind = false; - } else if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; + } else { + // rewindIfNeeded resets fCurrScanline, since it assumes that start + // needs to be called again before scanline decoding. PNG scanline + // decoding is the exception, since it needs to rewind between + // calls to getScanlines. Keep track of fCurrScanline, to undo the + // reset. + const int currScanline = this->onNextScanline(); + // This method would never be called if currScanline is -1 + SkASSERT(currScanline != -1); + + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; + } + this->updateNextScanline(currScanline); } - if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { + if (setjmp(png_jmpbuf(this->png_ptr()))) { SkCodecPrintf("setjmp long jump!\n"); - return SkCodec::kInvalidInput; + return kInvalidInput; } - const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr); SkAutoMalloc storage(count * fSrcRowBytes); uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); uint8_t* srcRow; - for (int i = 0; i < number_passes; i++) { - //read rows we planned to skip into garbage row - for (int y = 0; y < fCurrentRow; y++){ - png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1); + const int startRow = this->onNextScanline(); + for (int i = 0; i < this->numberPasses(); i++) { + // read rows we planned to skip into garbage row + for (int y = 0; y < startRow; y++){ + png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); } - //read rows we care about into buffer + // read rows we care about into buffer srcRow = storagePtr; for (int y = 0; y < count; y++) { - png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1); + png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); srcRow += fSrcRowBytes; } - //read rows we don't want into garbage buffer - for (int y = 0; y < fHeight - fCurrentRow - count; y++) { - png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1); + // read rows we don't want into garbage buffer + for (int y = 0; y < fHeight - startRow - count; y++) { + png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); } } //swizzle the rows we care about srcRow = storagePtr; void* dstRow = dst; + bool hasAlpha = false; for (int y = 0; y < count; y++) { - fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow)); + hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow)); dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); srcRow += fSrcRowBytes; } - fCurrentRow += count; - return SkCodec::kSuccess; + + if (hasAlpha) { + fAlphaState = kHasAlpha_AlphaState; + } else { + if (kUnknown_AlphaState == fAlphaState) { + fAlphaState = kOpaque_AlphaState; + } + // Otherwise, the AlphaState is unchanged. + } + + return kSuccess; } SkCodec::Result onSkipScanlines(int count) override { - //when ongetScanlines is called it will skip to fCurrentRow - fCurrentRow += count; + // The non-virtual version will update fCurrScanline. return SkCodec::kSuccess; } - bool onReallyHasAlpha() const override { return fHasAlpha; } + AlphaState alphaInScanlineDecode() const override { + return fAlphaState; + } SkScanlineOrder onGetScanlineOrder() const override { return kNone_SkScanlineOrder; } - SkEncodedFormat onGetEncodedFormat() const override { - return kPNG_SkEncodedFormat; - } - private: - SkAutoTDelete<SkPngCodec> fCodec; - bool fHasAlpha; - int fCurrentRow; + AlphaState fAlphaState; int fHeight; size_t fSrcRowBytes; SkAutoMalloc fGarbageRow; @@ -759,30 +814,33 @@ private: // is called whenever some action is taken that reads the stream and // therefore the next call will require a rewind. So it modifies a boolean // to note that the *next* time it is called a rewind is needed. - // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart - // followed by onGetScanlines does *not* require a rewind. Since - // rewindIfNeeded does not have this flexibility, we need to add another - // layer. + // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling + // onStartScanlineDecode followed by onGetScanlines does *not* require a + // rewind. Since rewindIfNeeded does not have this flexibility, we need to + // add another layer. bool fCanSkipRewind; - typedef SkScanlineDecoder INHERITED; + typedef SkPngCodec INHERITED; }; -SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream))); - if (!codec) { +SkCodec* SkPngCodec::NewFromStream(SkStream* stream) { + SkAutoTDelete<SkStream> streamDeleter(stream); + png_structp png_ptr; + png_infop info_ptr; + SkImageInfo imageInfo; + int bitDepth; + int numberPasses; + + if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) { return nullptr; } - codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr); - SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES); - - const SkImageInfo& srcInfo = codec->getInfo(); - if (codec->fNumberPasses > 1) { - // interlaced image - return new SkPngInterlacedScanlineDecoder(srcInfo, codec.detach()); + if (1 == numberPasses) { + return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, + bitDepth); } - return new SkPngScanlineDecoder(srcInfo, codec.detach()); + return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, + info_ptr, bitDepth, numberPasses); } diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h index e8807499ea..8ef1ae2c17 100644 --- a/src/codec/SkCodec_libpng.h +++ b/src/codec/SkCodec_libpng.h @@ -36,7 +36,31 @@ protected: override; SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; } bool onRewind() override; - bool onReallyHasAlpha() const override { return fReallyHasAlpha; } + bool onReallyHasAlpha() const final; + + // Helper to set up swizzler and color table. Also calls png_read_update_info. + Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&, + SkPMColor*, int* ctableCount); + + SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int, int); + + png_structp png_ptr() { return fPng_ptr; } + SkSwizzler* swizzler() { return fSwizzler; } + SkSwizzler::SrcConfig srcConfig() const { return fSrcConfig; } + int numberPasses() const { return fNumberPasses; } + + enum AlphaState { + // This class has done no decoding, or threw away its knowledge (in + // scanline decodes). + kUnknown_AlphaState, + // This class found the image (possibly partial, in the case of a + // scanline decode) to be opaque. + kOpaque_AlphaState, + // Ths class found the image to have alpha. + kHasAlpha_AlphaState, + }; + + virtual AlphaState alphaInScanlineDecode() const = 0; private: png_structp fPng_ptr; @@ -47,21 +71,13 @@ private: SkAutoTDelete<SkSwizzler> fSwizzler; SkSwizzler::SrcConfig fSrcConfig; - int fNumberPasses; - bool fReallyHasAlpha; + const int fNumberPasses; int fBitDepth; - SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int); - - // Helper to set up swizzler and color table. Also calls png_read_update_info. - Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&, - SkPMColor*, int* ctableCount); + AlphaState fAlphaState; bool decodePalette(bool premultiply, int* ctableCount); void destroyReadStruct(); - friend class SkPngScanlineDecoder; - friend class SkPngInterlacedScanlineDecoder; - typedef SkCodec INHERITED; }; diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp index 22f2bacce0..0c85eada7c 100644 --- a/src/codec/SkCodec_wbmp.cpp +++ b/src/codec/SkCodec_wbmp.cpp @@ -96,6 +96,8 @@ SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) { SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream) : INHERITED(info, stream) , fSrcRowBytes(get_src_row_bytes(this->getInfo().width())) + , fColorTable(nullptr) + , fSwizzler(nullptr) {} SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const { @@ -120,7 +122,7 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info, } if (!valid_alpha(info.alphaType(), this->getInfo().alphaType())) { - return SkCodec::kInvalidConversion; + return kInvalidConversion; } // Prepare a color table if necessary @@ -163,90 +165,55 @@ SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) { return new SkWbmpCodec(info, streamDeleter.detach()); } -class SkWbmpScanlineDecoder : public SkScanlineDecoder { -public: - /* - * Takes ownership of all pointer paramters. - */ - SkWbmpScanlineDecoder(SkWbmpCodec* codec) - : INHERITED(codec->getInfo()) - , fCodec(codec) - , fColorTable(nullptr) - , fSwizzler(nullptr) - , fSrcBuffer(codec->fSrcRowBytes) - {} - - SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override { - void* dstRow = dst; - for (int y = 0; y < count; ++y) { - SkCodec::Result rowResult = fCodec->readRow(fSrcBuffer.get()); - if (SkCodec::kSuccess != rowResult) { - return rowResult; - } - fSwizzler->swizzle(dstRow, fSrcBuffer.get()); - dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); +SkCodec::Result SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { + void* dstRow = dst; + for (int y = 0; y < count; ++y) { + Result rowResult = this->readRow(fSrcBuffer.get()); + if (kSuccess != rowResult) { + return rowResult; } - return SkCodec::kSuccess; + fSwizzler->swizzle(dstRow, fSrcBuffer.get()); + dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); } + return kSuccess; +} - SkCodec::Result onStart(const SkImageInfo& dstInfo, - const SkCodec::Options& options, SkPMColor inputColorTable[], - int* inputColorCount) { - if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; - } - if (options.fSubset) { - // Subsets are not supported. - return SkCodec::kUnimplemented; - } - if (dstInfo.dimensions() != this->getInfo().dimensions()) { - if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return SkCodec::kInvalidScale; - } - } - - if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { - return SkCodec::kInvalidConversion; - } - - // Fill in the color table - setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount); - - // Copy the color table to a pointer that can be owned by the scanline decoder - if (kIndex_8_SkColorType == dstInfo.colorType()) { - fColorTable.reset(new SkColorTable(inputColorTable, 2)); - } - - // Initialize the swizzler - fSwizzler.reset(fCodec->initializeSwizzler(dstInfo, - get_color_ptr(fColorTable.get()), options)); - if (nullptr == fSwizzler.get()) { - return SkCodec::kInvalidConversion; +SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, + const Options& options, SkPMColor inputColorTable[], int* inputColorCount) { + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; + } + if (options.fSubset) { + // Subsets are not supported. + return kUnimplemented; + } + if (dstInfo.dimensions() != this->getInfo().dimensions()) { + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return kInvalidScale; } - - return SkCodec::kSuccess; } - SkEncodedFormat onGetEncodedFormat() const { - return kWBMP_SkEncodedFormat; + if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { + return kInvalidConversion; } -private: - SkAutoTDelete<SkWbmpCodec> fCodec; - SkAutoTUnref<SkColorTable> fColorTable; - SkAutoTDelete<SkSwizzler> fSwizzler; - SkAutoTMalloc<uint8_t> fSrcBuffer; + // Fill in the color table + setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount); - typedef SkScanlineDecoder INHERITED; -}; + // Copy the color table to a pointer that can be owned by the scanline decoder + if (kIndex_8_SkColorType == dstInfo.colorType()) { + fColorTable.reset(new SkColorTable(inputColorTable, 2)); + } -SkScanlineDecoder* SkWbmpCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete<SkWbmpCodec> codec(static_cast<SkWbmpCodec*>( - SkWbmpCodec::NewFromStream(stream))); - if (!codec) { - return nullptr; + // Initialize the swizzler + fSwizzler.reset(this->initializeSwizzler(dstInfo, + get_color_ptr(fColorTable.get()), options)); + if (nullptr == fSwizzler.get()) { + return kInvalidConversion; } - // Return the new scanline decoder - return new SkWbmpScanlineDecoder(codec.detach()); + fSrcBuffer.reset(fSrcRowBytes); + + return kSuccess; } + diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h index 0891eb86b9..220570b0db 100644 --- a/src/codec/SkCodec_wbmp.h +++ b/src/codec/SkCodec_wbmp.h @@ -9,7 +9,6 @@ #define SkCodec_wbmp_DEFINED #include "SkCodec.h" -#include "SkScanlineDecoder.h" #include "SkSwizzler.h" class SkWbmpCodec final : public SkCodec { @@ -23,12 +22,6 @@ public: */ static SkCodec* NewFromStream(SkStream*); - /* - * Assumes IsWbmp was called and returned true - * Creates a wbmp scanline decoder - * Takes ownership of the stream - */ - static SkScanlineDecoder* NewSDFromStream(SkStream*); protected: SkEncodedFormat onGetEncodedFormat() const override; Result onGetPixels(const SkImageInfo&, void*, size_t, @@ -50,7 +43,16 @@ private: const size_t fSrcRowBytes; - friend class SkWbmpScanlineDecoder; + // Used for scanline decodes: + SkAutoTUnref<SkColorTable> fColorTable; + SkAutoTDelete<SkSwizzler> fSwizzler; + SkAutoTMalloc<uint8_t> fSrcBuffer; + + // FIXME: Override onSkipScanlines to avoid swizzling. + Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override; + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, + SkPMColor inputColorTable[], int* inputColorCount) override; + typedef SkCodec INHERITED; }; diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 4557e45673..dac0c17b77 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -12,7 +12,6 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkScaledCodec.h" -#include "SkScanlineDecoder.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkTypes.h" @@ -382,160 +381,136 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, return kSuccess; } -/* - * Enable scanline decoding for jpegs - */ -class SkJpegScanlineDecoder : public SkScanlineDecoder { -public: - SkJpegScanlineDecoder(const SkImageInfo& srcInfo, SkJpegCodec* codec) - : INHERITED(srcInfo) - , fCodec(codec) - , fOpts() - {} - - /* - * Return a valid set of output dimensions for this decoder, given an input scale - */ - SkISize onGetScaledDimensions(float desiredScale) override { - return fCodec->onGetScaledDimensions(desiredScale); +SkCodec::Result SkJpegCodec::initializeSwizzler(const SkImageInfo& info, const Options& options) { + SkSwizzler::SrcConfig srcConfig; + switch (info.colorType()) { + case kGray_8_SkColorType: + srcConfig = SkSwizzler::kGray; + break; + case kRGBA_8888_SkColorType: + srcConfig = SkSwizzler::kRGBX; + break; + case kBGRA_8888_SkColorType: + srcConfig = SkSwizzler::kBGRX; + break; + case kRGB_565_SkColorType: + srcConfig = SkSwizzler::kRGB_565; + break; + default: + // This function should only be called if the colorType is supported by jpeg + SkASSERT(false); } - /* - * Create the swizzler based on the encoded format. - * The swizzler is only used for sampling in the x direction. - */ - - SkCodec::Result initializeSwizzler(const SkImageInfo& info, const SkCodec::Options& options) { - SkSwizzler::SrcConfig srcConfig; - switch (info.colorType()) { - case kGray_8_SkColorType: - srcConfig = SkSwizzler::kGray; - break; - case kRGBA_8888_SkColorType: - srcConfig = SkSwizzler::kRGBX; - break; - case kBGRA_8888_SkColorType: - srcConfig = SkSwizzler::kBGRX; - break; - case kRGB_565_SkColorType: - srcConfig = SkSwizzler::kRGB_565; - break; - default: - //would have exited before now if the colorType was supported by jpeg - SkASSERT(false); - } - - fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info, options.fZeroInitialized, - this->getInfo())); - if (!fSwizzler) { - // FIXME: CreateSwizzler could fail for another reason. - return SkCodec::kUnimplemented; - } - return SkCodec::kSuccess; + fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info, options.fZeroInitialized, + this->getInfo())); + if (!fSwizzler) { + return SkCodec::kUnimplemented; } - SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, - SkPMColor ctable[], int* ctableCount) override { + return kSuccess; +} - // Rewind the stream if needed - if (!fCodec->rewindIfNeeded()) { - return SkCodec::kCouldNotRewind; - } +SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, + const Options& options, SkPMColor ctable[], int* ctableCount) { + // Rewind the stream if needed + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; + } - // Set the jump location for libjpeg errors - if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { - SkCodecPrintf("setjmp: Error from libjpeg\n"); - return SkCodec::kInvalidInput; - } + // Set the jump location for libjpeg errors + if (setjmp(fDecoderMgr->getJmpBuf())) { + SkCodecPrintf("setjmp: Error from libjpeg\n"); + return kInvalidInput; + } - // Check if we can decode to the requested destination and set the output color space - if (!fCodec->setOutputColorSpace(dstInfo)) { - return SkCodec::kInvalidConversion; - } + // Check if we can decode to the requested destination and set the output color space + if (!this->setOutputColorSpace(dstInfo)) { + return kInvalidConversion; + } - // Perform the necessary scaling - if (!fCodec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) { - // full native scaling to dstInfo dimensions not supported + // Perform the necessary scaling + if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) { + // full native scaling to dstInfo dimensions not supported - if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { - return SkCodec::kInvalidScale; - } - // create swizzler for sampling - SkCodec::Result result = this->initializeSwizzler(dstInfo, options); - if (SkCodec::kSuccess != result) { - SkCodecPrintf("failed to initialize the swizzler.\n"); - return result; - } - fStorage.reset(get_row_bytes(fCodec->fDecoderMgr->dinfo())); - fSrcRow = static_cast<uint8_t*>(fStorage.get()); - } else { - fSrcRow = nullptr; - fSwizzler.reset(nullptr); + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return kInvalidScale; } - - // Now, given valid output dimensions, we can start the decompress - if (!jpeg_start_decompress(fCodec->fDecoderMgr->dinfo())) { - SkCodecPrintf("start decompress failed\n"); - return SkCodec::kInvalidInput; + // create swizzler for sampling + Result result = this->initializeSwizzler(dstInfo, options); + if (kSuccess != result) { + SkCodecPrintf("failed to initialize the swizzler.\n"); + return result; } + fStorage.reset(get_row_bytes(fDecoderMgr->dinfo())); + fSrcRow = static_cast<uint8_t*>(fStorage.get()); + } else { + fSrcRow = nullptr; + fSwizzler.reset(nullptr); + } + + // Now, given valid output dimensions, we can start the decompress + if (!jpeg_start_decompress(fDecoderMgr->dinfo())) { + SkCodecPrintf("start decompress failed\n"); + return kInvalidInput; + } - fOpts = options; + return kSuccess; +} - return SkCodec::kSuccess; +SkJpegCodec::~SkJpegCodec() { + // FIXME: This probably does not need to be called after a full decode + // FIXME: Is it safe to call when it doesn't need to be called? + if (setjmp(fDecoderMgr->getJmpBuf())) { + SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); + return; } - virtual ~SkJpegScanlineDecoder() { - if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { - SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); - return; - } + // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a + // partial decode. + fDecoderMgr->dinfo()->output_scanline = this->getInfo().height(); + jpeg_finish_decompress(fDecoderMgr->dinfo()); +} - // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a - // partial decode. - fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height(); - jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo()); +SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { + // Set the jump location for libjpeg errors + if (setjmp(fDecoderMgr->getJmpBuf())) { + return fDecoderMgr->returnFailure("setjmp", kInvalidInput); + } + // Read rows one at a time + JSAMPLE* dstRow; + if (fSwizzler) { + // write data to storage row, then sample using swizzler + dstRow = fSrcRow; + } else { + // write data directly to dst + dstRow = (JSAMPLE*) dst; } - SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { - // Set the jump location for libjpeg errors - if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { - return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvalidInput); - } - // Read rows one at a time - JSAMPLE* dstRow; - if (fSwizzler) { - // write data to storage row, then sample using swizzler - dstRow = fSrcRow; - } else { - // write data directly to dst - dstRow = (JSAMPLE*) dst; + for (int y = 0; y < count; y++) { + // Read row of the image + uint32_t rowsDecoded = jpeg_read_scanlines(fDecoderMgr->dinfo(), &dstRow, 1); + if (rowsDecoded != 1) { + SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, count - y, + SK_ColorBLACK, nullptr, this->options().fZeroInitialized); + fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height(); + return kIncompleteInput; } - for (int y = 0; y < count; y++) { - // Read row of the image - uint32_t rowsDecoded = jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dstRow, 1); - if (rowsDecoded != 1) { - SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, count - y, - SK_ColorBLACK, nullptr, fOpts.fZeroInitialized); - fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height(); - return SkCodec::kIncompleteInput; - } - - // Convert to RGBA if necessary - if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) { - convert_CMYK_to_RGBA(dstRow, fCodec->fDecoderMgr->dinfo()->output_width); - } + // Convert to RGBA if necessary + if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { + convert_CMYK_to_RGBA(dstRow, fDecoderMgr->dinfo()->output_width); + } - if(fSwizzler) { - // use swizzler to sample row - fSwizzler->swizzle(dst, dstRow); - dst = SkTAddOffset<JSAMPLE>(dst, rowBytes); - } else { - dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); - } + if(fSwizzler) { + // use swizzler to sample row + fSwizzler->swizzle(dst, dstRow); + dst = SkTAddOffset<JSAMPLE>(dst, rowBytes); + } else { + dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); } - return SkCodec::kSuccess; } + return kSuccess; +} #ifndef TURBO_HAS_SKIP // TODO (msarett): Make this a member function and avoid reallocating the @@ -548,39 +523,14 @@ public: } #endif - SkCodec::Result onSkipScanlines(int count) override { - // Set the jump location for libjpeg errors - if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { - return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvalidInput); - } - - jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count); - - return SkCodec::kSuccess; - } - - SkEncodedFormat onGetEncodedFormat() const override { - return kJPEG_SkEncodedFormat; - } - -private: - SkAutoTDelete<SkJpegCodec> fCodec; - SkAutoMalloc fStorage; // Only used if sampling is needed - uint8_t* fSrcRow; // Only used if sampling is needed - SkCodec::Options fOpts; - SkAutoTDelete<SkSwizzler> fSwizzler; - - typedef SkScanlineDecoder INHERITED; -}; - -SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) { - SkAutoTDelete<SkJpegCodec> codec(static_cast<SkJpegCodec*>(SkJpegCodec::NewFromStream(stream))); - if (!codec) { - return nullptr; +SkCodec::Result SkJpegCodec::onSkipScanlines(int count) { + // Set the jump location for libjpeg errors + if (setjmp(fDecoderMgr->getJmpBuf())) { + return fDecoderMgr->returnFailure("setjmp", kInvalidInput); } - const SkImageInfo& srcInfo = codec->getInfo(); + jpeg_skip_scanlines(fDecoderMgr->dinfo(), count); - // Return the new scanline decoder - return new SkJpegScanlineDecoder(srcInfo, codec.detach()); + return kSuccess; } + diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h index 38e41e757c..b86396317b 100644 --- a/src/codec/SkJpegCodec.h +++ b/src/codec/SkJpegCodec.h @@ -18,8 +18,6 @@ extern "C" { #include "jpeglib.h" } -class SkScanlineDecoder; - /* * * This class implements the decoding for jpeg images @@ -41,13 +39,6 @@ public: */ static SkCodec* NewFromStream(SkStream*); - /* - * Assumes IsJpeg was called and returned true - * Creates a jpeg scanline decoder - * Takes ownership of the stream - */ - static SkScanlineDecoder* NewSDFromStream(SkStream*); - protected: /* @@ -102,6 +93,8 @@ private: */ SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, JpegDecoderMgr* decoderMgr); + ~SkJpegCodec() override; + /* * Checks if the conversion between the input image and the requested output * image has been implemented @@ -115,13 +108,23 @@ private: */ bool nativelyScaleToDimensions(uint32_t width, uint32_t height); + // scanline decoding + Result initializeSwizzler(const SkImageInfo&, const SkCodec::Options&); + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, + SkPMColor ctable[], int* ctableCount) override; + Result onGetScanlines(void* dst, int count, size_t rowBytes) override; + Result onSkipScanlines(int count) override; + SkAutoTDelete<JpegDecoderMgr> fDecoderMgr; // We will save the state of the decompress struct after reading the header. // This allows us to safely call onGetScaledDimensions() at any time. const int fReadyState; - - friend class SkJpegScanlineDecoder; + // scanline decoding + SkAutoMalloc fStorage; // Only used if sampling is needed + uint8_t* fSrcRow; // Only used if sampling is needed + SkAutoTDelete<SkSwizzler> fSwizzler; + typedef SkCodec INHERITED; }; diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp index 951ab27b1c..36aeda98cf 100644 --- a/src/codec/SkScaledCodec.cpp +++ b/src/codec/SkScaledCodec.cpp @@ -21,13 +21,13 @@ SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { return SkWebpCodec::NewFromStream(stream); } - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromStream(stream)); - if (nullptr == scanlineDecoder) { + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); + if (nullptr == codec) { return nullptr; } // wrap in new SkScaledCodec - return new SkScaledCodec(scanlineDecoder.detach()); + return new SkScaledCodec(codec.detach()); } SkCodec* SkScaledCodec::NewFromData(SkData* data) { @@ -37,9 +37,9 @@ SkCodec* SkScaledCodec::NewFromData(SkData* data) { return NewFromStream(new SkMemoryStream(data)); } -SkScaledCodec::SkScaledCodec(SkScanlineDecoder* scanlineDecoder) - : INHERITED(scanlineDecoder->getInfo(), nullptr) - , fScanlineDecoder(scanlineDecoder) +SkScaledCodec::SkScaledCodec(SkCodec* codec) + : INHERITED(codec->getInfo(), nullptr) + , fCodec(codec) {} SkScaledCodec::~SkScaledCodec() {} @@ -78,12 +78,12 @@ static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na * Return a valid set of output dimensions for this decoder, given an input scale */ SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { - SkISize nativeDimensions = fScanlineDecoder->getScaledDimensions(desiredScale); + SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale); // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... SkISize scaledCodecDimensions; if (desiredScale > 0.5f) { // sampleSize = 1 - scaledCodecDimensions = fScanlineDecoder->getInfo().dimensions(); + scaledCodecDimensions = fCodec->getInfo().dimensions(); } // sampleSize determines the step size between samples // Ex: sampleSize = 2, sample every second pixel in x and y directions @@ -185,19 +185,20 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi return kUnimplemented; } - Result result = fScanlineDecoder->start(requestedInfo, &options, ctable, ctableCount); + // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels. + Result result = fCodec->startScanlineDecode(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: { + switch (fCodec->getScanlineOrder()) { + case SkCodec::kTopDown_SkScanlineOrder: + case SkCodec::kBottomUp_SkScanlineOrder: + case SkCodec::kNone_SkScanlineOrder: + return fCodec->getScanlines(dst, requestedInfo.height(), rowBytes); + case SkCodec::kOutOfOrder_SkScanlineOrder: { for (int y = 0; y < requestedInfo.height(); y++) { - int dstY = fScanlineDecoder->getY(); + int dstY = fCodec->nextScanline(); void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY); - result = fScanlineDecoder->getScanlines(dstPtr, 1, rowBytes); + result = fCodec->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) { @@ -213,42 +214,44 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi // no scaling requested return result; } - + // scaling requested int sampleX; int sampleY; - if (!scaling_supported(requestedInfo, fScanlineDecoder->getInfo(), &sampleX, &sampleY)) { + if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY)) { return kInvalidScale; } // set first sample pixel in y direction int Y0 = get_start_coord(sampleY); int dstHeight = requestedInfo.height(); - int srcHeight = fScanlineDecoder->getInfo().height(); + int srcHeight = fCodec->getInfo().height(); SkImageInfo info = requestedInfo; - // use original height as scanlineDecoder does not support y sampling natively + // use original height as codec does not support y sampling natively info = info.makeWH(requestedInfo.width(), srcHeight); - // update scanlineDecoder with new info - result = fScanlineDecoder->start(info, &options, ctable, ctableCount); + // update codec with new info + // FIXME: The previous call to start returned kInvalidScale. This call may + // require a rewind. (skbug.com/4284) + result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount); if (kSuccess != result) { return result; } - switch(fScanlineDecoder->getScanlineOrder()) { - case SkScanlineDecoder::kTopDown_SkScanlineOrder: { - result = fScanlineDecoder->skipScanlines(Y0); + switch(fCodec->getScanlineOrder()) { + case SkCodec::kTopDown_SkScanlineOrder: { + result = fCodec->skipScanlines(Y0); if (kSuccess != result && kIncompleteInput != result) { return result; } for (int y = 0; y < dstHeight; y++) { - result = fScanlineDecoder->getScanlines(dst, 1, rowBytes); + result = fCodec->getScanlines(dst, 1, rowBytes); if (kSuccess != result && kIncompleteInput != result) { return result; } if (y < dstHeight - 1) { - result = fScanlineDecoder->skipScanlines(sampleY - 1); + result = fCodec->skipScanlines(sampleY - 1); if (kSuccess != result && kIncompleteInput != result) { return result; } @@ -257,18 +260,18 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi } return result; } - case SkScanlineDecoder::kBottomUp_SkScanlineOrder: - case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: { + case SkCodec::kBottomUp_SkScanlineOrder: + case SkCodec::kOutOfOrder_SkScanlineOrder: { for (int y = 0; y < srcHeight; y++) { - int srcY = fScanlineDecoder->getY(); + int srcY = fCodec->nextScanline(); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY)); - result = fScanlineDecoder->getScanlines(dstPtr, 1, rowBytes); + result = fCodec->getScanlines(dstPtr, 1, rowBytes); if (kSuccess != result && kIncompleteInput != result) { return result; } } else { - result = fScanlineDecoder->skipScanlines(1); + result = fCodec->skipScanlines(1); if (kSuccess != result && kIncompleteInput != result) { return result; } @@ -276,10 +279,10 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi } return result; } - case SkScanlineDecoder::kNone_SkScanlineOrder: { + case SkCodec::kNone_SkScanlineOrder: { SkAutoMalloc storage(srcHeight * rowBytes); uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); - result = fScanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes); + result = fCodec->getScanlines(storagePtr, srcHeight, rowBytes); if (kSuccess != result && kIncompleteInput != result) { return result; } diff --git a/src/codec/SkScanlineDecoder.cpp b/src/codec/SkScanlineDecoder.cpp deleted file mode 100644 index e011507f80..0000000000 --- a/src/codec/SkScanlineDecoder.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkScanlineDecoder.h" -#include "SkBmpCodec.h" -#include "SkCodec_libgif.h" -#include "SkCodec_libpng.h" -#include "SkCodec_wbmp.h" -#include "SkCodecPriv.h" -#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK -#include "SkJpegCodec.h" -#endif - -struct DecoderProc { - bool (*IsFormat)(SkStream*); - SkScanlineDecoder* (*NewFromStream)(SkStream*); -}; - -static const DecoderProc gDecoderProcs[] = { - { SkPngCodec::IsPng, SkPngCodec::NewSDFromStream }, -#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK - { SkJpegCodec::IsJpeg, SkJpegCodec::NewSDFromStream }, -#endif - { SkGifCodec::IsGif, SkGifCodec::NewSDFromStream }, - { SkBmpCodec::IsBmp, SkBmpCodec::NewSDFromStream }, - { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewSDFromStream }, -}; - -SkScanlineDecoder* SkScanlineDecoder::NewFromStream(SkStream* stream) { - if (!stream) { - return nullptr; - } - - SkAutoTDelete<SkStream> streamDeleter(stream); - - SkAutoTDelete<SkScanlineDecoder> codec(nullptr); - for (uint32_t i = 0; i < SK_ARRAY_COUNT(gDecoderProcs); i++) { - DecoderProc proc = gDecoderProcs[i]; - const bool correctFormat = proc.IsFormat(stream); - if (!stream->rewind()) { - return nullptr; - } - if (correctFormat) { - codec.reset(proc.NewFromStream(streamDeleter.detach())); - break; - } - } - - // Set the max size at 128 megapixels (512 MB for kN32). - // This is about 4x smaller than a test image that takes a few minutes for - // dm to decode and draw. - const int32_t maxSize = 1 << 27; - if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) { - SkCodecPrintf("Error: Image size too large, cannot decode.\n"); - return nullptr; - } else { - return codec.detach(); - } -} - -SkScanlineDecoder* SkScanlineDecoder::NewFromData(SkData* data) { - if (!data) { - return nullptr; - } - return NewFromStream(new SkMemoryStream(data)); -} - - -SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo, - const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) { - // Ensure that valid color ptrs are passed in for kIndex8 color type - if (kIndex_8_SkColorType == dstInfo.colorType()) { - if (nullptr == ctable || nullptr == ctableCount) { - return SkCodec::kInvalidParameters; - } - } else { - if (ctableCount) { - *ctableCount = 0; - } - ctableCount = nullptr; - ctable = nullptr; - } - - // Set options. - SkCodec::Options optsStorage; - if (nullptr == options) { - options = &optsStorage; - } - - const SkCodec::Result result = this->onStart(dstInfo, *options, ctable, ctableCount); - if (result != SkCodec::kSuccess) { - return result; - } - - fCurrScanline = 0; - fDstInfo = dstInfo; - fOptions = *options; - return SkCodec::kSuccess; -} - -SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo) { - return this->start(dstInfo, nullptr, nullptr, nullptr); -} - diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp index 2ea6eda57e..f1c5bbaa12 100644 --- a/tests/CodexTest.cpp +++ b/tests/CodexTest.cpp @@ -11,7 +11,6 @@ #include "SkMD5.h" #include "SkRandom.h" #include "SkScaledCodec.h" -#include "SkScanlineDecoder.h" #include "Test.h" static SkStreamAsset* resource(const char path[]) { @@ -147,25 +146,44 @@ static void check(skiatest::Reporter* r, // Scanline decoding follows. - stream.reset(resource(path)); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( - SkScanlineDecoder::NewFromStream(stream.detach())); + // Need to call start() first. + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == SkCodec::kScanlineDecodingNotStarted); + REPORTER_ASSERT(r, codec->skipScanlines(1) + == SkCodec::kScanlineDecodingNotStarted); + + const SkCodec::Result startResult = codec->startScanlineDecode(info); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); - REPORTER_ASSERT(r, scanlineDecoder); - REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess); + REPORTER_ASSERT(r, startResult == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { - result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0); + result = codec->getScanlines(bm.getAddr(0, y), 1, 0); REPORTER_ASSERT(r, result == SkCodec::kSuccess); } // verify that scanline decoding gives the same result. - if (SkScanlineDecoder::kTopDown_SkScanlineOrder == scanlineDecoder->getScanlineOrder()) { + if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) { compare_to_good_digest(r, digest, bm); } + + // Cannot continue to decode scanlines beyond the end + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == SkCodec::kInvalidParameters); + + // Interrupting a scanline decode with a full decode starts from + // scratch + REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess); + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == SkCodec::kSuccess); + REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()) + == SkCodec::kSuccess); + REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) + == SkCodec::kScanlineDecodingNotStarted); + REPORTER_ASSERT(r, codec->skipScanlines(1) + == SkCodec::kScanlineDecodingNotStarted); } else { - REPORTER_ASSERT(r, !scanlineDecoder); + REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented); } // The rest of this function tests decoding subsets, and will decode an arbitrary number of @@ -247,10 +265,102 @@ DEF_TEST(Codec, r) { check(r, "mandrill_512.png", SkISize::Make(512, 512), true, false); check(r, "mandrill_64.png", SkISize::Make(64, 64), true, false); check(r, "plane.png", SkISize::Make(250, 126), true, false); + check(r, "plane_interlaced.png", SkISize::Make(250, 126), true, false); check(r, "randPixels.png", SkISize::Make(8, 8), true, false); check(r, "yellow_rose.png", SkISize::Make(400, 301), true, false); } +// Test interlaced PNG in stripes, similar to DM's kStripe_Mode +DEF_TEST(Codec_stripes, r) { + const char * path = "plane_interlaced.png"; + SkAutoTDelete<SkStream> stream(resource(path)); + if (!stream) { + SkDebugf("Missing resource '%s'\n", path); + } + + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); + REPORTER_ASSERT(r, codec); + + if (!codec) { + return; + } + + switch (codec->getScanlineOrder()) { + case SkCodec::kBottomUp_SkScanlineOrder: + case SkCodec::kOutOfOrder_SkScanlineOrder: + ERRORF(r, "This scanline order will not match the original."); + return; + default: + break; + } + + // Baseline for what the image should look like, using N32. + const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); + + SkBitmap bm; + bm.allocPixels(info); + SkAutoLockPixels autoLockPixels(bm); + SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + SkMD5::Digest digest; + md5(bm, &digest); + + // Now decode in stripes + const int height = info.height(); + const int numStripes = 4; + int stripeHeight; + int remainingLines; + SkTDivMod(height, numStripes, &stripeHeight, &remainingLines); + + bm.eraseColor(SK_ColorYELLOW); + + result = codec->startScanlineDecode(info); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + // Odd stripes + for (int i = 1; i < numStripes; i += 2) { + // Skip the even stripes + result = codec->skipScanlines(stripeHeight); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight, + bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + } + + // Even stripes + result = codec->startScanlineDecode(info); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + for (int i = 0; i < numStripes; i += 2) { + result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight, + bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + // Skip the odd stripes + if (i + 1 < numStripes) { + result = codec->skipScanlines(stripeHeight); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + } + } + + // Remainder at the end + if (remainingLines > 0) { + result = codec->startScanlineDecode(info); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + result = codec->skipScanlines(height - remainingLines); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + result = codec->getScanlines(bm.getAddr(0, height - remainingLines), + remainingLines, bm.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + } + + compare_to_good_digest(r, digest, bm); +} + static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) { SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false)); // We should not have gotten a codec. Bots should catch us if we leaked anything. @@ -369,13 +479,12 @@ static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) { SkDebugf("Missing resource '%s'\n", path); return; } - SkAutoTDelete<SkScanlineDecoder> decoder(SkScanlineDecoder::NewFromStream( - stream.detach())); + SkAutoTDelete<SkCodec> decoder(SkCodec::NewFromStream(stream.detach())); // This should return kSuccess because kIndex8 is supported. SkPMColor colorStorage[256]; int colorCount; - SkCodec::Result result = decoder->start( + SkCodec::Result result = decoder->startScanlineDecode( decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, colorStorage, &colorCount); REPORTER_ASSERT(r, SkCodec::kSuccess == result); // The rest of the test is uninteresting if kIndex8 is not supported @@ -385,10 +494,10 @@ static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) { // This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid // colorPtr and a valid colorCountPtr. - result = decoder->start( + result = decoder->startScanlineDecode( decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, nullptr, nullptr); REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); - result = decoder->start( + result = decoder->startScanlineDecode( decoder->getInfo().makeColorType(kIndex_8_SkColorType)); REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); } diff --git a/tools/SkBitmapRegionCanvas.cpp b/tools/SkBitmapRegionCanvas.cpp index 2344d9020f..c54d936edc 100644 --- a/tools/SkBitmapRegionCanvas.cpp +++ b/tools/SkBitmapRegionCanvas.cpp @@ -7,9 +7,8 @@ #include "SkBitmapRegionCanvas.h" #include "SkCanvas.h" -#include "SkScanlineDecoder.h" -SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkScanlineDecoder* decoder) +SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder) : INHERITED(decoder->getInfo().width(), decoder->getInfo().height()) , fDecoder(decoder) {} @@ -114,7 +113,7 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY, dstColorType, dstAlphaType); // Start the scanline decoder - SkCodec::Result r = fDecoder->start(decodeInfo); + SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo); if (SkCodec::kSuccess != r) { SkDebugf("Error: Could not start scanline decoder.\n"); return nullptr; diff --git a/tools/SkBitmapRegionCanvas.h b/tools/SkBitmapRegionCanvas.h index 96631d70d3..05eca8b3fc 100644 --- a/tools/SkBitmapRegionCanvas.h +++ b/tools/SkBitmapRegionCanvas.h @@ -7,10 +7,10 @@ #include "SkBitmap.h" #include "SkBitmapRegionDecoderInterface.h" -#include "SkScanlineDecoder.h" +#include "SkCodec.h" /* - * This class implements SkBitmapRegionDecoder using an SkScanlineDecoder and + * This class implements SkBitmapRegionDecoder using an SkCodec and * an SkCanvas. It uses the scanline decoder to subset the height. It then * will subset the width and scale by drawing to an SkCanvas. */ @@ -22,7 +22,7 @@ public: /* * Takes ownership of pointer to decoder */ - SkBitmapRegionCanvas(SkScanlineDecoder* decoder); + SkBitmapRegionCanvas(SkCodec* decoder); /* * Three differences from the Android version: @@ -36,7 +36,7 @@ public: private: - SkAutoTDelete<SkScanlineDecoder> fDecoder; + SkAutoTDelete<SkCodec> fDecoder; typedef SkBitmapRegionDecoderInterface INHERITED; diff --git a/tools/SkBitmapRegionDecoderInterface.cpp b/tools/SkBitmapRegionDecoderInterface.cpp index 090f042ce3..5c769d676e 100644 --- a/tools/SkBitmapRegionDecoderInterface.cpp +++ b/tools/SkBitmapRegionDecoderInterface.cpp @@ -8,7 +8,7 @@ #include "SkBitmapRegionCanvas.h" #include "SkBitmapRegionDecoderInterface.h" #include "SkBitmapRegionSampler.h" -#include "SkScanlineDecoder.h" +#include "SkCodec.h" #include "SkImageDecoder.h" SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder( @@ -29,14 +29,14 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi return new SkBitmapRegionSampler(decoder, width, height); } case kCanvas_Strategy: { - SkScanlineDecoder* decoder = SkScanlineDecoder::NewFromStream(stream); + SkCodec* decoder = SkCodec::NewFromStream(stream); if (nullptr == decoder) { SkDebugf("Error: Failed to create decoder.\n"); return nullptr; } switch (decoder->getScanlineOrder()) { - case SkScanlineDecoder::kTopDown_SkScanlineOrder: - case SkScanlineDecoder::kNone_SkScanlineOrder: + case SkCodec::kTopDown_SkScanlineOrder: + case SkCodec::kNone_SkScanlineOrder: break; default: SkDebugf("Error: Scanline ordering not supported.\n"); |