aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/nanobench.cpp5
-rw-r--r--bench/subset/SubsetSingleBench.cpp12
-rw-r--r--bench/subset/SubsetTranslateBench.cpp12
-rw-r--r--bench/subset/SubsetZoomBench.cpp12
-rw-r--r--dm/DM.cpp6
-rw-r--r--dm/DMSrcSink.cpp66
-rw-r--r--gyp/codec.gyp1
-rw-r--r--include/codec/SkCodec.h205
-rw-r--r--include/codec/SkScaledCodec.h10
-rw-r--r--include/codec/SkScanlineDecoder.h288
-rw-r--r--resources/plane_interlaced.pngbin0 -> 6451 bytes
-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
-rw-r--r--tests/CodexTest.cpp137
-rw-r--r--tools/SkBitmapRegionCanvas.cpp5
-rw-r--r--tools/SkBitmapRegionCanvas.h8
-rw-r--r--tools/SkBitmapRegionDecoderInterface.cpp8
34 files changed, 1034 insertions, 1133 deletions
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index a1db0acd80..900ba4dfa2 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -38,7 +38,6 @@
#include "SkOSFile.h"
#include "SkPictureRecorder.h"
#include "SkPictureUtils.h"
-#include "SkScanlineDecoder.h"
#include "SkString.h"
#include "SkSurface.h"
#include "SkTaskGroup.h"
@@ -522,9 +521,7 @@ static bool valid_subset_bench(const SkString& path, SkColorType colorType, bool
int colorCount;
const SkImageInfo info = codec->getInfo().makeColorType(colorType);
SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromData(encoded));
- if (nullptr == scanlineDecoder || scanlineDecoder->start(info, nullptr,
- colors, &colorCount) != SkCodec::kSuccess)
+ if (codec->startScanlineDecode(info, nullptr, colors, &colorCount) != SkCodec::kSuccess)
{
SkDebugf("Could not create scanline decoder for %s with color type %s. "
"Skipping bench.\n", path.c_str(), color_type_to_str(colorType));
diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp
index 2e5703f508..e409cf7ed8 100644
--- a/bench/subset/SubsetSingleBench.cpp
+++ b/bench/subset/SubsetSingleBench.cpp
@@ -12,7 +12,6 @@
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
/*
@@ -65,20 +64,19 @@ void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) {
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
- SkScanlineDecoder::NewFromStream(fStream->duplicate()));
- const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType);
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+ const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
- scanlineDecoder->start(info, nullptr, colors, &colorCount);
+ codec->startScanlineDecode(info, nullptr, colors, &colorCount);
SkBitmap bitmap;
SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight);
alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
- scanlineDecoder->skipScanlines(fOffsetTop);
+ codec->skipScanlines(fOffsetTop);
uint32_t bpp = info.bytesPerPixel();
for (uint32_t y = 0; y < fSubsetHeight; y++) {
- scanlineDecoder->getScanlines(row.get(), 1, 0);
+ codec->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp,
fSubsetWidth * bpp);
}
diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp
index bed3580853..a5c4d46655 100644
--- a/bench/subset/SubsetTranslateBench.cpp
+++ b/bench/subset/SubsetTranslateBench.cpp
@@ -12,7 +12,6 @@
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
/*
@@ -61,11 +60,10 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) {
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
- SkScanlineDecoder::NewFromStream(fStream->duplicate()));
- const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType);
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+ const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
- scanlineDecoder->start(info, nullptr, colors, &colorCount);
+ codec->startScanlineDecode(info, nullptr, colors, &colorCount);
SkBitmap bitmap;
// Note that we use the same bitmap for all of the subsets.
@@ -75,7 +73,7 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) {
for (int x = 0; x < info.width(); x += fSubsetWidth) {
for (int y = 0; y < info.height(); y += fSubsetHeight) {
- scanlineDecoder->skipScanlines(y);
+ codec->skipScanlines(y);
const uint32_t currSubsetWidth =
x + (int) fSubsetWidth > info.width() ?
info.width() - x : fSubsetWidth;
@@ -84,7 +82,7 @@ void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) {
info.height() - y : fSubsetHeight;
const uint32_t bpp = info.bytesPerPixel();
for (uint32_t y = 0; y < currSubsetHeight; y++) {
- scanlineDecoder->getScanlines(row.get(), 1, 0);
+ codec->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + x * bpp,
currSubsetWidth * bpp);
}
diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp
index 655285fdf8..869e93043d 100644
--- a/bench/subset/SubsetZoomBench.cpp
+++ b/bench/subset/SubsetZoomBench.cpp
@@ -12,7 +12,6 @@
#include "SkCodec.h"
#include "SkImageDecoder.h"
#include "SkOSFile.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
/*
@@ -61,11 +60,10 @@ void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) {
SkPMColor colors[256];
if (fUseCodec) {
for (int count = 0; count < n; count++) {
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
- SkScanlineDecoder::NewFromStream(fStream->duplicate()));
- const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType);
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate()));
+ const SkImageInfo info = codec->getInfo().makeColorType(fColorType);
SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
- scanlineDecoder->start(info, nullptr, colors, &colorCount);
+ codec->startScanlineDecode(info, nullptr, colors, &colorCount);
const int centerX = info.width() / 2;
const int centerY = info.height() / 2;
@@ -83,9 +81,9 @@ void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) {
alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
uint32_t bpp = info.bytesPerPixel();
- scanlineDecoder->skipScanlines(subsetStartY);
+ codec->skipScanlines(subsetStartY);
for (int y = 0; y < subsetHeight; y++) {
- scanlineDecoder->getScanlines(row.get(), 1, 0);
+ codec->getScanlines(row.get(), 1, 0);
memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp,
subsetWidth * bpp);
}
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 11581fda81..0faca8f7c3 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -311,6 +311,12 @@ static void push_codec_srcs(Path path) {
}
}
+ if (path.endsWith(".ico") || path.endsWith(".ICO")) {
+ // FIXME: skbug.com/4404: ICO does not have the ability to decode scanlines, so we cannot
+ // use SkScaledCodec with it.
+ return;
+ }
+
// SkScaledCodec Scales
// The native scales are included to make sure that SkScaledCodec defaults to the native
// scaling strategy when possible.
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 96a0ab1a3e..2e22556335 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -25,7 +25,6 @@
#include "SkRecorder.h"
#include "SkSVGCanvas.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkStream.h"
#include "SkTLogic.h"
#include "SkXMLWriter.h"
@@ -238,18 +237,6 @@ bool CodecSrc::veto(SinkFlags flags) const {
|| flags.approach != SinkFlags::kDirect;
}
-SkScanlineDecoder* start_scanline_decoder(SkData* encoded, const SkImageInfo& info,
- SkPMColor* colorPtr, int* colorCountPtr) {
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromData(encoded));
- if (nullptr == scanlineDecoder) {
- return nullptr;
- }
- if (SkCodec::kSuccess != scanlineDecoder->start(info, NULL, colorPtr, colorCountPtr)) {
- return nullptr;
- }
- return scanlineDecoder.detach();
-}
-
Error CodecSrc::draw(SkCanvas* canvas) const {
SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
if (!encoded) {
@@ -348,25 +335,24 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
break;
}
case kScanline_Mode: {
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
- start_scanline_decoder(encoded.get(), decodeInfo, colorPtr, colorCountPtr));
- if (nullptr == scanlineDecoder) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ colorCountPtr)) {
return Error::Nonfatal("Could not start scanline decoder");
}
SkCodec::Result result = SkCodec::kUnimplemented;
- switch (scanlineDecoder->getScanlineOrder()) {
- case SkScanlineDecoder::kTopDown_SkScanlineOrder:
- case SkScanlineDecoder::kBottomUp_SkScanlineOrder:
- case SkScanlineDecoder::kNone_SkScanlineOrder:
- result = scanlineDecoder->getScanlines(bitmap.getAddr(0, 0),
+ switch (codec->getScanlineOrder()) {
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kBottomUp_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder:
+ result = codec->getScanlines(bitmap.getAddr(0, 0),
decodeInfo.height(), bitmap.rowBytes());
break;
- case SkScanlineDecoder::kOutOfOrder_SkScanlineOrder: {
+ case SkCodec::kOutOfOrder_SkScanlineOrder: {
for (int y = 0; y < decodeInfo.height(); y++) {
- int dstY = scanlineDecoder->getY();
+ int dstY = codec->nextScanline();
void* dstPtr = bitmap.getAddr(0, dstY);
- result = scanlineDecoder->getScanlines(dstPtr, 1, bitmap.rowBytes());
+ result = codec->getScanlines(dstPtr, 1, bitmap.rowBytes());
if (SkCodec::kSuccess != result && SkCodec::kIncompleteInput != result) {
return SkStringPrintf("%s failed with error message %d",
fPath.c_str(), (int) result);
@@ -432,11 +418,10 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
subsetHeight + extraY : subsetHeight;
const int y = row * subsetHeight;
//create scanline decoder for each subset
- SkAutoTDelete<SkScanlineDecoder> decoder(start_scanline_decoder(encoded.get(),
- decodeInfo, colorPtr, colorCountPtr));
- // TODO (msarett): Support this mode for all scanline orderings.
- if (nullptr == decoder || SkScanlineDecoder::kTopDown_SkScanlineOrder !=
- decoder->getScanlineOrder()) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ colorCountPtr)
+ // TODO (msarett): Support this mode for all scanline orderings.
+ || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
if (x == 0 && y == 0) {
//first try, image may not be compatible
return Error::Nonfatal("Could not start top-down scanline decoder");
@@ -445,7 +430,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
}
//skip to first line of subset
- const SkCodec::Result skipResult = decoder->skipScanlines(y);
+ const SkCodec::Result skipResult = codec->skipScanlines(y);
switch (skipResult) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
@@ -461,7 +446,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds));
SkAutoLockPixels autlockSubsetBm(subsetBm, true);
const SkCodec::Result subsetResult =
- decoder->getScanlines(buffer, currentSubsetHeight, rowBytes);
+ codec->getScanlines(buffer, currentSubsetHeight, rowBytes);
switch (subsetResult) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
@@ -501,10 +486,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
const int numStripes = (height + stripeHeight - 1) / stripeHeight;
// Decode odd stripes
- SkAutoTDelete<SkScanlineDecoder> decoder(
- start_scanline_decoder(encoded.get(), decodeInfo, colorPtr, colorCountPtr));
- if (nullptr == decoder ||
- SkScanlineDecoder::kTopDown_SkScanlineOrder != decoder->getScanlineOrder()) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ colorCountPtr)
+ || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
// This mode was designed to test the new skip scanlines API in libjpeg-turbo.
// Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
// to run this test for image types that do not have this scanline ordering.
@@ -513,7 +497,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
for (int i = 0; i < numStripes; i += 2) {
// Skip a stripe
const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
- SkCodec::Result result = decoder->skipScanlines(linesToSkip);
+ SkCodec::Result result = codec->skipScanlines(linesToSkip);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
@@ -526,7 +510,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
const int startY = (i + 1) * stripeHeight;
const int linesToRead = SkTMin(stripeHeight, height - startY);
if (linesToRead > 0) {
- result = decoder->getScanlines(bitmap.getAddr(0, startY),
+ result = codec->getScanlines(bitmap.getAddr(0, startY),
linesToRead, bitmap.rowBytes());
switch (result) {
case SkCodec::kSuccess:
@@ -539,8 +523,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
// Decode even stripes
- const SkCodec::Result startResult = decoder->start(decodeInfo, nullptr, colorPtr,
- colorCountPtr);
+ const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr,
+ colorPtr, colorCountPtr);
if (SkCodec::kSuccess != startResult) {
return "Failed to restart scanline decoder with same parameters.";
}
@@ -548,7 +532,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
// Read a stripe
const int startY = i * stripeHeight;
const int linesToRead = SkTMin(stripeHeight, height - startY);
- SkCodec::Result result = decoder->getScanlines(bitmap.getAddr(0, startY),
+ SkCodec::Result result = codec->getScanlines(bitmap.getAddr(0, startY),
linesToRead, bitmap.rowBytes());
switch (result) {
case SkCodec::kSuccess:
@@ -561,7 +545,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
// Skip a stripe
const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
if (linesToSkip > 0) {
- result = decoder->skipScanlines(linesToSkip);
+ result = codec->skipScanlines(linesToSkip);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
index a99707e4ea..205ff917cc 100644
--- a/gyp/codec.gyp
+++ b/gyp/codec.gyp
@@ -47,7 +47,6 @@
'../src/codec/SkMaskSwizzler.cpp',
'../src/codec/SkMasks.cpp',
'../src/codec/SkScaledCodec.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 bdfa2326fb..01fd4fa62a 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -45,7 +45,7 @@ public:
/**
* Return the ImageInfo associated with this codec.
*/
- const SkImageInfo& getInfo() const { return fInfo; }
+ const SkImageInfo& getInfo() const { return fSrcInfo; }
/**
* Return a size that approximately supports the desired scale factor.
@@ -131,7 +131,12 @@ public:
*/
kCouldNotRewind,
/**
- * This method is not implemented by this generator.
+ * startScanlineDecode() was not called before calling getScanlines.
+ */
+ kScanlineDecodingNotStarted,
+ /**
+ * This method is not implemented by this codec.
+ * FIXME: Perhaps this should be kUnsupported?
*/
kUnimplemented,
};
@@ -202,6 +207,9 @@ public:
* If info is not kIndex8_SkColorType, then the last two parameters may be NULL. If ctableCount
* is not null, it will be set to 0.
*
+ * If a scanline decode is in progress, scanline mode will end, requiring the client to call
+ * startScanlineDecode() in order to return to decoding scanlines.
+ *
* @return Result kSuccess, or another value explaining the type of failure.
*/
Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*,
@@ -225,6 +233,147 @@ public:
return this->onReallyHasAlpha();
}
+ /**
+ * The remaining functions revolve around decoding scanlines.
+ */
+
+ /**
+ * Prepare for a scanline decode with the specified options.
+ *
+ * After this call, this class will be ready to decode the first scanline.
+ *
+ * This must be called in order to call getScanlines or skipScanlines.
+ *
+ * This may require rewinding the stream.
+ *
+ * Not all SkCodecs support this.
+ *
+ * @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.
+ */
+ Result startScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options* options,
+ SkPMColor ctable[], int* ctableCount);
+
+ /**
+ * Simplified version of startScanlineDecode() that asserts that info is NOT
+ * kIndex8_SkColorType and uses the default Options.
+ */
+ Result startScanlineDecode(const SkImageInfo& dstInfo);
+
+ /**
+ * Write the next countLines scanlines into dst.
+ *
+ * Not valid to call before calling startScanlineDecode().
+ *
+ * @param dst Must be non-null, and large enough to hold countLines
+ * scanlines of size rowBytes.
+ * @param countLines Number of lines to write.
+ * @param rowBytes Number of bytes per row. Must be large enough to hold
+ * a scanline based on the SkImageInfo used to create this object.
+ */
+ Result getScanlines(void* dst, int countLines, size_t rowBytes);
+
+ /**
+ * Skip count scanlines.
+ *
+ * Not valid to call before calling startScanlineDecode().
+ *
+ * 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.
+ */
+ Result skipScanlines(int countLines);
+
+ /**
+ * The order in which rows are output from the scanline decoder is not the
+ * same for all variations of all image types. This explains the possible
+ * output row orderings.
+ */
+ enum SkScanlineOrder {
+ /*
+ * By far the most common, this indicates that the image can be decoded
+ * reliably using the scanline decoder, and that rows will be output in
+ * the logical order.
+ */
+ kTopDown_SkScanlineOrder,
+
+ /*
+ * This indicates that the scanline decoder reliably outputs rows, but
+ * they will be returned in reverse order. If the scanline format is
+ * kBottomUp, the nextScanline() API can be used to determine the actual
+ * y-coordinate of the next output row, but the client is not forced
+ * to take advantage of this, given that it's not too tough to keep
+ * track independently.
+ *
+ * For full image decodes, it is safe to get all of the scanlines at
+ * once, since the decoder will handle inverting the rows as it
+ * decodes.
+ *
+ * For subset decodes and sampling, it is simplest to get and skip
+ * scanlines one at a time, using the nextScanline() API. It is
+ * possible to ask for larger chunks at a time, but this should be used
+ * with caution. As with full image decodes, the decoder will handle
+ * inverting the requested rows, but rows will still be delivered
+ * starting from the bottom of the image.
+ *
+ * Upside down bmps are an example.
+ */
+ kBottomUp_SkScanlineOrder,
+
+ /*
+ * This indicates that the scanline decoder reliably outputs rows, but
+ * they will not be in logical order. If the scanline format is
+ * kOutOfOrder, the nextScanline() API should be used to determine the
+ * actual y-coordinate of the next output row.
+ *
+ * For this scanline ordering, it is advisable to get and skip
+ * scanlines one at a time.
+ *
+ * Interlaced gifs are an example.
+ */
+ kOutOfOrder_SkScanlineOrder,
+
+ /*
+ * Indicates that the entire image must be decoded in order to output
+ * any amount of scanlines. In this case, it is a REALLY BAD IDEA to
+ * request scanlines 1-by-1 or in small chunks. The client should
+ * determine which scanlines are needed and ask for all of them in
+ * a single call to getScanlines().
+ *
+ * Interlaced pngs are an example.
+ */
+ kNone_SkScanlineOrder,
+ };
+
+ /**
+ * An enum representing the order in which scanlines will be returned by
+ * the scanline decoder.
+ */
+ SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); }
+
+ /**
+ * Returns the y-coordinate of the next row to be returned by the scanline
+ * decoder. This will be overridden in the case of
+ * kOutOfOrder_SkScanlineOrder and should be unnecessary in the case of
+ * kNone_SkScanlineOrder.
+ *
+ * Results are undefined when not in scanline decoding mode.
+ */
+ int nextScanline() const {
+ return this->onNextScanline();
+ }
+
protected:
SkCodec(const SkImageInfo&, SkStream*);
@@ -275,9 +424,59 @@ protected:
return fStream.get();
}
+ /**
+ * The remaining functions revolve around decoding scanlines.
+ */
+
+ /**
+ * Most images types will be kTopDown and will not need to override this function.
+ */
+ virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; }
+
+ /**
+ * Most images will be kTopDown and will not need to override this function.
+ */
+ virtual int onNextScanline() const { return fCurrScanline; }
+
+ /**
+ * Update the next scanline. Used by interlaced png.
+ */
+ void updateNextScanline(int newY) { fCurrScanline = newY; }
+
+ const SkImageInfo& dstInfo() const { return fDstInfo; }
+
+ const SkCodec::Options& options() const { return fOptions; }
+
private:
- const SkImageInfo fInfo;
+ const SkImageInfo fSrcInfo;
SkAutoTDelete<SkStream> fStream;
bool fNeedsRewind;
+ // These fields are only meaningful during scanline decodes.
+ SkImageInfo fDstInfo;
+ SkCodec::Options fOptions;
+ int fCurrScanline;
+
+ // Methods for scanline decoding.
+ virtual SkCodec::Result onStartScanlineDecode(const SkImageInfo& dstInfo,
+ const SkCodec::Options& options, SkPMColor ctable[], int* ctableCount) {
+ return kUnimplemented;
+ }
+
+ // Naive default version just calls onGetScanlines on temp memory.
+ virtual SkCodec::Result onSkipScanlines(int countLines) {
+ SkAutoMalloc storage(fDstInfo.minRowBytes());
+ // Note that we pass 0 to rowBytes so we continue to use the same memory.
+ // Also note that while getScanlines checks that rowBytes is big enough,
+ // onGetScanlines bypasses that check.
+ // Calling the virtual method also means we do not double count
+ // countLines.
+ return this->onGetScanlines(storage.get(), countLines, 0);
+ }
+
+ virtual SkCodec::Result onGetScanlines(void* dst, int countLines,
+ size_t rowBytes) {
+ return kUnimplemented;
+ }
+
};
#endif // SkCodec_DEFINED
diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h
index 1bcdf085b2..20428d8d73 100644
--- a/include/codec/SkScaledCodec.h
+++ b/include/codec/SkScaledCodec.h
@@ -8,9 +8,7 @@
#define SkScaledCodec_DEFINED
#include "SkCodec.h"
-#include "SkScanlineDecoder.h"
-class SkScanlineDecoder;
class SkStream;
/**
@@ -52,18 +50,18 @@ protected:
Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
override;
SkEncodedFormat onGetEncodedFormat() const override {
- return fScanlineDecoder->getEncodedFormat();
+ return fCodec->getEncodedFormat();
}
bool onReallyHasAlpha() const override {
- return fScanlineDecoder->reallyHasAlpha();
+ return fCodec->reallyHasAlpha();
}
private:
- SkAutoTDelete<SkScanlineDecoder> fScanlineDecoder;
+ SkAutoTDelete<SkCodec> fCodec;
- explicit SkScaledCodec(SkScanlineDecoder*);
+ explicit SkScaledCodec(SkCodec*);
typedef SkCodec INHERITED;
};
diff --git a/include/codec/SkScanlineDecoder.h b/include/codec/SkScanlineDecoder.h
deleted file mode 100644
index 61184d644a..0000000000
--- a/include/codec/SkScanlineDecoder.h
+++ /dev/null
@@ -1,288 +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.
- */
-
-#ifndef SkScanlineDecoder_DEFINED
-#define SkScanlineDecoder_DEFINED
-
-#include "../private/SkTemplates.h"
-#include "SkCodec.h"
-#include "SkImageInfo.h"
-#include "SkTypes.h"
-
-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
- * fact, in the case of subset decodes, it is likely that there will be
- * scanlines at the bottom of the image that have been ignored.
- */
- virtual ~SkScanlineDecoder() {}
-
- /**
- * Return a size that approximately supports the desired scale factor.
- * The codec may not be able to scale efficiently to the exact scale
- * factor requested, so return a size that approximates that scale.
- * The returned value is the codec's suggestion for the closest valid
- * scale that it can natively support
- * FIXME: share this with SkCodec
- */
- SkISize getScaledDimensions(float desiredScale) {
- return this->onGetScaledDimensions(desiredScale);
- }
-
- /**
- * 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.
- * @param rowBytes Number of bytes per row. Must be large enough to hold
- * 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;
- }
- const SkCodec::Result result = this->onGetScanlines(dst, countLines, rowBytes);
- fCurrScanline += countLines;
- return result;
- }
-
- /**
- * 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
- // can catch their bug.
- return SkCodec::kInvalidParameters;
- }
- const SkCodec::Result result = this->onSkipScanlines(countLines);
- fCurrScanline += countLines;
- return result;
- }
-
- /**
- * 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
- * determine if such an image truly had alpha. Calling it before decoding
- * is undefined.
- * FIXME: see skbug.com/3582.
- */
- bool reallyHasAlpha() const {
- return this->onReallyHasAlpha();
- }
-
- /**
- * Format of the encoded data.
- */
- SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }
-
- /**
- * The order in which rows are output from the scanline decoder is not the
- * same for all variations of all image types. This explains the possible
- * output row orderings.
- */
- enum SkScanlineOrder {
- /*
- * By far the most common, this indicates that the image can be decoded
- * reliably using the scanline decoder, and that rows will be output in
- * the logical order.
- */
- kTopDown_SkScanlineOrder,
-
- /*
- * This indicates that the scanline decoder reliably outputs rows, but
- * they will be returned in reverse order. If the scanline format is
- * kBottomUp, the getY() API can be used to determine the actual
- * y-coordinate of the next output row, but the client is not forced
- * to take advantage of this, given that it's not too tough to keep
- * track independently.
- *
- * For full image decodes, it is safe to get all of the scanlines at
- * once, since the decoder will handle inverting the rows as it
- * decodes.
- *
- * For subset decodes and sampling, it is simplest to get and skip
- * scanlines one at a time, using the getY() API. It is possible to
- * ask for larger chunks at a time, but this should be used with
- * caution. As with full image decodes, the decoder will handle
- * inverting the requested rows, but rows will still be delivered
- * starting from the bottom of the image.
- *
- * Upside down bmps are an example.
- */
- kBottomUp_SkScanlineOrder,
-
- /*
- * This indicates that the scanline decoder reliably outputs rows, but
- * they will not be in logical order. If the scanline format is
- * kOutOfOrder, the getY() API should be used to determine the actual
- * y-coordinate of the next output row.
- *
- * For this scanline ordering, it is advisable to get and skip
- * scanlines one at a time.
- *
- * Interlaced gifs are an example.
- */
- kOutOfOrder_SkScanlineOrder,
-
- /*
- * Indicates that the entire image must be decoded in order to output
- * any amount of scanlines. In this case, it is a REALLY BAD IDEA to
- * request scanlines 1-by-1 or in small chunks. The client should
- * determine which scanlines are needed and ask for all of them in
- * a single call to getScanlines().
- *
- * Interlaced pngs are an example.
- */
- kNone_SkScanlineOrder,
- };
-
- /**
- * An enum representing the order in which scanlines will be returned by
- * the scanline decoder.
- */
- SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); }
-
- /**
- * Returns the y-coordinate of the next row to be returned by the scanline
- * decoder. This will be overridden in the case of
- * kOutOfOrder_SkScanlineOrder and should be unnecessary in the case of
- * kNone_SkScanlineOrder.
- */
- int getY() const {
- SkASSERT(kNone_SkScanlineOrder != this->getScanlineOrder());
- return this->onGetY();
- }
-
-protected:
- SkScanlineDecoder(const SkImageInfo& srcInfo)
- : fSrcInfo(srcInfo)
- , fDstInfo()
- , fOptions()
- , fCurrScanline(0) {}
-
- virtual SkISize onGetScaledDimensions(float /* desiredScale */) {
- // By default, scaling is not supported.
- return this->getInfo().dimensions();
- }
-
- virtual SkEncodedFormat onGetEncodedFormat() const = 0;
-
- virtual bool onReallyHasAlpha() const { return false; }
-
- /**
- * Most images types will be kTopDown and will not need to override this function.
- */
- virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; }
-
- /**
- * Most images will be kTopDown and will not need to override this function.
- */
- virtual int onGetY() const { return fCurrScanline; }
-
- const SkImageInfo& dstInfo() const { return fDstInfo; }
-
- const SkCodec::Options& options() const { return fOptions; }
-
-private:
- const SkImageInfo fSrcInfo;
- SkImageInfo fDstInfo;
- SkCodec::Options fOptions;
- 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());
- // Note that we pass 0 to rowBytes so we continue to use the same memory.
- // Also note that while getScanlines checks that rowBytes is big enough,
- // onGetScanlines bypasses that check.
- // Calling the virtual method also means we do not double count
- // countLines.
- return this->onGetScanlines(storage.get(), countLines, 0);
- }
-
- virtual SkCodec::Result onGetScanlines(void* dst, int countLines,
- size_t rowBytes) = 0;
-
-};
-#endif // SkScanlineDecoder_DEFINED
diff --git a/resources/plane_interlaced.png b/resources/plane_interlaced.png
new file mode 100644
index 0000000000..3b217f205d
--- /dev/null
+++ b/resources/plane_interlaced.png
Binary files differ
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);
-}
-
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index 2ea6eda57e..f1c5bbaa12 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -11,7 +11,6 @@
#include "SkMD5.h"
#include "SkRandom.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "Test.h"
static SkStreamAsset* resource(const char path[]) {
@@ -147,25 +146,44 @@ static void check(skiatest::Reporter* r,
// Scanline decoding follows.
- stream.reset(resource(path));
- SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
- SkScanlineDecoder::NewFromStream(stream.detach()));
+ // Need to call start() first.
+ REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+ == SkCodec::kScanlineDecodingNotStarted);
+ REPORTER_ASSERT(r, codec->skipScanlines(1)
+ == SkCodec::kScanlineDecodingNotStarted);
+
+ const SkCodec::Result startResult = codec->startScanlineDecode(info);
if (supportsScanlineDecoding) {
bm.eraseColor(SK_ColorYELLOW);
- REPORTER_ASSERT(r, scanlineDecoder);
- REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
for (int y = 0; y < info.height(); y++) {
- result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0);
+ result = codec->getScanlines(bm.getAddr(0, y), 1, 0);
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
}
// verify that scanline decoding gives the same result.
- if (SkScanlineDecoder::kTopDown_SkScanlineOrder == scanlineDecoder->getScanlineOrder()) {
+ if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
compare_to_good_digest(r, digest, bm);
}
+
+ // Cannot continue to decode scanlines beyond the end
+ REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+ == SkCodec::kInvalidParameters);
+
+ // Interrupting a scanline decode with a full decode starts from
+ // scratch
+ REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+ == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
+ == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
+ == SkCodec::kScanlineDecodingNotStarted);
+ REPORTER_ASSERT(r, codec->skipScanlines(1)
+ == SkCodec::kScanlineDecodingNotStarted);
} else {
- REPORTER_ASSERT(r, !scanlineDecoder);
+ REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
}
// The rest of this function tests decoding subsets, and will decode an arbitrary number of
@@ -247,10 +265,102 @@ DEF_TEST(Codec, r) {
check(r, "mandrill_512.png", SkISize::Make(512, 512), true, false);
check(r, "mandrill_64.png", SkISize::Make(64, 64), true, false);
check(r, "plane.png", SkISize::Make(250, 126), true, false);
+ check(r, "plane_interlaced.png", SkISize::Make(250, 126), true, false);
check(r, "randPixels.png", SkISize::Make(8, 8), true, false);
check(r, "yellow_rose.png", SkISize::Make(400, 301), true, false);
}
+// Test interlaced PNG in stripes, similar to DM's kStripe_Mode
+DEF_TEST(Codec_stripes, r) {
+ const char * path = "plane_interlaced.png";
+ SkAutoTDelete<SkStream> stream(resource(path));
+ if (!stream) {
+ SkDebugf("Missing resource '%s'\n", path);
+ }
+
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
+ REPORTER_ASSERT(r, codec);
+
+ if (!codec) {
+ return;
+ }
+
+ switch (codec->getScanlineOrder()) {
+ case SkCodec::kBottomUp_SkScanlineOrder:
+ case SkCodec::kOutOfOrder_SkScanlineOrder:
+ ERRORF(r, "This scanline order will not match the original.");
+ return;
+ default:
+ break;
+ }
+
+ // Baseline for what the image should look like, using N32.
+ const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
+
+ SkBitmap bm;
+ bm.allocPixels(info);
+ SkAutoLockPixels autoLockPixels(bm);
+ SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ SkMD5::Digest digest;
+ md5(bm, &digest);
+
+ // Now decode in stripes
+ const int height = info.height();
+ const int numStripes = 4;
+ int stripeHeight;
+ int remainingLines;
+ SkTDivMod(height, numStripes, &stripeHeight, &remainingLines);
+
+ bm.eraseColor(SK_ColorYELLOW);
+
+ result = codec->startScanlineDecode(info);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ // Odd stripes
+ for (int i = 1; i < numStripes; i += 2) {
+ // Skip the even stripes
+ result = codec->skipScanlines(stripeHeight);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
+ bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ }
+
+ // Even stripes
+ result = codec->startScanlineDecode(info);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ for (int i = 0; i < numStripes; i += 2) {
+ result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
+ bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ // Skip the odd stripes
+ if (i + 1 < numStripes) {
+ result = codec->skipScanlines(stripeHeight);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ }
+ }
+
+ // Remainder at the end
+ if (remainingLines > 0) {
+ result = codec->startScanlineDecode(info);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ result = codec->skipScanlines(height - remainingLines);
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+
+ result = codec->getScanlines(bm.getAddr(0, height - remainingLines),
+ remainingLines, bm.rowBytes());
+ REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ }
+
+ compare_to_good_digest(r, digest, bm);
+}
+
static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false));
// We should not have gotten a codec. Bots should catch us if we leaked anything.
@@ -369,13 +479,12 @@ static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) {
SkDebugf("Missing resource '%s'\n", path);
return;
}
- SkAutoTDelete<SkScanlineDecoder> decoder(SkScanlineDecoder::NewFromStream(
- stream.detach()));
+ SkAutoTDelete<SkCodec> decoder(SkCodec::NewFromStream(stream.detach()));
// This should return kSuccess because kIndex8 is supported.
SkPMColor colorStorage[256];
int colorCount;
- SkCodec::Result result = decoder->start(
+ SkCodec::Result result = decoder->startScanlineDecode(
decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, colorStorage, &colorCount);
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
// The rest of the test is uninteresting if kIndex8 is not supported
@@ -385,10 +494,10 @@ static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) {
// This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid
// colorPtr and a valid colorCountPtr.
- result = decoder->start(
+ result = decoder->startScanlineDecode(
decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, nullptr, nullptr);
REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
- result = decoder->start(
+ result = decoder->startScanlineDecode(
decoder->getInfo().makeColorType(kIndex_8_SkColorType));
REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
}
diff --git a/tools/SkBitmapRegionCanvas.cpp b/tools/SkBitmapRegionCanvas.cpp
index 2344d9020f..c54d936edc 100644
--- a/tools/SkBitmapRegionCanvas.cpp
+++ b/tools/SkBitmapRegionCanvas.cpp
@@ -7,9 +7,8 @@
#include "SkBitmapRegionCanvas.h"
#include "SkCanvas.h"
-#include "SkScanlineDecoder.h"
-SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkScanlineDecoder* decoder)
+SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
: INHERITED(decoder->getInfo().width(), decoder->getInfo().height())
, fDecoder(decoder)
{}
@@ -114,7 +113,7 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
dstColorType, dstAlphaType);
// Start the scanline decoder
- SkCodec::Result r = fDecoder->start(decodeInfo);
+ SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
if (SkCodec::kSuccess != r) {
SkDebugf("Error: Could not start scanline decoder.\n");
return nullptr;
diff --git a/tools/SkBitmapRegionCanvas.h b/tools/SkBitmapRegionCanvas.h
index 96631d70d3..05eca8b3fc 100644
--- a/tools/SkBitmapRegionCanvas.h
+++ b/tools/SkBitmapRegionCanvas.h
@@ -7,10 +7,10 @@
#include "SkBitmap.h"
#include "SkBitmapRegionDecoderInterface.h"
-#include "SkScanlineDecoder.h"
+#include "SkCodec.h"
/*
- * This class implements SkBitmapRegionDecoder using an SkScanlineDecoder and
+ * This class implements SkBitmapRegionDecoder using an SkCodec and
* an SkCanvas. It uses the scanline decoder to subset the height. It then
* will subset the width and scale by drawing to an SkCanvas.
*/
@@ -22,7 +22,7 @@ public:
/*
* Takes ownership of pointer to decoder
*/
- SkBitmapRegionCanvas(SkScanlineDecoder* decoder);
+ SkBitmapRegionCanvas(SkCodec* decoder);
/*
* Three differences from the Android version:
@@ -36,7 +36,7 @@ public:
private:
- SkAutoTDelete<SkScanlineDecoder> fDecoder;
+ SkAutoTDelete<SkCodec> fDecoder;
typedef SkBitmapRegionDecoderInterface INHERITED;
diff --git a/tools/SkBitmapRegionDecoderInterface.cpp b/tools/SkBitmapRegionDecoderInterface.cpp
index 090f042ce3..5c769d676e 100644
--- a/tools/SkBitmapRegionDecoderInterface.cpp
+++ b/tools/SkBitmapRegionDecoderInterface.cpp
@@ -8,7 +8,7 @@
#include "SkBitmapRegionCanvas.h"
#include "SkBitmapRegionDecoderInterface.h"
#include "SkBitmapRegionSampler.h"
-#include "SkScanlineDecoder.h"
+#include "SkCodec.h"
#include "SkImageDecoder.h"
SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(
@@ -29,14 +29,14 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
return new SkBitmapRegionSampler(decoder, width, height);
}
case kCanvas_Strategy: {
- SkScanlineDecoder* decoder = SkScanlineDecoder::NewFromStream(stream);
+ SkCodec* decoder = SkCodec::NewFromStream(stream);
if (nullptr == decoder) {
SkDebugf("Error: Failed to create decoder.\n");
return nullptr;
}
switch (decoder->getScanlineOrder()) {
- case SkScanlineDecoder::kTopDown_SkScanlineOrder:
- case SkScanlineDecoder::kNone_SkScanlineOrder:
+ case SkCodec::kTopDown_SkScanlineOrder:
+ case SkCodec::kNone_SkScanlineOrder:
break;
default:
SkDebugf("Error: Scanline ordering not supported.\n");