aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar scroggo <scroggo@chromium.org>2015-08-04 09:24:45 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-08-04 09:24:45 -0700
commit1c005e4a38e29d648ecebada25d3a718155043a3 (patch)
treeccc10aa351e4c58f9ffcd53b849d9a5afc46c28b /src
parentddc726f1de09426557983361a0e7838a83612315 (diff)
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
Diffstat (limited to 'src')
-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
6 files changed, 233 insertions, 100 deletions
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);
+}
+