aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/nanobench.cpp7
-rw-r--r--bench/subset/SubsetSingleBench.cpp8
-rw-r--r--bench/subset/SubsetTranslateBench.cpp8
-rw-r--r--bench/subset/SubsetZoomBench.cpp8
-rw-r--r--dm/DMSrcSink.cpp28
-rw-r--r--gyp/codec.gyp1
-rw-r--r--include/codec/SkCodec.h56
-rw-r--r--include/codec/SkScanlineDecoder.h72
-rw-r--r--src/codec/SkCodec.cpp19
-rw-r--r--src/codec/SkCodec_libpng.cpp117
-rw-r--r--src/codec/SkCodec_libpng.h8
-rw-r--r--src/codec/SkJpegCodec.cpp85
-rw-r--r--src/codec/SkJpegCodec.h12
-rw-r--r--src/codec/SkScanlineDecoder.cpp92
-rw-r--r--tests/CodexTest.cpp11
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);