diff options
-rw-r--r-- | bench/nanobench.cpp | 7 | ||||
-rw-r--r-- | bench/subset/SubsetSingleBench.cpp | 8 | ||||
-rw-r--r-- | bench/subset/SubsetTranslateBench.cpp | 8 | ||||
-rw-r--r-- | bench/subset/SubsetZoomBench.cpp | 8 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 28 | ||||
-rw-r--r-- | gyp/codec.gyp | 1 | ||||
-rw-r--r-- | include/codec/SkCodec.h | 56 | ||||
-rw-r--r-- | include/codec/SkScanlineDecoder.h | 72 | ||||
-rw-r--r-- | src/codec/SkCodec.cpp | 19 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.cpp | 117 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.h | 8 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.cpp | 85 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.h | 12 | ||||
-rw-r--r-- | src/codec/SkScanlineDecoder.cpp | 92 | ||||
-rw-r--r-- | tests/CodexTest.cpp | 11 |
15 files changed, 340 insertions, 192 deletions
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp index dbf7124555..2720b6f2de 100644 --- a/bench/nanobench.cpp +++ b/bench/nanobench.cpp @@ -513,9 +513,10 @@ static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool int colorCount; const SkImageInfo info = codec->getInfo().makeColorType(colorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder(info, NULL, - colors, &colorCount)); - if (NULL == scanlineDecoder) { + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromData(encoded)); + if (NULL == scanlineDecoder || scanlineDecoder->start(info, NULL, + colors, &colorCount) != SkCodec::kSuccess) + { SkDebugf("Could not create scanline decoder for %s with color type %s. " "Skipping bench.\n", path.c_str(), get_color_name(colorType)); return false; diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp index 1828f8771f..303cd556ec 100644 --- a/bench/subset/SubsetSingleBench.cpp +++ b/bench/subset/SubsetSingleBench.cpp @@ -64,11 +64,11 @@ void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); - const SkImageInfo info = codec->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( + SkScanlineDecoder::NewFromStream(fStream->duplicate())); + const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder( - info, NULL, colors, &colorCount)); + scanlineDecoder->start(info, NULL, colors, &colorCount); SkBitmap bitmap; SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight); diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp index 708cb553c7..2e66c3807e 100644 --- a/bench/subset/SubsetTranslateBench.cpp +++ b/bench/subset/SubsetTranslateBench.cpp @@ -60,11 +60,11 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); - const SkImageInfo info = codec->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( + SkScanlineDecoder::NewFromStream(fStream->duplicate())); + const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder( - info, NULL, colors, &colorCount)); + scanlineDecoder->start(info, NULL, colors, &colorCount); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp index 22bca23318..84d50aa478 100644 --- a/bench/subset/SubsetZoomBench.cpp +++ b/bench/subset/SubsetZoomBench.cpp @@ -60,11 +60,11 @@ void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) { SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); - const SkImageInfo info = codec->getInfo().makeColorType(fColorType); + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( + SkScanlineDecoder::NewFromStream(fStream->duplicate())); + const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder( - info, NULL, colors, &colorCount)); + scanlineDecoder->start(info, NULL, colors, &colorCount); const int centerX = info.width() / 2; const int centerY = info.height() / 2; diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index d1d9be030f..f555b9fcbd 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -159,11 +159,13 @@ Error CodecSrc::draw(SkCanvas* canvas) const { break; } case kScanline_Mode: { - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder( - decodeInfo, NULL, colorPtr, colorCountPtr)); - if (NULL == scanlineDecoder) { + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( + SkScanlineDecoder::NewFromData(encoded)); + if (NULL == scanlineDecoder || SkCodec::kSuccess != + scanlineDecoder->start(decodeInfo, NULL, colorPtr, colorCountPtr)) { return Error::Nonfatal("Cannot use scanline decoder for all images"); } + const SkCodec::Result result = scanlineDecoder->getScanlines( bitmap.getAddr(0, 0), decodeInfo.height(), bitmap.rowBytes()); switch (result) { @@ -222,8 +224,11 @@ Error CodecSrc::draw(SkCanvas* canvas) const { const int y = row * subsetHeight; //create scanline decoder for each subset SkAutoTDelete<SkScanlineDecoder> subsetScanlineDecoder( - codec->getScanlineDecoder(decodeInfo, NULL, colorPtr, colorCountPtr)); - if (NULL == subsetScanlineDecoder) { + SkScanlineDecoder::NewFromData(encoded)); + if (NULL == subsetScanlineDecoder || SkCodec::kSuccess != + subsetScanlineDecoder->start( + decodeInfo, NULL, colorPtr, colorCountPtr)) + { if (x == 0 && y == 0) { //first try, image may not be compatible return Error::Nonfatal("Cannot use scanline decoder for all images"); @@ -289,9 +294,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const { const int numStripes = (height + stripeHeight - 1) / stripeHeight; // Decode odd stripes - SkAutoTDelete<SkScanlineDecoder> decoder( - codec->getScanlineDecoder(decodeInfo, NULL, colorPtr, colorCountPtr)); - if (NULL == decoder) { + SkAutoTDelete<SkScanlineDecoder> decoder(SkScanlineDecoder::NewFromData(encoded)); + if (NULL == decoder || SkCodec::kSuccess != + decoder->start(decodeInfo, NULL, colorPtr, colorCountPtr)) { return Error::Nonfatal("Cannot use scanline decoder for all images"); } for (int i = 0; i < numStripes; i += 2) { @@ -323,9 +328,10 @@ Error CodecSrc::draw(SkCanvas* canvas) const { } // Decode even stripes - decoder.reset(codec->getScanlineDecoder(decodeInfo, NULL, colorPtr, colorCountPtr)); - if (NULL == decoder) { - return "Failed to create second scanline decoder."; + const SkCodec::Result startResult = decoder->start(decodeInfo, NULL, colorPtr, + colorCountPtr); + if (SkCodec::kSuccess != startResult) { + return "Failed to restart scanline decoder with same parameters."; } for (int i = 0; i < numStripes; i += 2) { // Read a stripe diff --git a/gyp/codec.gyp b/gyp/codec.gyp index 5a368af821..c7c1892bd5 100644 --- a/gyp/codec.gyp +++ b/gyp/codec.gyp @@ -44,6 +44,7 @@ '../src/codec/SkJpegUtility_codec.cpp', '../src/codec/SkMaskSwizzler.cpp', '../src/codec/SkMasks.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 1cdc88d4ad..ce581632e1 100644 --- a/include/codec/SkCodec.h +++ b/include/codec/SkCodec.h @@ -17,7 +17,6 @@ #include "SkTypes.h" class SkData; -class SkScanlineDecoder; /** * Abstraction layer directly on top of an image codec. @@ -202,37 +201,6 @@ public: Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); /** - * Create a new object which can be used to decode individual scanlines. - * - * The returned object has its own state, independent of the SkCodec, or any - * previously spawned SkScanlineDecoders. At creation, it will be ready to - * return the first scanline. - * - * @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 New SkScanlineDecoder, or NULL on failure. - * - * NOTE: This requires duplicating the SkStream. - */ - SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo, const Options* options, - SkPMColor ctable[], int* ctableCount); - - /** - * Simplified version of getScanlineDecoder() that asserts that info is NOT - * kIndex8_SkColorType and uses the default Options. - */ - SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo); - - /** * 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 @@ -263,30 +231,6 @@ protected: return false; } - /** - * Override if your codec supports scanline decoding. - * - * @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 New SkScanlineDecoder on success, NULL otherwise. The caller is - * responsible for deleting the returned object. - */ - virtual SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo, - const Options& options, - SkPMColor ctable[], - int* ctableCount) { - return NULL; - } - virtual bool onReallyHasAlpha() const { return false; } enum RewindState { diff --git a/include/codec/SkScanlineDecoder.h b/include/codec/SkScanlineDecoder.h index 8376e57c09..c233663dbd 100644 --- a/include/codec/SkScanlineDecoder.h +++ b/include/codec/SkScanlineDecoder.h @@ -16,6 +16,27 @@ 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 @@ -25,8 +46,43 @@ public: virtual ~SkScanlineDecoder() {} /** + * 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. @@ -34,6 +90,7 @@ public: * 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; @@ -46,12 +103,15 @@ public: /** * 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 @@ -76,8 +136,9 @@ public: } protected: - SkScanlineDecoder(const SkImageInfo& requested) - : fDstInfo(requested) + SkScanlineDecoder(const SkImageInfo& srcInfo) + : fSrcInfo(srcInfo) + , fDstInfo() , fCurrScanline(0) {} virtual bool onReallyHasAlpha() const { return false; } @@ -85,9 +146,14 @@ protected: const SkImageInfo& dstInfo() const { return fDstInfo; } private: - const SkImageInfo fDstInfo; + const SkImageInfo fSrcInfo; + SkImageInfo fDstInfo; 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()); diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp index 1f83680ef6..6ef61b289a 100644 --- a/src/codec/SkCodec.cpp +++ b/src/codec/SkCodec.cpp @@ -140,22 +140,3 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t return this->getPixels(info, pixels, rowBytes, NULL, NULL, NULL); } -SkScanlineDecoder* SkCodec::getScanlineDecoder(const SkImageInfo& dstInfo, const Options* options, - SkPMColor ctable[], int* ctableCount) { - - // Set options. - Options optsStorage; - if (NULL == options) { - options = &optsStorage; - } - - return this->onGetScanlineDecoder(dstInfo, *options, ctable, ctableCount); -} - -SkScanlineDecoder* SkCodec::getScanlineDecoder(const SkImageInfo& dstInfo) { - SkASSERT(kIndex_8_SkColorType != dstInfo.colorType()); - if (kIndex_8_SkColorType == dstInfo.colorType()) { - return NULL; - } - return this->getScanlineDecoder(dstInfo, NULL, NULL, NULL); -} diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp index 9f9c110cc6..d29ca8a2a3 100644 --- a/src/codec/SkCodec_libpng.cpp +++ b/src/codec/SkCodec_libpng.cpp @@ -594,13 +594,40 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* class SkPngScanlineDecoder : public SkScanlineDecoder { public: - SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) - : INHERITED(dstInfo) + SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) + : INHERITED(srcInfo) , fCodec(codec) , fHasAlpha(false) + {} + + SkCodec::Result onStart(const SkImageInfo& dstInfo, + const SkCodec::Options& options, + SkPMColor ctable[], int* ctableCount) override { + if (!fCodec->handleRewind()) { + return SkCodec::kCouldNotRewind; + } + + if (!conversion_possible(dstInfo, this->getInfo())) { + return SkCodec::kInvalidConversion; + } + + // Check to see if scaling was requested. + if (dstInfo.dimensions() != this->getInfo().dimensions()) { + return SkCodec::kInvalidScale; + } + + const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, + ctableCount); + if (result != SkCodec::kSuccess) { + return result; + } + + fHasAlpha = false; fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); fSrcRow = static_cast<uint8_t*>(fStorage.get()); + + return SkCodec::kSuccess; } SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override { @@ -648,24 +675,60 @@ private: class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder { public: - SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec) - : INHERITED(dstInfo) + SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec) + : INHERITED(srcInfo) , fCodec(codec) , fHasAlpha(false) , fCurrentRow(0) - , fHeight(dstInfo.height()) + , fHeight(srcInfo.height()) + , fCanSkipRewind(false) + {} + + SkCodec::Result onStart(const SkImageInfo& dstInfo, + const SkCodec::Options& options, + SkPMColor ctable[], int* ctableCount) override { + if (!fCodec->handleRewind()) { + return SkCodec::kCouldNotRewind; + } + + if (!conversion_possible(dstInfo, this->getInfo())) { + return SkCodec::kInvalidConversion; + } + + // Check to see if scaling was requested. + if (dstInfo.dimensions() != this->getInfo().dimensions()) { + return SkCodec::kInvalidScale; + } + + const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, + ctableCount); + if (result != SkCodec::kSuccess) { + return result; + } + + fHasAlpha = false; + fCurrentRow = 0; + fHeight = dstInfo.height(); fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig); fGarbageRow.reset(fSrcRowBytes); fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); + fCanSkipRewind = true; + + return SkCodec::kSuccess; } SkCodec::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 (!fCodec->handleRewind()) { + // 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. + // Next time onGetScanlines is called, we will need to rewind. + fCanSkipRewind = false; + } else if (!fCodec->handleRewind()) { return SkCodec::kCouldNotRewind; } + if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) { SkCodecPrintf("setjmp long jump!\n"); return SkCodec::kInvalidInput; @@ -718,42 +781,34 @@ private: size_t fSrcRowBytes; SkAutoMalloc fGarbageRow; uint8_t* fGarbageRowPtr; + // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function + // 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. + bool fCanSkipRewind; typedef SkScanlineDecoder INHERITED; }; - -SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, - const Options& options, SkPMColor ctable[], int* ctableCount) { - if (!conversion_possible(dstInfo, this->getInfo())) { - SkCodecPrintf("no conversion possible\n"); - return NULL; - } - // Check to see if scaling was requested. - if (dstInfo.dimensions() != this->getInfo().dimensions()) { - return NULL; - } - // Create a new SkPngCodec, to be owned by the scanline decoder. - SkStream* stream = this->stream()->duplicate(); - if (!stream) { - return NULL; - } +SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) { SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream))); if (!codec) { return NULL; } - if (codec->initializeSwizzler(dstInfo, options, ctable, ctableCount) != kSuccess) { - SkCodecPrintf("failed to initialize the swizzler.\n"); - return NULL; - } - + 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 SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, codec.detach())); + return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (srcInfo, codec.detach())); } - return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, codec.detach())); + return SkNEW_ARGS(SkPngScanlineDecoder, (srcInfo, codec.detach())); } diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h index 297e8043fe..21bbdadb49 100644 --- a/src/codec/SkCodec_libpng.h +++ b/src/codec/SkCodec_libpng.h @@ -23,18 +23,18 @@ class SkStream; class SkPngCodec : public SkCodec { public: - // Assumes IsPng was called and returned true. - static SkCodec* NewFromStream(SkStream*); static bool IsPng(SkStream*); + // Assume IsPng was called and returned true. + static SkCodec* NewFromStream(SkStream*); + static SkScanlineDecoder* NewSDFromStream(SkStream*); + virtual ~SkPngCodec(); protected: Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*) override; SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; } - SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options, - SkPMColor ctable[], int* ctableCount) override; bool onReallyHasAlpha() const override { return fReallyHasAlpha; } private: png_structp fPng_ptr; diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 84e90d4442..e160f0c4e2 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -389,13 +389,47 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, */ class SkJpegScanlineDecoder : public SkScanlineDecoder { public: - SkJpegScanlineDecoder(const SkImageInfo& dstInfo, SkJpegCodec* codec, - const SkCodec::Options& opts) - : INHERITED(dstInfo) + SkJpegScanlineDecoder(const SkImageInfo& srcInfo, SkJpegCodec* codec) + : INHERITED(srcInfo) , fCodec(codec) - , fOpts(opts) + , fOpts() {} + SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, + SkPMColor ctable[], int* ctableCount) override { + + // Rewind the stream if needed + if (!fCodec->handleRewind()) { + return SkCodec::kCouldNotRewind; + } + + // Set the jump location for libjpeg errors + if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { + SkCodecPrintf("setjmp: Error from libjpeg\n"); + return SkCodec::kInvalidInput; + } + + // Check if we can decode to the requested destination and set the output color space + if (!fCodec->setOutputColorSpace(dstInfo)) { + return SkCodec::kInvalidConversion; + } + + // Perform the necessary scaling + if (!fCodec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { + return SkCodec::kInvalidScale; + } + + // Now, given valid output dimensions, we can start the decompress + if (!turbo_jpeg_start_decompress(fCodec->fDecoderMgr->dinfo())) { + SkCodecPrintf("start decompress failed\n"); + return SkCodec::kInvalidInput; + } + + fOpts = options; + + return SkCodec::kSuccess; + } + virtual ~SkJpegScanlineDecoder() { if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); @@ -464,53 +498,18 @@ public: private: SkAutoTDelete<SkJpegCodec> fCodec; - const SkCodec::Options& fOpts; + SkCodec::Options fOpts; typedef SkScanlineDecoder INHERITED; }; -SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, - const Options& options, SkPMColor ctable[], int* ctableCount) { - - // Rewind the stream if needed - if (!this->handleRewind()) { - SkCodecPrintf("Could not rewind\n"); - return NULL; - } - - // Set the jump location for libjpeg errors - if (setjmp(fDecoderMgr->getJmpBuf())) { - SkCodecPrintf("setjmp: Error from libjpeg\n"); - return NULL; - } - - SkStream* stream = this->stream()->duplicate(); - if (!stream) { - return NULL; - } +SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) { SkAutoTDelete<SkJpegCodec> codec(static_cast<SkJpegCodec*>(SkJpegCodec::NewFromStream(stream))); if (!codec) { return NULL; } - // Check if we can decode to the requested destination and set the output color space - if (!codec->setOutputColorSpace(dstInfo)) { - SkCodecPrintf("Cannot convert to output type\n"); - return NULL; - } - - // Perform the necessary scaling - if (!codec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { - SkCodecPrintf("Cannot scale to output dimensions\n"); - return NULL; - } - - // Now, given valid output dimensions, we can start the decompress - if (!turbo_jpeg_start_decompress(codec->fDecoderMgr->dinfo())) { - SkCodecPrintf("start decompress failed\n"); - return NULL; - } - + const SkImageInfo& srcInfo = codec->getInfo(); // Return the new scanline decoder - return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, codec.detach(), options)); + return SkNEW_ARGS(SkJpegScanlineDecoder, (srcInfo, codec.detach())); } diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h index 1844269e6b..5e2135cdff 100644 --- a/src/codec/SkJpegCodec.h +++ b/src/codec/SkJpegCodec.h @@ -18,6 +18,8 @@ extern "C" { #include "jpeglib.h" } +class SkScanlineDecoder; + /* * * This class implements the decoding for jpeg images @@ -39,6 +41,13 @@ 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: /* @@ -56,9 +65,6 @@ protected: return kJPEG_SkEncodedFormat; } - SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options, - SkPMColor ctable[], int* ctableCount) override; - private: /* diff --git a/src/codec/SkScanlineDecoder.cpp b/src/codec/SkScanlineDecoder.cpp new file mode 100644 index 0000000000..00c020c7c2 --- /dev/null +++ b/src/codec/SkScanlineDecoder.cpp @@ -0,0 +1,92 @@ +/* + * 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 "SkCodec_libpng.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 +}; + +SkScanlineDecoder* SkScanlineDecoder::NewFromStream(SkStream* stream) { + if (!stream) { + return NULL; + } + + SkAutoTDelete<SkStream> streamDeleter(stream); + + SkAutoTDelete<SkScanlineDecoder> codec(NULL); + 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 NULL; + } + 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 NULL; + } else { + return codec.detach(); + } +} + +SkScanlineDecoder* SkScanlineDecoder::NewFromData(SkData* data) { + if (!data) { + return NULL; + } + return NewFromStream(SkNEW_ARGS(SkMemoryStream, (data))); +} + + +SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo, + const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) { + // Set options. + SkCodec::Options optsStorage; + if (NULL == options) { + options = &optsStorage; + } + + const SkCodec::Result result = this->onStart(dstInfo, *options, ctable, ctableCount); + if (result != SkCodec::kSuccess) { + return result; + } + + fCurrScanline = 0; + fDstInfo = dstInfo; + return SkCodec::kSuccess; +} + +SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo) { + SkASSERT(kIndex_8_SkColorType != dstInfo.colorType()); + if (kIndex_8_SkColorType == dstInfo.colorType()) { + return SkCodec::kInvalidConversion; + } + return this->start(dstInfo, NULL, NULL, NULL); +} + diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp index 82e490a2bd..1dd64d9a6d 100644 --- a/tests/CodexTest.cpp +++ b/tests/CodexTest.cpp @@ -95,17 +95,14 @@ static void check(skiatest::Reporter* r, // verify that re-decoding gives the same result. compare_to_good_digest(r, digest, bm); - SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(codec->getScanlineDecoder(info)); + stream.reset(resource(path)); + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( + SkScanlineDecoder::NewFromStream(stream.detach())); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); REPORTER_ASSERT(r, scanlineDecoder); - // Regular decodes should not be affected by creating a scanline decoder - result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL); - REPORTER_ASSERT(r, SkCodec::kSuccess == result); - compare_to_good_digest(r, digest, bm); - - bm.eraseColor(SK_ColorYELLOW); + REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0); |