aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec
diff options
context:
space:
mode:
authorGravatar scroggo <scroggo@google.com>2015-09-30 08:57:13 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-09-30 08:57:14 -0700
commit46c574725676b26ada63ac15e42cda309dcd5090 (patch)
treed9ee41e9f1c607ff139eca622bb03809eb9a83d3 /src/codec
parente2bcec38486f2e1a897973c953e5610eaef4221e (diff)
Merge SkCodec with SkScanlineDecoder
Benefits: - This mimics other decoding APIs (including the ones SkCodec relies on, e.g. a png_struct, which can be used to decode an entire image or one line at a time). - It allows a client to ask us to do what we can do efficiently - i.e. start from encoded data and either decode the whole thing or scanlines. - It removes the duplicate methods which appeared in both SkCodec and SkScanlineDecoder (some of which, e.g. in SkJpegScanlineDecoder, just call fCodec->sameMethod()). - It simplifies moving more checks into the base class (e.g. the examples in skbug.com/4284). BUG=skia:4175 BUG=skia:4284 ===================================================================== SkScanlineDecoder.h/.cpp: Removed. SkCodec.h/.cpp: Add methods, enums, and variables which were previously in SkScanlineDecoder. Default fCurrScanline to -1, as a sentinel that start has not been called. General changes: Convert SkScanlineDecoders to SkCodecs. General changes in SkCodec subclasses: Merge SkScanlineDecoder implementation into SkCodec. Most (all?) owned an SkCodec, so they now call this-> instead of fCodec->. SkBmpCodec.h/.cpp: Replace the unused rowOrder method with an override for onGetScanlineOrder. Make getDstRow const, since it is called by onGetY, which is const. SkCodec_libpng.h/.cpp: Make SkPngCodec an abstract class, with two subclasses which handle scanline decoding separately (they share code for decoding the entire image). Reimplement onReallyHasAlpha so that it can return the most recent result (e.g. after a scanline decode which only decoded part of the image) or a better answer (e.g. if the whole image is known to be opaque). Compute fNumberPasses early, so we know which subclass to instantiate. Make SkPngInterlaceScanlineDecoder use the base class' fCurrScanline rather than a separate variable. CodexTest.cpp: Add tests for the state changes in SkCodec (need to call start before decoding scanlines; calling getPixels means that start will need to be called again before decoding more scanlines). Add a test which decodes in stripes, currently only used for an interlaced PNG. TODO: Add tests for onReallyHasAlpha. Review URL: https://codereview.chromium.org/1365313002
Diffstat (limited to 'src/codec')
-rw-r--r--src/codec/SkBmpCodec.cpp97
-rw-r--r--src/codec/SkBmpCodec.h27
-rw-r--r--src/codec/SkBmpMaskCodec.cpp2
-rw-r--r--src/codec/SkBmpMaskCodec.h2
-rw-r--r--src/codec/SkBmpRLECodec.cpp3
-rw-r--r--src/codec/SkBmpRLECodec.h2
-rw-r--r--src/codec/SkBmpStandardCodec.cpp3
-rw-r--r--src/codec/SkBmpStandardCodec.h2
-rw-r--r--src/codec/SkCodec.cpp80
-rw-r--r--src/codec/SkCodec_libgif.cpp174
-rw-r--r--src/codec/SkCodec_libgif.h23
-rw-r--r--src/codec/SkCodec_libpng.cpp320
-rw-r--r--src/codec/SkCodec_libpng.h38
-rw-r--r--src/codec/SkCodec_wbmp.cpp117
-rw-r--r--src/codec/SkCodec_wbmp.h18
-rw-r--r--src/codec/SkJpegCodec.cpp278
-rw-r--r--src/codec/SkJpegCodec.h25
-rw-r--r--src/codec/SkScaledCodec.cpp73
-rw-r--r--src/codec/SkScanlineDecoder.cpp108
19 files changed, 648 insertions, 744 deletions
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index a55cb8cf55..4e801f0be1 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -262,10 +262,10 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) {
}
// Check for valid dimensions from header
- SkScanlineDecoder::SkScanlineOrder rowOrder = SkScanlineDecoder::kBottomUp_SkScanlineOrder;
+ SkCodec::SkScanlineOrder rowOrder = SkCodec::kBottomUp_SkScanlineOrder;
if (height < 0) {
height = -height;
- rowOrder = SkScanlineDecoder::kTopDown_SkScanlineOrder;
+ rowOrder = SkCodec::kTopDown_SkScanlineOrder;
}
// The height field for bmp in ico is double the actual height because they
// contain an XOR mask followed by an AND mask
@@ -531,7 +531,7 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) {
}
SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
- uint16_t bitsPerPixel, SkScanlineDecoder::SkScanlineOrder rowOrder)
+ uint16_t bitsPerPixel, SkCodec::SkScanlineOrder rowOrder)
: INHERITED(info, stream)
, fBitsPerPixel(bitsPerPixel)
, fRowOrder(rowOrder)
@@ -541,11 +541,11 @@ bool SkBmpCodec::onRewind() {
return SkBmpCodec::ReadHeader(this->stream(), this->inIco(), nullptr);
}
-int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) {
- if (SkScanlineDecoder::kTopDown_SkScanlineOrder == fRowOrder) {
+int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
+ if (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) {
return y;
}
- SkASSERT(SkScanlineDecoder::kBottomUp_SkScanlineOrder == fRowOrder);
+ SkASSERT(SkCodec::kBottomUp_SkScanlineOrder == fRowOrder);
return height - y - 1;
}
@@ -557,7 +557,7 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) {
* filling at the top of the image.
*/
void* SkBmpCodec::getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const {
- return (SkScanlineDecoder::kTopDown_SkScanlineOrder == fRowOrder) ?
+ return (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) ?
SkTAddOffset<void*>(dst, y * dstRowBytes) : dst;
}
@@ -574,71 +574,36 @@ uint32_t SkBmpCodec::computeNumColors(uint32_t numColors) {
return numColors;
}
-/*
- * Scanline decoder for bmps
- */
-class SkBmpScanlineDecoder : public SkScanlineDecoder {
-public:
- SkBmpScanlineDecoder(SkBmpCodec* codec)
- : INHERITED(codec->getInfo())
- , fCodec(codec)
- {}
-
- SkEncodedFormat onGetEncodedFormat() const override {
- return kBMP_SkEncodedFormat;
- }
-
- SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options,
- SkPMColor inputColorPtr[], int* inputColorCount) override {
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
- }
- if (options.fSubset) {
- // Subsets are not supported.
- return SkCodec::kUnimplemented;
- }
- if (dstInfo.dimensions() != this->getInfo().dimensions()) {
- if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
- }
- }
- if (!conversion_possible(dstInfo, this->getInfo())) {
- SkCodecPrintf("Error: cannot convert input type to output type.\n");
- return SkCodec::kInvalidConversion;
- }
-
- return fCodec->prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
+SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
}
-
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
- // Create a new image info representing the portion of the image to decode
- SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
-
- // Decode the requested rows
- return fCodec->decodeRows(rowInfo, dst, rowBytes, this->options());
+ if (options.fSubset) {
+ // Subsets are not supported.
+ return kUnimplemented;
}
-
- SkScanlineOrder onGetScanlineOrder() const override {
- return fCodec->fRowOrder;
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
+ return SkCodec::kInvalidScale;
+ }
}
-
- int onGetY() const override {
- return fCodec->getDstRow(this->INHERITED::onGetY(), this->dstInfo().height());
+ if (!conversion_possible(dstInfo, this->getInfo())) {
+ SkCodecPrintf("Error: cannot convert input type to output type.\n");
+ return kInvalidConversion;
}
- // TODO(msarett): Override default skipping with something more clever.
-
-private:
- SkAutoTDelete<SkBmpCodec> fCodec;
+ return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
+}
- typedef SkScanlineDecoder INHERITED;
-};
+SkCodec::Result SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+ // Create a new image info representing the portion of the image to decode
+ SkImageInfo rowInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
-SkScanlineDecoder* SkBmpCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkBmpCodec> codec(static_cast<SkBmpCodec*>(SkBmpCodec::NewFromStream(stream)));
- if (!codec) {
- return NULL;
- }
+ // Decode the requested rows
+ return this->decodeRows(rowInfo, dst, rowBytes, this->options());
+}
- return new SkBmpScanlineDecoder(codec.detach());
+int SkBmpCodec::onNextScanline() const {
+ return this->getDstRow(this->INHERITED::onNextScanline(), this->dstInfo().height());
}
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index 4b2cd2a268..ea6f789407 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -11,7 +11,6 @@
#include "SkColorTable.h"
#include "SkImageInfo.h"
#include "SkMaskSwizzler.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
#include "SkSwizzler.h"
#include "SkTypes.h"
@@ -41,17 +40,10 @@ public:
*/
static SkCodec* NewFromIco(SkStream*);
- /*
- * Assumes IsBmp was called and returned true
- * Creates a bmp scanline decoder
- * Takes ownership of the stream
- */
- static SkScanlineDecoder* NewSDFromStream(SkStream* stream);
-
protected:
SkBmpCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel,
- SkScanlineDecoder::SkScanlineOrder rowOrder);
+ SkCodec::SkScanlineOrder rowOrder);
SkEncodedFormat onGetEncodedFormat() const override { return kBMP_SkEncodedFormat; }
@@ -87,7 +79,7 @@ protected:
* when we want to decode the full or one when we are
* sampling.
*/
- int32_t getDstRow(int32_t y, int32_t height);
+ int32_t getDstRow(int32_t y, int32_t height) const;
/*
* Get the destination row to start filling from
@@ -107,7 +99,7 @@ protected:
* Accessors used by subclasses
*/
uint16_t bitsPerPixel() const { return fBitsPerPixel; }
- SkScanlineDecoder::SkScanlineOrder rowOrder() const { return fRowOrder; }
+ SkScanlineOrder onGetScanlineOrder() const override { return fRowOrder; }
/*
* To be overriden by bmp subclasses, which provide unique implementations.
@@ -153,10 +145,17 @@ private:
virtual Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
const Options& opts) = 0;
- const uint16_t fBitsPerPixel;
- const SkScanlineDecoder::SkScanlineOrder fRowOrder;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options&,
+ SkPMColor inputColorPtr[], int* inputColorCount) override;
+
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override;
+
+ int onNextScanline() const override;
+
+ // TODO(msarett): Override default skipping with something more clever.
- friend class SkBmpScanlineDecoder;
+ const uint16_t fBitsPerPixel;
+ const SkScanlineOrder fRowOrder;
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 3036f337b7..2e88d54f02 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -14,7 +14,7 @@
*/
SkBmpMaskCodec::SkBmpMaskCodec(const SkImageInfo& info, SkStream* stream,
uint16_t bitsPerPixel, SkMasks* masks,
- SkScanlineDecoder::SkScanlineOrder rowOrder)
+ SkCodec::SkScanlineOrder rowOrder)
: INHERITED(info, stream, bitsPerPixel, rowOrder)
, fMasks(masks)
, fMaskSwizzler(nullptr)
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 58b2e6b507..0b1291266d 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -30,7 +30,7 @@ public:
*/
SkBmpMaskCodec(const SkImageInfo& srcInfo, SkStream* stream,
uint16_t bitsPerPixel, SkMasks* masks,
- SkScanlineDecoder::SkScanlineOrder rowOrder);
+ SkCodec::SkScanlineOrder rowOrder);
protected:
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 58c060531e..79dd63307e 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -9,7 +9,6 @@
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
/*
@@ -19,7 +18,7 @@
SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
uint16_t bitsPerPixel, uint32_t numColors,
uint32_t bytesPerColor, uint32_t offset,
- SkScanlineDecoder::SkScanlineOrder rowOrder,
+ SkCodec::SkScanlineOrder rowOrder,
size_t RLEBytes)
: INHERITED(info, stream, bitsPerPixel, rowOrder)
, fColorTable(nullptr)
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
index aa5b061273..ea2c2b6bab 100644
--- a/src/codec/SkBmpRLECodec.h
+++ b/src/codec/SkBmpRLECodec.h
@@ -36,7 +36,7 @@ public:
*/
SkBmpRLECodec(const SkImageInfo& srcInfo, SkStream* stream,
uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
- uint32_t offset, SkScanlineDecoder::SkScanlineOrder rowOrder,
+ uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
size_t RLEBytes);
protected:
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 59bc917481..02f2d4215a 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -8,7 +8,6 @@
#include "SkBmpStandardCodec.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
/*
@@ -18,7 +17,7 @@
SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream,
uint16_t bitsPerPixel, uint32_t numColors,
uint32_t bytesPerColor, uint32_t offset,
- SkScanlineDecoder::SkScanlineOrder rowOrder, bool inIco)
+ SkCodec::SkScanlineOrder rowOrder, bool inIco)
: INHERITED(info, stream, bitsPerPixel, rowOrder)
, fColorTable(nullptr)
, fNumColors(this->computeNumColors(numColors))
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index fcc246f353..0d81cbfee4 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -37,7 +37,7 @@ public:
*/
SkBmpStandardCodec(const SkImageInfo& srcInfo, SkStream* stream,
uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
- uint32_t offset, SkScanlineDecoder::SkScanlineOrder rowOrder,
+ uint32_t offset, SkCodec::SkScanlineOrder rowOrder,
bool isIco);
protected:
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index d12de21f51..97bcf3e1d8 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -76,9 +76,12 @@ SkCodec* SkCodec::NewFromData(SkData* data) {
}
SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
- : fInfo(info)
+ : fSrcInfo(info)
, fStream(stream)
, fNeedsRewind(false)
+ , fDstInfo()
+ , fOptions()
+ , fCurrScanline(-1)
{}
SkCodec::~SkCodec() {}
@@ -92,6 +95,9 @@ bool SkCodec::rewindIfNeeded() {
return true;
}
+ // startScanlineDecode will need to be called before decoding scanlines.
+ fCurrScanline = -1;
+
if (!fStream->rewind()) {
return false;
}
@@ -148,3 +154,75 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
}
+
+SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
+ const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
+ // Reset fCurrScanline in case of failure.
+ fCurrScanline = -1;
+ // Ensure that valid color ptrs are passed in for kIndex8 color type
+ if (kIndex_8_SkColorType == dstInfo.colorType()) {
+ if (nullptr == ctable || nullptr == ctableCount) {
+ return SkCodec::kInvalidParameters;
+ }
+ } else {
+ if (ctableCount) {
+ *ctableCount = 0;
+ }
+ ctableCount = nullptr;
+ ctable = nullptr;
+ }
+
+ // Set options.
+ Options optsStorage;
+ if (nullptr == options) {
+ options = &optsStorage;
+ }
+
+ const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
+ if (result != SkCodec::kSuccess) {
+ return result;
+ }
+
+ fCurrScanline = 0;
+ fDstInfo = dstInfo;
+ fOptions = *options;
+ return kSuccess;
+}
+
+SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) {
+ return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr);
+}
+
+SkCodec::Result SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
+ if (fCurrScanline < 0) {
+ return kScanlineDecodingNotStarted;
+ }
+
+ SkASSERT(!fDstInfo.isEmpty());
+ if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0
+ || fCurrScanline + countLines > fDstInfo.height()) {
+ return kInvalidParameters;
+ }
+
+ const Result result = this->onGetScanlines(dst, countLines, rowBytes);
+ fCurrScanline += countLines;
+ return result;
+}
+
+SkCodec::Result SkCodec::skipScanlines(int countLines) {
+ if (fCurrScanline < 0) {
+ return kScanlineDecodingNotStarted;
+ }
+
+ SkASSERT(!fDstInfo.isEmpty());
+ if (fCurrScanline + countLines > fDstInfo.height()) {
+ // Arguably, we could just skip the scanlines which are remaining,
+ // and return kSuccess. We choose to return invalid so the client
+ // can catch their bug.
+ return SkCodec::kInvalidParameters;
+ }
+
+ const Result result = this->onSkipScanlines(countLines);
+ fCurrScanline += countLines;
+ return result;
+}
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
index d8889defaf..ad7fb5b578 100644
--- a/src/codec/SkCodec_libgif.cpp
+++ b/src/codec/SkCodec_libgif.cpp
@@ -582,124 +582,94 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
return kSuccess;
}
-// TODO (msarett): skbug.com/3582
-// Should we implement reallyHasAlpha? Or should we read extension blocks in the
-// header? Or should we do both?
-
-class SkGifScanlineDecoder : public SkScanlineDecoder {
-public:
- SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- {}
-
- SkEncodedFormat onGetEncodedFormat() const override {
- return kGIF_SkEncodedFormat;
- }
-
- SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts,
- SkPMColor inputColorPtr[], int* inputColorCount) override {
- SkCodec::Result result = fCodec->prepareToDecode(dstInfo, inputColorPtr, inputColorCount,
- this->options());
- if (SkCodec::kSuccess != result) {
- return result;
- }
+SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
+ const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) {
+ Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount,
+ this->options());
+ if (kSuccess != result) {
+ return result;
+ }
- // Check to see if scaling was requested.
- if (dstInfo.dimensions() != this->getInfo().dimensions()) {
- if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return gif_error("Scaling not supported.\n", SkCodec::kInvalidScale);
- }
+ // Check to see if scaling was requested.
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
+ return gif_error("Scaling not supported.\n", SkCodec::kInvalidScale);
}
+ }
- // Initialize the swizzler
- if (fCodec->fFrameIsSubset) {
- int sampleX;
- SkScaledCodec::ComputeSampleSize(dstInfo, fCodec->getInfo(), &sampleX, NULL);
- const SkImageInfo subsetDstInfo = dstInfo.makeWH(
- get_scaled_dimension(fCodec->fFrameDims.width(), sampleX),
- fCodec->fFrameDims.height());
- if (SkCodec::kSuccess != fCodec->initializeSwizzler(subsetDstInfo,
- opts.fZeroInitialized)) {
- return gif_error("Could not initialize swizzler.\n", SkCodec::kUnimplemented);
- }
- } else {
- if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZeroInitialized)) {
- return gif_error("Could not initialize swizzler.\n", SkCodec::kUnimplemented);
- }
+ // Initialize the swizzler
+ if (fFrameIsSubset) {
+ int sampleX;
+ SkScaledCodec::ComputeSampleSize(dstInfo, this->getInfo(), &sampleX, NULL);
+ const SkImageInfo subsetDstInfo = dstInfo.makeWH(
+ get_scaled_dimension(fFrameDims.width(), sampleX),
+ fFrameDims.height());
+ if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitialized)) {
+ return gif_error("Could not initialize swizzler.\n", kUnimplemented);
+ }
+ } else {
+ if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized)) {
+ return gif_error("Could not initialize swizzler.\n", kUnimplemented);
}
-
- return SkCodec::kSuccess;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
- if (fCodec->fFrameIsSubset) {
- // Fill the requested rows
- const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get());
- SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFillIndex,
- colorPtr, this->options().fZeroInitialized);
-
- // Do nothing for rows before the image frame
- int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY();
- if (rowsBeforeFrame > 0) {
- count = SkTMin(0, count - rowsBeforeFrame);
- dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
- }
-
- // Do nothing for rows after the image frame
- int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims.bottom();
- if (rowsAfterFrame > 0) {
- count = SkTMin(0, count - rowsAfterFrame);
- }
+ return kSuccess;
+}
- // Adjust dst pointer for left offset
- dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel(
- this->dstInfo().colorType()) * fCodec->fFrameDims.left());
+SkCodec::Result SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+ if (fFrameIsSubset) {
+ // Fill the requested rows
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+ SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fFillIndex,
+ colorPtr, this->options().fZeroInitialized);
+
+ // Do nothing for rows before the image frame
+ // FIXME: nextScanline is not virtual, so using "INHERITED" does not change
+ // behavior. Was the intent to call this->INHERITED::onNextScanline()? Same
+ // for the next call down below.
+ int rowsBeforeFrame = fFrameDims.top() - this->INHERITED::nextScanline();
+ if (rowsBeforeFrame > 0) {
+ count = SkTMin(0, count - rowsBeforeFrame);
+ dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
}
- for (int i = 0; i < count; i++) {
- if (SkCodec::kSuccess != fCodec->readRow()) {
- const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get());
- SkSwizzler::Fill(dst, this->dstInfo(), rowBytes,
- count - i, fCodec->fFillIndex, colorPtr,
- this->options().fZeroInitialized);
- return gif_error("Could not decode line\n", SkCodec::kIncompleteInput);
- }
- fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get());
- dst = SkTAddOffset<void>(dst, rowBytes);
+ // Do nothing for rows after the image frame
+ int rowsAfterFrame = this->INHERITED::nextScanline() + count - fFrameDims.bottom();
+ if (rowsAfterFrame > 0) {
+ count = SkTMin(0, count - rowsAfterFrame);
}
- return SkCodec::kSuccess;
- }
- SkScanlineOrder onGetScanlineOrder() const override {
- if (fCodec->fGif->Image.Interlace) {
- return kOutOfOrder_SkScanlineOrder;
- } else {
- return kTopDown_SkScanlineOrder;
- }
+ // Adjust dst pointer for left offset
+ int bpp = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameDims.left();
+ dst = SkTAddOffset<void>(dst, bpp);
}
- int onGetY() const override {
- if (fCodec->fGif->Image.Interlace) {
- return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo().height());
- } else {
- return INHERITED::onGetY();
+ for (int i = 0; i < count; i++) {
+ if (kSuccess != this->readRow()) {
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+ SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count - i, fFillIndex, colorPtr,
+ this->options().fZeroInitialized);
+ return gif_error("Could not decode line\n", SkCodec::kIncompleteInput);
}
+ fSwizzler->swizzle(dst, fSrcBuffer.get());
+ dst = SkTAddOffset<void>(dst, rowBytes);
}
+ return SkCodec::kSuccess;
+}
-private:
- SkAutoTDelete<SkGifCodec> fCodec;
-
- typedef SkScanlineDecoder INHERITED;
-};
-
-SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFromStream(stream)));
- if (!codec) {
- return NULL;
+SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const {
+ if (fGif->Image.Interlace) {
+ return kOutOfOrder_SkScanlineOrder;
+ } else {
+ return kTopDown_SkScanlineOrder;
}
+}
- const SkImageInfo& srcInfo = codec->getInfo();
-
- return new SkGifScanlineDecoder(srcInfo, codec.detach());
+int SkGifCodec::onNextScanline() const {
+ if (fGif->Image.Interlace) {
+ return get_output_row_interlaced(this->INHERITED::onNextScanline(), this->dstInfo().height());
+ } else {
+ return this->INHERITED::onNextScanline();
+ }
}
+
diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h
index 8845536851..3999c41ede 100644
--- a/src/codec/SkCodec_libgif.h
+++ b/src/codec/SkCodec_libgif.h
@@ -8,7 +8,6 @@
#include "SkCodec.h"
#include "SkColorTable.h"
#include "SkImageInfo.h"
-#include "SkScanlineDecoder.h"
#include "SkSwizzler.h"
#include "gif_lib.h"
@@ -33,8 +32,6 @@ public:
*/
static SkCodec* NewFromStream(SkStream*);
- static SkScanlineDecoder* NewSDFromStream(SkStream* stream);
-
protected:
/*
@@ -83,7 +80,7 @@ private:
* @param transIndex This call will set the transparent index based on the
* extension data.
*/
- static SkCodec::Result ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex);
+ static Result ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex);
/*
* A gif may contain many image frames, all of different sizes.
@@ -114,7 +111,7 @@ private:
* Checks for invalid inputs and calls rewindIfNeeded(), setFramDimensions(), and
* initializeColorTable() in the proper sequence.
*/
- SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
+ Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
int* inputColorCount, const Options& opts);
/*
@@ -125,14 +122,22 @@ private:
* indicated in the header.
* @param zeroInit Indicates if destination memory is zero initialized.
*/
- SkCodec::Result initializeSwizzler(const SkImageInfo& dstInfo,
- ZeroInitialized zeroInit);
+ Result initializeSwizzler(const SkImageInfo& dstInfo, ZeroInitialized zeroInit);
/*
* @return kSuccess if the read is successful and kIncompleteInput if the
* read fails.
*/
- SkCodec::Result readRow();
+ Result readRow();
+
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opts,
+ SkPMColor inputColorPtr[], int* inputColorCount) override;
+
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override;
+
+ SkScanlineOrder onGetScanlineOrder() const override;
+
+ int onNextScanline() const override;
/*
* This function cleans up the gif object after the decode completes
@@ -168,7 +173,5 @@ private:
SkAutoTDelete<SkSwizzler> fSwizzler;
SkAutoTUnref<SkColorTable> fColorTable;
- friend class SkGifScanlineDecoder;
-
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 2ba2d5bb9f..faba79f6a2 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -12,7 +12,6 @@
#include "SkBitmap.h"
#include "SkMath.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkSize.h"
#include "SkStream.h"
#include "SkSwizzler.h"
@@ -163,7 +162,10 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
palette++;
}
- fReallyHasAlpha = transLessThanFF < 0;
+ if (transLessThanFF >= 0) {
+ // No transparent colors were found.
+ fAlphaState = kOpaque_AlphaState;
+ }
for (; index < numPalette; index++) {
*colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
@@ -216,7 +218,8 @@ bool SkPngCodec::IsPng(SkStream* stream) {
// png_destroy_read_struct. If it returns false, the passed in fields (except
// stream) are unchanged.
static bool read_header(SkStream* stream, png_structp* png_ptrp,
- png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) {
+ png_infop* info_ptrp, SkImageInfo* imageInfo,
+ int* bitDepthPtr, int* numberPassesPtr) {
// The image is known to be a PNG. Decode enough to know the SkImageInfo.
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
sk_error_fn, sk_warning_fn);
@@ -333,6 +336,11 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp,
SkASSERT(false);
}
+ int numberPasses = png_set_interlace_handling(png_ptr);
+ if (numberPassesPtr) {
+ *numberPassesPtr = numberPasses;
+ }
+
// FIXME: Also need to check for sRGB (skbug.com/3471).
if (imageInfo) {
@@ -349,29 +357,21 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp,
return true;
}
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
- SkAutoTDelete<SkStream> streamDeleter(stream);
- png_structp png_ptr;
- png_infop info_ptr;
- SkImageInfo imageInfo;
- int bitDepth;
- if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) {
- return new SkPngCodec(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, bitDepth);
- }
- return nullptr;
-}
-
-#define INVALID_NUMBER_PASSES -1
SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
- png_structp png_ptr, png_infop info_ptr, int bitDepth)
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
: INHERITED(info, stream)
, fPng_ptr(png_ptr)
, fInfo_ptr(info_ptr)
, fSrcConfig(SkSwizzler::kUnknown)
- , fNumberPasses(INVALID_NUMBER_PASSES)
- , fReallyHasAlpha(false)
+ , fNumberPasses(numberPasses)
, fBitDepth(bitDepth)
-{}
+{
+ if (info.alphaType() == kOpaque_SkAlphaType) {
+ fAlphaState = kOpaque_AlphaState;
+ } else {
+ fAlphaState = kUnknown_AlphaState;
+ }
+}
SkPngCodec::~SkPngCodec() {
this->destroyReadStruct();
@@ -401,13 +401,9 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
SkCodecPrintf("setjmp long jump!\n");
return kInvalidInput;
}
- fNumberPasses = png_set_interlace_handling(fPng_ptr);
png_read_update_info(fPng_ptr, fInfo_ptr);
- // Set to the default before calling decodePalette, which may change it.
- fReallyHasAlpha = false;
-
- //srcColorType was determined in readHeader() which determined png color type
+ //srcColorType was determined in read_header() which determined png color type
const SkColorType srcColorType = this->getInfo().colorType();
switch (srcColorType) {
@@ -459,7 +455,7 @@ bool SkPngCodec::onRewind() {
png_structp png_ptr;
png_infop info_ptr;
- if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr)) {
+ if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) {
return false;
}
@@ -498,7 +494,8 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
return kInvalidInput;
}
- SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
+ bool hasAlpha = false;
+ // FIXME: We could split these out based on subclass.
SkAutoMalloc storage;
void* dstRow = dst;
if (fNumberPasses > 1) {
@@ -522,7 +519,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
// Now swizzle it.
uint8_t* srcRow = base;
for (int y = 0; y < height; y++) {
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
srcRow += srcRowBytes;
}
@@ -531,11 +528,18 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
for (int y = 0; y < requestedInfo.height(); y++) {
png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
+ // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines.
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
}
}
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ fAlphaState = kOpaque_AlphaState;
+ }
+
// FIXME: do we need substituteTranspColor? Note that we cannot do it for
// scanline decoding, but we could do it here. Alternatively, we could do
// it as we go, instead of in post-processing like SkPNGImageDecoder.
@@ -547,136 +551,170 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
// read rest of file, and get additional comment and time chunks in info_ptr
png_read_end(fPng_ptr, fInfo_ptr);
+
return kSuccess;
}
-class SkPngScanlineDecoder : public SkScanlineDecoder {
+bool SkPngCodec::onReallyHasAlpha() const {
+ switch (fAlphaState) {
+ case kOpaque_AlphaState:
+ return false;
+ case kUnknown_AlphaState:
+ // Maybe the subclass knows?
+ return this->alphaInScanlineDecode() == kHasAlpha_AlphaState;
+ case kHasAlpha_AlphaState:
+ switch (this->alphaInScanlineDecode()) {
+ case kUnknown_AlphaState:
+ // Scanline decoder must not have been used. Return our knowledge.
+ return true;
+ case kOpaque_AlphaState:
+ // Scanline decoder was used, and did not find alpha in its subset.
+ return false;
+ case kHasAlpha_AlphaState:
+ return true;
+ }
+ }
+
+ // All valid AlphaStates have been covered, so this should not be reached.
+ SkASSERT(false);
+ return true;
+}
+
+// Subclass of SkPngCodec which supports scanline decoding
+class SkPngScanlineDecoder : public SkPngCodec {
public:
- SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- , fHasAlpha(false)
+ SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
+ png_structp png_ptr, png_infop info_ptr, int bitDepth)
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1)
+ , fSrcRow(nullptr)
+ , fAlphaState(kUnknown_AlphaState)
{}
- SkCodec::Result onStart(const SkImageInfo& dstInfo,
- const SkCodec::Options& options,
- SkPMColor ctable[], int* ctableCount) override {
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override {
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
- return SkCodec::kInvalidConversion;
+ return kInvalidConversion;
}
// Check to see if scaling was requested.
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
+ return kInvalidScale;
}
}
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
- ctableCount);
- if (result != SkCodec::kSuccess) {
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable,
+ ctableCount);
+ if (result != kSuccess) {
return result;
}
- fHasAlpha = false;
- fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
+ fAlphaState = kUnknown_AlphaState;
+ fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()));
fSrcRow = static_cast<uint8_t*>(fStorage.get());
- return SkCodec::kSuccess;
+ return kSuccess;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return kInvalidInput;
}
void* dstRow = dst;
+ bool hasAlpha = false;
for (int i = 0; i < count; i++) {
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow));
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow));
dstRow = SkTAddOffset<void>(dstRow, rowBytes);
}
- return SkCodec::kSuccess;
+
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ if (kUnknown_AlphaState == fAlphaState) {
+ fAlphaState = kOpaque_AlphaState;
+ }
+ // Otherwise, the AlphaState is unchanged.
+ }
+
+ return kSuccess;
}
- SkCodec::Result onSkipScanlines(int count) override {
+ Result onSkipScanlines(int count) override {
// FIXME: Could we use the return value of setjmp to specify the type of
// error?
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return kInvalidInput;
}
//there is a potential tradeoff of memory vs speed created by putting this in a loop.
//calling png_read_rows in a loop is insignificantly slower than calling it once with count
//as png_read_rows has it's own loop which calls png_read_row count times.
for (int i = 0; i < count; i++) {
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
}
return SkCodec::kSuccess;
}
- bool onReallyHasAlpha() const override { return fHasAlpha; }
-
- SkEncodedFormat onGetEncodedFormat() const override {
- return kPNG_SkEncodedFormat;
+ AlphaState alphaInScanlineDecode() const override {
+ return fAlphaState;
}
-
private:
- SkAutoTDelete<SkPngCodec> fCodec;
- bool fHasAlpha;
+ AlphaState fAlphaState;
SkAutoMalloc fStorage;
uint8_t* fSrcRow;
- typedef SkScanlineDecoder INHERITED;
+ typedef SkPngCodec INHERITED;
};
-class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
+class SkPngInterlacedScanlineDecoder : public SkPngCodec {
public:
- SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- , fHasAlpha(false)
- , fCurrentRow(0)
- , fHeight(srcInfo.height())
+ SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses)
+ , fAlphaState(kUnknown_AlphaState)
+ , fHeight(-1)
, fCanSkipRewind(false)
- {}
+ {
+ SkASSERT(numberPasses != 1);
+ }
- SkCodec::Result onStart(const SkImageInfo& dstInfo,
- const SkCodec::Options& options,
- SkPMColor ctable[], int* ctableCount) override
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override
{
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
- return SkCodec::kInvalidConversion;
+ return kInvalidConversion;
}
// Check to see if scaling was requested.
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
+ return kInvalidScale;
}
}
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
- ctableCount);
- if (result != SkCodec::kSuccess) {
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable,
+ ctableCount);
+ if (result != kSuccess) {
return result;
}
- fHasAlpha = false;
- fCurrentRow = 0;
+ fAlphaState = kUnknown_AlphaState;
fHeight = dstInfo.height();
- fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
+ // FIXME: This need not be called on a second call to onStartScanlineDecode.
+ fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig());
fGarbageRow.reset(fSrcRowBytes);
fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
fCanSkipRewind = true;
@@ -684,73 +722,90 @@ public:
return SkCodec::kSuccess;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
+ Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
// rewind stream if have previously called onGetScanlines,
// since we need entire progressive image to get scanlines
if (fCanSkipRewind) {
- // We already rewound in onStart, so there is no reason to rewind.
+ // We already rewound in onStartScanlineDecode, so there is no reason to rewind.
// Next time onGetScanlines is called, we will need to rewind.
fCanSkipRewind = false;
- } else if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ } else {
+ // rewindIfNeeded resets fCurrScanline, since it assumes that start
+ // needs to be called again before scanline decoding. PNG scanline
+ // decoding is the exception, since it needs to rewind between
+ // calls to getScanlines. Keep track of fCurrScanline, to undo the
+ // reset.
+ const int currScanline = this->onNextScanline();
+ // This method would never be called if currScanline is -1
+ SkASSERT(currScanline != -1);
+
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
+ }
+ this->updateNextScanline(currScanline);
}
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return kInvalidInput;
}
- const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
SkAutoMalloc storage(count * fSrcRowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
uint8_t* srcRow;
- for (int i = 0; i < number_passes; i++) {
- //read rows we planned to skip into garbage row
- for (int y = 0; y < fCurrentRow; y++){
- png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
+ const int startRow = this->onNextScanline();
+ for (int i = 0; i < this->numberPasses(); i++) {
+ // read rows we planned to skip into garbage row
+ for (int y = 0; y < startRow; y++){
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
}
- //read rows we care about into buffer
+ // read rows we care about into buffer
srcRow = storagePtr;
for (int y = 0; y < count; y++) {
- png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
+ png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1);
srcRow += fSrcRowBytes;
}
- //read rows we don't want into garbage buffer
- for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
- png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
+ // read rows we don't want into garbage buffer
+ for (int y = 0; y < fHeight - startRow - count; y++) {
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
}
}
//swizzle the rows we care about
srcRow = storagePtr;
void* dstRow = dst;
+ bool hasAlpha = false;
for (int y = 0; y < count; y++) {
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow));
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
srcRow += fSrcRowBytes;
}
- fCurrentRow += count;
- return SkCodec::kSuccess;
+
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ if (kUnknown_AlphaState == fAlphaState) {
+ fAlphaState = kOpaque_AlphaState;
+ }
+ // Otherwise, the AlphaState is unchanged.
+ }
+
+ return kSuccess;
}
SkCodec::Result onSkipScanlines(int count) override {
- //when ongetScanlines is called it will skip to fCurrentRow
- fCurrentRow += count;
+ // The non-virtual version will update fCurrScanline.
return SkCodec::kSuccess;
}
- bool onReallyHasAlpha() const override { return fHasAlpha; }
+ AlphaState alphaInScanlineDecode() const override {
+ return fAlphaState;
+ }
SkScanlineOrder onGetScanlineOrder() const override {
return kNone_SkScanlineOrder;
}
- SkEncodedFormat onGetEncodedFormat() const override {
- return kPNG_SkEncodedFormat;
- }
-
private:
- SkAutoTDelete<SkPngCodec> fCodec;
- bool fHasAlpha;
- int fCurrentRow;
+ AlphaState fAlphaState;
int fHeight;
size_t fSrcRowBytes;
SkAutoMalloc fGarbageRow;
@@ -759,30 +814,33 @@ private:
// is called whenever some action is taken that reads the stream and
// therefore the next call will require a rewind. So it modifies a boolean
// to note that the *next* time it is called a rewind is needed.
- // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart
- // followed by onGetScanlines does *not* require a rewind. Since
- // rewindIfNeeded does not have this flexibility, we need to add another
- // layer.
+ // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling
+ // onStartScanlineDecode followed by onGetScanlines does *not* require a
+ // rewind. Since rewindIfNeeded does not have this flexibility, we need to
+ // add another layer.
bool fCanSkipRewind;
- typedef SkScanlineDecoder INHERITED;
+ typedef SkPngCodec INHERITED;
};
-SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream)));
- if (!codec) {
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+ SkAutoTDelete<SkStream> streamDeleter(stream);
+ png_structp png_ptr;
+ png_infop info_ptr;
+ SkImageInfo imageInfo;
+ int bitDepth;
+ int numberPasses;
+
+ if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) {
return nullptr;
}
- codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr);
- SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES);
-
- const SkImageInfo& srcInfo = codec->getInfo();
- if (codec->fNumberPasses > 1) {
- // interlaced image
- return new SkPngInterlacedScanlineDecoder(srcInfo, codec.detach());
+ if (1 == numberPasses) {
+ return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr,
+ bitDepth);
}
- return new SkPngScanlineDecoder(srcInfo, codec.detach());
+ return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr,
+ info_ptr, bitDepth, numberPasses);
}
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index e8807499ea..8ef1ae2c17 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -36,7 +36,31 @@ protected:
override;
SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; }
bool onRewind() override;
- bool onReallyHasAlpha() const override { return fReallyHasAlpha; }
+ bool onReallyHasAlpha() const final;
+
+ // Helper to set up swizzler and color table. Also calls png_read_update_info.
+ Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&,
+ SkPMColor*, int* ctableCount);
+
+ SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int, int);
+
+ png_structp png_ptr() { return fPng_ptr; }
+ SkSwizzler* swizzler() { return fSwizzler; }
+ SkSwizzler::SrcConfig srcConfig() const { return fSrcConfig; }
+ int numberPasses() const { return fNumberPasses; }
+
+ enum AlphaState {
+ // This class has done no decoding, or threw away its knowledge (in
+ // scanline decodes).
+ kUnknown_AlphaState,
+ // This class found the image (possibly partial, in the case of a
+ // scanline decode) to be opaque.
+ kOpaque_AlphaState,
+ // Ths class found the image to have alpha.
+ kHasAlpha_AlphaState,
+ };
+
+ virtual AlphaState alphaInScanlineDecode() const = 0;
private:
png_structp fPng_ptr;
@@ -47,21 +71,13 @@ private:
SkAutoTDelete<SkSwizzler> fSwizzler;
SkSwizzler::SrcConfig fSrcConfig;
- int fNumberPasses;
- bool fReallyHasAlpha;
+ const int fNumberPasses;
int fBitDepth;
- SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int);
-
- // Helper to set up swizzler and color table. Also calls png_read_update_info.
- Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&,
- SkPMColor*, int* ctableCount);
+ AlphaState fAlphaState;
bool decodePalette(bool premultiply, int* ctableCount);
void destroyReadStruct();
- friend class SkPngScanlineDecoder;
- friend class SkPngInterlacedScanlineDecoder;
-
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
index 22f2bacce0..0c85eada7c 100644
--- a/src/codec/SkCodec_wbmp.cpp
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -96,6 +96,8 @@ SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) {
SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
: INHERITED(info, stream)
, fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
+ , fColorTable(nullptr)
+ , fSwizzler(nullptr)
{}
SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
@@ -120,7 +122,7 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
}
if (!valid_alpha(info.alphaType(), this->getInfo().alphaType())) {
- return SkCodec::kInvalidConversion;
+ return kInvalidConversion;
}
// Prepare a color table if necessary
@@ -163,90 +165,55 @@ SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
return new SkWbmpCodec(info, streamDeleter.detach());
}
-class SkWbmpScanlineDecoder : public SkScanlineDecoder {
-public:
- /*
- * Takes ownership of all pointer paramters.
- */
- SkWbmpScanlineDecoder(SkWbmpCodec* codec)
- : INHERITED(codec->getInfo())
- , fCodec(codec)
- , fColorTable(nullptr)
- , fSwizzler(nullptr)
- , fSrcBuffer(codec->fSrcRowBytes)
- {}
-
- SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
- void* dstRow = dst;
- for (int y = 0; y < count; ++y) {
- SkCodec::Result rowResult = fCodec->readRow(fSrcBuffer.get());
- if (SkCodec::kSuccess != rowResult) {
- return rowResult;
- }
- fSwizzler->swizzle(dstRow, fSrcBuffer.get());
- dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
+SkCodec::Result SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
+ void* dstRow = dst;
+ for (int y = 0; y < count; ++y) {
+ Result rowResult = this->readRow(fSrcBuffer.get());
+ if (kSuccess != rowResult) {
+ return rowResult;
}
- return SkCodec::kSuccess;
+ fSwizzler->swizzle(dstRow, fSrcBuffer.get());
+ dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
}
+ return kSuccess;
+}
- SkCodec::Result onStart(const SkImageInfo& dstInfo,
- const SkCodec::Options& options, SkPMColor inputColorTable[],
- int* inputColorCount) {
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
- }
- if (options.fSubset) {
- // Subsets are not supported.
- return SkCodec::kUnimplemented;
- }
- if (dstInfo.dimensions() != this->getInfo().dimensions()) {
- if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
- }
- }
-
- if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) {
- return SkCodec::kInvalidConversion;
- }
-
- // Fill in the color table
- setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount);
-
- // Copy the color table to a pointer that can be owned by the scanline decoder
- if (kIndex_8_SkColorType == dstInfo.colorType()) {
- fColorTable.reset(new SkColorTable(inputColorTable, 2));
- }
-
- // Initialize the swizzler
- fSwizzler.reset(fCodec->initializeSwizzler(dstInfo,
- get_color_ptr(fColorTable.get()), options));
- if (nullptr == fSwizzler.get()) {
- return SkCodec::kInvalidConversion;
+SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
+ const Options& options, SkPMColor inputColorTable[], int* inputColorCount) {
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
+ }
+ if (options.fSubset) {
+ // Subsets are not supported.
+ return kUnimplemented;
+ }
+ if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+ if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
+ return kInvalidScale;
}
-
- return SkCodec::kSuccess;
}
- SkEncodedFormat onGetEncodedFormat() const {
- return kWBMP_SkEncodedFormat;
+ if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) {
+ return kInvalidConversion;
}
-private:
- SkAutoTDelete<SkWbmpCodec> fCodec;
- SkAutoTUnref<SkColorTable> fColorTable;
- SkAutoTDelete<SkSwizzler> fSwizzler;
- SkAutoTMalloc<uint8_t> fSrcBuffer;
+ // Fill in the color table
+ setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount);
- typedef SkScanlineDecoder INHERITED;
-};
+ // Copy the color table to a pointer that can be owned by the scanline decoder
+ if (kIndex_8_SkColorType == dstInfo.colorType()) {
+ fColorTable.reset(new SkColorTable(inputColorTable, 2));
+ }
-SkScanlineDecoder* SkWbmpCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkWbmpCodec> codec(static_cast<SkWbmpCodec*>(
- SkWbmpCodec::NewFromStream(stream)));
- if (!codec) {
- return nullptr;
+ // Initialize the swizzler
+ fSwizzler.reset(this->initializeSwizzler(dstInfo,
+ get_color_ptr(fColorTable.get()), options));
+ if (nullptr == fSwizzler.get()) {
+ return kInvalidConversion;
}
- // Return the new scanline decoder
- return new SkWbmpScanlineDecoder(codec.detach());
+ fSrcBuffer.reset(fSrcRowBytes);
+
+ return kSuccess;
}
+
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
index 0891eb86b9..220570b0db 100644
--- a/src/codec/SkCodec_wbmp.h
+++ b/src/codec/SkCodec_wbmp.h
@@ -9,7 +9,6 @@
#define SkCodec_wbmp_DEFINED
#include "SkCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkSwizzler.h"
class SkWbmpCodec final : public SkCodec {
@@ -23,12 +22,6 @@ public:
*/
static SkCodec* NewFromStream(SkStream*);
- /*
- * Assumes IsWbmp was called and returned true
- * Creates a wbmp scanline decoder
- * Takes ownership of the stream
- */
- static SkScanlineDecoder* NewSDFromStream(SkStream*);
protected:
SkEncodedFormat onGetEncodedFormat() const override;
Result onGetPixels(const SkImageInfo&, void*, size_t,
@@ -50,7 +43,16 @@ private:
const size_t fSrcRowBytes;
- friend class SkWbmpScanlineDecoder;
+ // Used for scanline decodes:
+ SkAutoTUnref<SkColorTable> fColorTable;
+ SkAutoTDelete<SkSwizzler> fSwizzler;
+ SkAutoTMalloc<uint8_t> fSrcBuffer;
+
+ // FIXME: Override onSkipScanlines to avoid swizzling.
+ Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor inputColorTable[], int* inputColorCount) override;
+
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 4557e45673..dac0c17b77 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -12,7 +12,6 @@
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkTypes.h"
@@ -382,160 +381,136 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
return kSuccess;
}
-/*
- * Enable scanline decoding for jpegs
- */
-class SkJpegScanlineDecoder : public SkScanlineDecoder {
-public:
- SkJpegScanlineDecoder(const SkImageInfo& srcInfo, SkJpegCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- , fOpts()
- {}
-
- /*
- * Return a valid set of output dimensions for this decoder, given an input scale
- */
- SkISize onGetScaledDimensions(float desiredScale) override {
- return fCodec->onGetScaledDimensions(desiredScale);
+SkCodec::Result SkJpegCodec::initializeSwizzler(const SkImageInfo& info, const Options& options) {
+ SkSwizzler::SrcConfig srcConfig;
+ switch (info.colorType()) {
+ case kGray_8_SkColorType:
+ srcConfig = SkSwizzler::kGray;
+ break;
+ case kRGBA_8888_SkColorType:
+ srcConfig = SkSwizzler::kRGBX;
+ break;
+ case kBGRA_8888_SkColorType:
+ srcConfig = SkSwizzler::kBGRX;
+ break;
+ case kRGB_565_SkColorType:
+ srcConfig = SkSwizzler::kRGB_565;
+ break;
+ default:
+ // This function should only be called if the colorType is supported by jpeg
+ SkASSERT(false);
}
- /*
- * Create the swizzler based on the encoded format.
- * The swizzler is only used for sampling in the x direction.
- */
-
- SkCodec::Result initializeSwizzler(const SkImageInfo& info, const SkCodec::Options& options) {
- SkSwizzler::SrcConfig srcConfig;
- switch (info.colorType()) {
- case kGray_8_SkColorType:
- srcConfig = SkSwizzler::kGray;
- break;
- case kRGBA_8888_SkColorType:
- srcConfig = SkSwizzler::kRGBX;
- break;
- case kBGRA_8888_SkColorType:
- srcConfig = SkSwizzler::kBGRX;
- break;
- case kRGB_565_SkColorType:
- srcConfig = SkSwizzler::kRGB_565;
- break;
- default:
- //would have exited before now if the colorType was supported by jpeg
- SkASSERT(false);
- }
-
- fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info, options.fZeroInitialized,
- this->getInfo()));
- if (!fSwizzler) {
- // FIXME: CreateSwizzler could fail for another reason.
- return SkCodec::kUnimplemented;
- }
- return SkCodec::kSuccess;
+ fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, info, options.fZeroInitialized,
+ this->getInfo()));
+ if (!fSwizzler) {
+ return SkCodec::kUnimplemented;
}
- SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options,
- SkPMColor ctable[], int* ctableCount) override {
+ return kSuccess;
+}
- // Rewind the stream if needed
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
- }
+SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
+ const Options& options, SkPMColor ctable[], int* ctableCount) {
+ // Rewind the stream if needed
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
+ }
- // Set the jump location for libjpeg errors
- if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
- SkCodecPrintf("setjmp: Error from libjpeg\n");
- return SkCodec::kInvalidInput;
- }
+ // Set the jump location for libjpeg errors
+ if (setjmp(fDecoderMgr->getJmpBuf())) {
+ SkCodecPrintf("setjmp: Error from libjpeg\n");
+ return kInvalidInput;
+ }
- // Check if we can decode to the requested destination and set the output color space
- if (!fCodec->setOutputColorSpace(dstInfo)) {
- return SkCodec::kInvalidConversion;
- }
+ // Check if we can decode to the requested destination and set the output color space
+ if (!this->setOutputColorSpace(dstInfo)) {
+ return kInvalidConversion;
+ }
- // Perform the necessary scaling
- if (!fCodec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
- // full native scaling to dstInfo dimensions not supported
+ // Perform the necessary scaling
+ if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
+ // full native scaling to dstInfo dimensions not supported
- if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
- }
- // create swizzler for sampling
- SkCodec::Result result = this->initializeSwizzler(dstInfo, options);
- if (SkCodec::kSuccess != result) {
- SkCodecPrintf("failed to initialize the swizzler.\n");
- return result;
- }
- fStorage.reset(get_row_bytes(fCodec->fDecoderMgr->dinfo()));
- fSrcRow = static_cast<uint8_t*>(fStorage.get());
- } else {
- fSrcRow = nullptr;
- fSwizzler.reset(nullptr);
+ if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
+ return kInvalidScale;
}
-
- // Now, given valid output dimensions, we can start the decompress
- if (!jpeg_start_decompress(fCodec->fDecoderMgr->dinfo())) {
- SkCodecPrintf("start decompress failed\n");
- return SkCodec::kInvalidInput;
+ // create swizzler for sampling
+ Result result = this->initializeSwizzler(dstInfo, options);
+ if (kSuccess != result) {
+ SkCodecPrintf("failed to initialize the swizzler.\n");
+ return result;
}
+ fStorage.reset(get_row_bytes(fDecoderMgr->dinfo()));
+ fSrcRow = static_cast<uint8_t*>(fStorage.get());
+ } else {
+ fSrcRow = nullptr;
+ fSwizzler.reset(nullptr);
+ }
+
+ // Now, given valid output dimensions, we can start the decompress
+ if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
+ SkCodecPrintf("start decompress failed\n");
+ return kInvalidInput;
+ }
- fOpts = options;
+ return kSuccess;
+}
- return SkCodec::kSuccess;
+SkJpegCodec::~SkJpegCodec() {
+ // FIXME: This probably does not need to be called after a full decode
+ // FIXME: Is it safe to call when it doesn't need to be called?
+ if (setjmp(fDecoderMgr->getJmpBuf())) {
+ SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
+ return;
}
- virtual ~SkJpegScanlineDecoder() {
- if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
- SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
- return;
- }
+ // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a
+ // partial decode.
+ fDecoderMgr->dinfo()->output_scanline = this->getInfo().height();
+ jpeg_finish_decompress(fDecoderMgr->dinfo());
+}
- // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a
- // partial decode.
- fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height();
- jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo());
+SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+ // Set the jump location for libjpeg errors
+ if (setjmp(fDecoderMgr->getJmpBuf())) {
+ return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
+ }
+ // Read rows one at a time
+ JSAMPLE* dstRow;
+ if (fSwizzler) {
+ // write data to storage row, then sample using swizzler
+ dstRow = fSrcRow;
+ } else {
+ // write data directly to dst
+ dstRow = (JSAMPLE*) dst;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
- // Set the jump location for libjpeg errors
- if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
- return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvalidInput);
- }
- // Read rows one at a time
- JSAMPLE* dstRow;
- if (fSwizzler) {
- // write data to storage row, then sample using swizzler
- dstRow = fSrcRow;
- } else {
- // write data directly to dst
- dstRow = (JSAMPLE*) dst;
+ for (int y = 0; y < count; y++) {
+ // Read row of the image
+ uint32_t rowsDecoded = jpeg_read_scanlines(fDecoderMgr->dinfo(), &dstRow, 1);
+ if (rowsDecoded != 1) {
+ SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, count - y,
+ SK_ColorBLACK, nullptr, this->options().fZeroInitialized);
+ fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height();
+ return kIncompleteInput;
}
- for (int y = 0; y < count; y++) {
- // Read row of the image
- uint32_t rowsDecoded = jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dstRow, 1);
- if (rowsDecoded != 1) {
- SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, count - y,
- SK_ColorBLACK, nullptr, fOpts.fZeroInitialized);
- fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height();
- return SkCodec::kIncompleteInput;
- }
-
- // Convert to RGBA if necessary
- if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) {
- convert_CMYK_to_RGBA(dstRow, fCodec->fDecoderMgr->dinfo()->output_width);
- }
+ // Convert to RGBA if necessary
+ if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) {
+ convert_CMYK_to_RGBA(dstRow, fDecoderMgr->dinfo()->output_width);
+ }
- if(fSwizzler) {
- // use swizzler to sample row
- fSwizzler->swizzle(dst, dstRow);
- dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
- } else {
- dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes);
- }
+ if(fSwizzler) {
+ // use swizzler to sample row
+ fSwizzler->swizzle(dst, dstRow);
+ dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
+ } else {
+ dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes);
}
- return SkCodec::kSuccess;
}
+ return kSuccess;
+}
#ifndef TURBO_HAS_SKIP
// TODO (msarett): Make this a member function and avoid reallocating the
@@ -548,39 +523,14 @@ public:
}
#endif
- SkCodec::Result onSkipScanlines(int count) override {
- // Set the jump location for libjpeg errors
- if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
- return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvalidInput);
- }
-
- jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count);
-
- return SkCodec::kSuccess;
- }
-
- SkEncodedFormat onGetEncodedFormat() const override {
- return kJPEG_SkEncodedFormat;
- }
-
-private:
- SkAutoTDelete<SkJpegCodec> fCodec;
- SkAutoMalloc fStorage; // Only used if sampling is needed
- uint8_t* fSrcRow; // Only used if sampling is needed
- SkCodec::Options fOpts;
- SkAutoTDelete<SkSwizzler> fSwizzler;
-
- typedef SkScanlineDecoder INHERITED;
-};
-
-SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkJpegCodec> codec(static_cast<SkJpegCodec*>(SkJpegCodec::NewFromStream(stream)));
- if (!codec) {
- return nullptr;
+SkCodec::Result SkJpegCodec::onSkipScanlines(int count) {
+ // Set the jump location for libjpeg errors
+ if (setjmp(fDecoderMgr->getJmpBuf())) {
+ return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
}
- const SkImageInfo& srcInfo = codec->getInfo();
+ jpeg_skip_scanlines(fDecoderMgr->dinfo(), count);
- // Return the new scanline decoder
- return new SkJpegScanlineDecoder(srcInfo, codec.detach());
+ return kSuccess;
}
+
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 38e41e757c..b86396317b 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -18,8 +18,6 @@ extern "C" {
#include "jpeglib.h"
}
-class SkScanlineDecoder;
-
/*
*
* This class implements the decoding for jpeg images
@@ -41,13 +39,6 @@ public:
*/
static SkCodec* NewFromStream(SkStream*);
- /*
- * Assumes IsJpeg was called and returned true
- * Creates a jpeg scanline decoder
- * Takes ownership of the stream
- */
- static SkScanlineDecoder* NewSDFromStream(SkStream*);
-
protected:
/*
@@ -102,6 +93,8 @@ private:
*/
SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, JpegDecoderMgr* decoderMgr);
+ ~SkJpegCodec() override;
+
/*
* Checks if the conversion between the input image and the requested output
* image has been implemented
@@ -115,13 +108,23 @@ private:
*/
bool nativelyScaleToDimensions(uint32_t width, uint32_t height);
+ // scanline decoding
+ Result initializeSwizzler(const SkImageInfo&, const SkCodec::Options&);
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override;
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override;
+ Result onSkipScanlines(int count) override;
+
SkAutoTDelete<JpegDecoderMgr> fDecoderMgr;
// We will save the state of the decompress struct after reading the header.
// This allows us to safely call onGetScaledDimensions() at any time.
const int fReadyState;
-
- friend class SkJpegScanlineDecoder;
+ // scanline decoding
+ SkAutoMalloc fStorage; // Only used if sampling is needed
+ uint8_t* fSrcRow; // Only used if sampling is needed
+ SkAutoTDelete<SkSwizzler> fSwizzler;
+
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index 951ab27b1c..36aeda98cf 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -21,13 +21,13 @@ SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) {
return SkWebpCodec::NewFromStream(stream);
}
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromStream(stream));
- if (nullptr == scanlineDecoder) {
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
+ if (nullptr == codec) {
return nullptr;
}
// wrap in new SkScaledCodec
- return new SkScaledCodec(scanlineDecoder.detach());
+ return new SkScaledCodec(codec.detach());
}
SkCodec* SkScaledCodec::NewFromData(SkData* data) {
@@ -37,9 +37,9 @@ SkCodec* SkScaledCodec::NewFromData(SkData* data) {
return NewFromStream(new SkMemoryStream(data));
}
-SkScaledCodec::SkScaledCodec(SkScanlineDecoder* scanlineDecoder)
- : INHERITED(scanlineDecoder->getInfo(), nullptr)
- , fScanlineDecoder(scanlineDecoder)
+SkScaledCodec::SkScaledCodec(SkCodec* codec)
+ : INHERITED(codec->getInfo(), nullptr)
+ , fCodec(codec)
{}
SkScaledCodec::~SkScaledCodec() {}
@@ -78,12 +78,12 @@ static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na
* Return a valid set of output dimensions for this decoder, given an input scale
*/
SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const {
- SkISize nativeDimensions = fScanlineDecoder->getScaledDimensions(desiredScale);
+ SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale);
// support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ...
SkISize scaledCodecDimensions;
if (desiredScale > 0.5f) {
// sampleSize = 1
- scaledCodecDimensions = fScanlineDecoder->getInfo().dimensions();
+ scaledCodecDimensions = fCodec->getInfo().dimensions();
}
// sampleSize determines the step size between samples
// Ex: sampleSize = 2, sample every second pixel in x and y directions
@@ -185,19 +185,20 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
return kUnimplemented;
}
- Result result = fScanlineDecoder->start(requestedInfo, &options, ctable, ctableCount);
+ // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels.
+ Result result = fCodec->startScanlineDecode(requestedInfo, &options, ctable, ctableCount);
if (kSuccess == result) {
// native decode supported
- switch (fScanlineDecoder->getScanlineOrder()) {
- case SkScanlineDecoder::kTopDown_SkScanlineOrder:
- case SkScanlineDecoder::kBottomUp_SkScanlineOrder:
- case SkScanlineDecoder::kNone_SkScanlineOrder:
- return fScanlineDecoder->getScanlines(dst, requestedInfo.height(), rowBytes);
- case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: {
+ switch (fCodec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kBottomUp_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder:
+ return fCodec->getScanlines(dst, requestedInfo.height(), rowBytes);
+ case SkCodec::kOutOfOrder_SkScanlineOrder: {
for (int y = 0; y < requestedInfo.height(); y++) {
- int dstY = fScanlineDecoder->getY();
+ int dstY = fCodec->nextScanline();
void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY);
- result = fScanlineDecoder->getScanlines(dstPtr, 1, rowBytes);
+ result = fCodec->getScanlines(dstPtr, 1, rowBytes);
// FIXME (msarett): Make the SkCodec base class take care of filling
// uninitialized pixels so we can return immediately on kIncompleteInput.
if (kSuccess != result && kIncompleteInput != result) {
@@ -213,42 +214,44 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
// no scaling requested
return result;
}
-
+
// scaling requested
int sampleX;
int sampleY;
- if (!scaling_supported(requestedInfo, fScanlineDecoder->getInfo(), &sampleX, &sampleY)) {
+ if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY)) {
return kInvalidScale;
}
// set first sample pixel in y direction
int Y0 = get_start_coord(sampleY);
int dstHeight = requestedInfo.height();
- int srcHeight = fScanlineDecoder->getInfo().height();
+ int srcHeight = fCodec->getInfo().height();
SkImageInfo info = requestedInfo;
- // use original height as scanlineDecoder does not support y sampling natively
+ // use original height as codec does not support y sampling natively
info = info.makeWH(requestedInfo.width(), srcHeight);
- // update scanlineDecoder with new info
- result = fScanlineDecoder->start(info, &options, ctable, ctableCount);
+ // update codec with new info
+ // FIXME: The previous call to start returned kInvalidScale. This call may
+ // require a rewind. (skbug.com/4284)
+ result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount);
if (kSuccess != result) {
return result;
}
- switch(fScanlineDecoder->getScanlineOrder()) {
- case SkScanlineDecoder::kTopDown_SkScanlineOrder: {
- result = fScanlineDecoder->skipScanlines(Y0);
+ switch(fCodec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder: {
+ result = fCodec->skipScanlines(Y0);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
for (int y = 0; y < dstHeight; y++) {
- result = fScanlineDecoder->getScanlines(dst, 1, rowBytes);
+ result = fCodec->getScanlines(dst, 1, rowBytes);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
if (y < dstHeight - 1) {
- result = fScanlineDecoder->skipScanlines(sampleY - 1);
+ result = fCodec->skipScanlines(sampleY - 1);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
@@ -257,18 +260,18 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
}
return result;
}
- case SkScanlineDecoder::kBottomUp_SkScanlineOrder:
- case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: {
+ case SkCodec::kBottomUp_SkScanlineOrder:
+ case SkCodec::kOutOfOrder_SkScanlineOrder: {
for (int y = 0; y < srcHeight; y++) {
- int srcY = fScanlineDecoder->getY();
+ int srcY = fCodec->nextScanline();
if (is_coord_necessary(srcY, sampleY, dstHeight)) {
void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY));
- result = fScanlineDecoder->getScanlines(dstPtr, 1, rowBytes);
+ result = fCodec->getScanlines(dstPtr, 1, rowBytes);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
} else {
- result = fScanlineDecoder->skipScanlines(1);
+ result = fCodec->skipScanlines(1);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
@@ -276,10 +279,10 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
}
return result;
}
- case SkScanlineDecoder::kNone_SkScanlineOrder: {
+ case SkCodec::kNone_SkScanlineOrder: {
SkAutoMalloc storage(srcHeight * rowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
- result = fScanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes);
+ result = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
if (kSuccess != result && kIncompleteInput != result) {
return result;
}
diff --git a/src/codec/SkScanlineDecoder.cpp b/src/codec/SkScanlineDecoder.cpp
deleted file mode 100644
index e011507f80..0000000000
--- a/src/codec/SkScanlineDecoder.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkScanlineDecoder.h"
-#include "SkBmpCodec.h"
-#include "SkCodec_libgif.h"
-#include "SkCodec_libpng.h"
-#include "SkCodec_wbmp.h"
-#include "SkCodecPriv.h"
-#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
-#include "SkJpegCodec.h"
-#endif
-
-struct DecoderProc {
- bool (*IsFormat)(SkStream*);
- SkScanlineDecoder* (*NewFromStream)(SkStream*);
-};
-
-static const DecoderProc gDecoderProcs[] = {
- { SkPngCodec::IsPng, SkPngCodec::NewSDFromStream },
-#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
- { SkJpegCodec::IsJpeg, SkJpegCodec::NewSDFromStream },
-#endif
- { SkGifCodec::IsGif, SkGifCodec::NewSDFromStream },
- { SkBmpCodec::IsBmp, SkBmpCodec::NewSDFromStream },
- { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewSDFromStream },
-};
-
-SkScanlineDecoder* SkScanlineDecoder::NewFromStream(SkStream* stream) {
- if (!stream) {
- return nullptr;
- }
-
- SkAutoTDelete<SkStream> streamDeleter(stream);
-
- SkAutoTDelete<SkScanlineDecoder> codec(nullptr);
- for (uint32_t i = 0; i < SK_ARRAY_COUNT(gDecoderProcs); i++) {
- DecoderProc proc = gDecoderProcs[i];
- const bool correctFormat = proc.IsFormat(stream);
- if (!stream->rewind()) {
- return nullptr;
- }
- if (correctFormat) {
- codec.reset(proc.NewFromStream(streamDeleter.detach()));
- break;
- }
- }
-
- // Set the max size at 128 megapixels (512 MB for kN32).
- // This is about 4x smaller than a test image that takes a few minutes for
- // dm to decode and draw.
- const int32_t maxSize = 1 << 27;
- if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) {
- SkCodecPrintf("Error: Image size too large, cannot decode.\n");
- return nullptr;
- } else {
- return codec.detach();
- }
-}
-
-SkScanlineDecoder* SkScanlineDecoder::NewFromData(SkData* data) {
- if (!data) {
- return nullptr;
- }
- return NewFromStream(new SkMemoryStream(data));
-}
-
-
-SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo,
- const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
- // Ensure that valid color ptrs are passed in for kIndex8 color type
- if (kIndex_8_SkColorType == dstInfo.colorType()) {
- if (nullptr == ctable || nullptr == ctableCount) {
- return SkCodec::kInvalidParameters;
- }
- } else {
- if (ctableCount) {
- *ctableCount = 0;
- }
- ctableCount = nullptr;
- ctable = nullptr;
- }
-
- // Set options.
- SkCodec::Options optsStorage;
- if (nullptr == options) {
- options = &optsStorage;
- }
-
- const SkCodec::Result result = this->onStart(dstInfo, *options, ctable, ctableCount);
- if (result != SkCodec::kSuccess) {
- return result;
- }
-
- fCurrScanline = 0;
- fDstInfo = dstInfo;
- fOptions = *options;
- return SkCodec::kSuccess;
-}
-
-SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo) {
- return this->start(dstInfo, nullptr, nullptr, nullptr);
-}
-