Create a scanline decoder without creating a codec
Prior to this CL, if a client wanted to decode scanlines, they had to
create an SkCodec in order to get an SkScanlineDecoder. This introduces
complications if input data is not easily shared between the two
objects.
Instead, add methods to SkScanlineDecoder for creating a new one from
input data, and remove the creation functions from SkCodec.
Update DM and tests.
Review URL: https://codereview.chromium.org/1267583002
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index dbf7124..2720b6f 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -513,9 +513,10 @@
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 1828f87..303cd55 100644
--- a/bench/subset/SubsetSingleBench.cpp
+++ b/bench/subset/SubsetSingleBench.cpp
@@ -64,11 +64,11 @@
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 708cb55..2e66c38 100644
--- a/bench/subset/SubsetTranslateBench.cpp
+++ b/bench/subset/SubsetTranslateBench.cpp
@@ -60,11 +60,11 @@
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 22bca23..84d50aa 100644
--- a/bench/subset/SubsetZoomBench.cpp
+++ b/bench/subset/SubsetZoomBench.cpp
@@ -60,11 +60,11 @@
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 d1d9be0..f555b9f 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -159,11 +159,13 @@
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 @@
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 @@
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 @@
}
// 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 5a368af..c7c1892 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 1cdc88d..ce58163 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 @@
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 @@
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 8376e57..c233663 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 @@
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 @@
* 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 @@
/**
* 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 @@
}
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 @@
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 1f83680..6ef61b2 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -140,22 +140,3 @@
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 9f9c110..d29ca8a 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -594,13 +594,40 @@
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 @@
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())
- {
- fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
- fGarbageRow.reset(fSrcRowBytes);
- fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
- }
+ , fHeight(srcInfo.height())
+ , fCanSkipRewind(false)
+ {}
- 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
+ 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 (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 @@
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 297e804..21bbdad 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -23,18 +23,18 @@
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 84e90d4..e160f0c 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -389,13 +389,47 @@
*/
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 @@
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 1844269..5e2135c 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -18,6 +18,8 @@
#include "jpeglib.h"
}
+class SkScanlineDecoder;
+
/*
*
* This class implements the decoding for jpeg images
@@ -39,6 +41,13 @@
*/
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 @@
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 0000000..00c020c
--- /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 82e490a..1dd64d9 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -95,17 +95,14 @@
// 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);