aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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/DMSrcSink.cpp112
-rw-r--r--gyp/codec.gyp1
-rw-r--r--gyp/tools.gyp3
-rw-r--r--include/codec/SkCodec.h116
-rw-r--r--include/codec/SkScaledCodec.h6
-rw-r--r--src/codec/SkBmpCodec.cpp18
-rw-r--r--src/codec/SkBmpCodec.h16
-rw-r--r--src/codec/SkBmpMaskCodec.cpp21
-rw-r--r--src/codec/SkBmpMaskCodec.h11
-rw-r--r--src/codec/SkBmpRLECodec.cpp53
-rw-r--r--src/codec/SkBmpRLECodec.h8
-rw-r--r--src/codec/SkBmpStandardCodec.cpp30
-rw-r--r--src/codec/SkBmpStandardCodec.h14
-rw-r--r--src/codec/SkCodec.cpp106
-rw-r--r--src/codec/SkCodecPriv.h65
-rw-r--r--src/codec/SkCodec_libgif.cpp121
-rw-r--r--src/codec/SkCodec_libgif.h20
-rw-r--r--src/codec/SkCodec_libico.cpp12
-rw-r--r--src/codec/SkCodec_libico.h5
-rw-r--r--src/codec/SkCodec_libpng.cpp75
-rw-r--r--src/codec/SkCodec_libpng.h8
-rw-r--r--src/codec/SkCodec_wbmp.cpp26
-rw-r--r--src/codec/SkCodec_wbmp.h15
-rw-r--r--src/codec/SkJpegCodec.cpp41
-rw-r--r--src/codec/SkJpegCodec.h8
-rw-r--r--src/codec/SkMaskSwizzler.h9
-rw-r--r--src/codec/SkSampler.cpp102
-rw-r--r--src/codec/SkSampler.h35
-rw-r--r--src/codec/SkScaledCodec.cpp90
-rw-r--r--src/codec/SkSwizzler.cpp79
-rw-r--r--src/codec/SkSwizzler.h48
-rw-r--r--src/codec/SkWebpCodec.cpp9
-rw-r--r--src/codec/SkWebpCodec.h2
-rw-r--r--tests/CodexTest.cpp151
-rw-r--r--tests/SwizzlerTest.cpp66
-rw-r--r--tools/SkBitmapRegionCanvas.cpp13
-rw-r--r--tools/SkBitmapRegionDecoderInterface.cpp8
-rw-r--r--tools/dm_flags.json760
-rwxr-xr-xtools/dm_flags.py21
42 files changed, 1662 insertions, 678 deletions
diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp
index 735effa100..b4e92e4e22 100644
--- a/bench/subset/SubsetSingleBench.cpp
+++ b/bench/subset/SubsetSingleBench.cpp
@@ -75,8 +75,8 @@ void SubsetSingleBench::onDraw(int n, SkCanvas* canvas) {
SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight);
alloc_pixels(&bitmap, subsetInfo, colors, colorCount);
- SkDEBUGCODE(result = ) codec->skipScanlines(fOffsetTop);
- SkASSERT(result == SkCodec::kSuccess);
+ SkDEBUGCODE(int lines = ) codec->skipScanlines(fOffsetTop);
+ SkASSERT((uint32_t) lines == fOffsetTop);
const uint32_t bpp = info.bytesPerPixel();
@@ -84,8 +84,8 @@ void SubsetSingleBench::onDraw(int n, SkCanvas* canvas) {
case SkCodec::kTopDown_SkScanlineOrder: {
SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]);
for (uint32_t y = 0; y < fSubsetHeight; y++) {
- SkDEBUGCODE(result = ) codec->getScanlines(row.get(), 1, 0);
- SkASSERT(result == SkCodec::kSuccess);
+ SkDEBUGCODE(lines = ) codec->getScanlines(row.get(), 1, 0);
+ SkASSERT(lines == 1);
memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp,
fSubsetWidth * bpp);
@@ -99,9 +99,9 @@ void SubsetSingleBench::onDraw(int n, SkCanvas* canvas) {
SkBitmap stripeBm;
alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
- SkDEBUGCODE(result = ) codec->getScanlines(stripeBm.getPixels(), fSubsetHeight,
+ SkDEBUGCODE(lines = ) codec->getScanlines(stripeBm.getPixels(), fSubsetHeight,
stripeBm.rowBytes());
- SkASSERT(result == SkCodec::kSuccess);
+ SkASSERT((uint32_t) lines == fSubsetHeight);
for (uint32_t y = 0; y < fSubsetHeight; y++) {
memcpy(bitmap.getAddr(0, y), stripeBm.getAddr(fOffsetLeft, y),
diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp
index d13f31a86d..f6f3d4b222 100644
--- a/bench/subset/SubsetTranslateBench.cpp
+++ b/bench/subset/SubsetTranslateBench.cpp
@@ -91,8 +91,8 @@ void SubsetTranslateBench::onDraw(int n, SkCanvas* canvas) {
codec->startScanlineDecode(info, nullptr, get_colors(&bitmap), &colorCount);
SkASSERT(SkCodec::kSuccess == result);
- SkDEBUGCODE(result =) codec->skipScanlines(y);
- SkASSERT(SkCodec::kSuccess == result);
+ SkDEBUGCODE(int lines =) codec->skipScanlines(y);
+ SkASSERT(y == lines);
const uint32_t currSubsetWidth =
x + (int) fSubsetWidth > info.width() ?
@@ -104,8 +104,8 @@ void SubsetTranslateBench::onDraw(int n, SkCanvas* canvas) {
switch (codec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder:
for (uint32_t y = 0; y < currSubsetHeight; y++) {
- SkDEBUGCODE(result =) codec->getScanlines(row.get(), 1, 0);
- SkASSERT(SkCodec::kSuccess == result);
+ SkDEBUGCODE(lines =) codec->getScanlines(row.get(), 1, 0);
+ SkASSERT(1 == lines);
memcpy(bitmap.getAddr(0, y), row.get() + x * bpp,
currSubsetWidth * bpp);
@@ -118,9 +118,9 @@ void SubsetTranslateBench::onDraw(int n, SkCanvas* canvas) {
SkBitmap stripeBm;
alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
- SkDEBUGCODE(result =) codec->getScanlines(stripeBm.getPixels(),
+ SkDEBUGCODE(lines =) codec->getScanlines(stripeBm.getPixels(),
currSubsetHeight, stripeBm.rowBytes());
- SkASSERT(SkCodec::kSuccess == result);
+ SkASSERT(currSubsetHeight == (uint32_t) lines);
for (uint32_t subsetY = 0; subsetY < currSubsetHeight; subsetY++) {
memcpy(bitmap.getAddr(0, subsetY), stripeBm.getAddr(x, subsetY),
diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp
index 901fbb6082..3edc17f1cb 100644
--- a/bench/subset/SubsetZoomBench.cpp
+++ b/bench/subset/SubsetZoomBench.cpp
@@ -88,14 +88,14 @@ void SubsetZoomBench::onDraw(int n, SkCanvas* canvas) {
uint32_t bpp = info.bytesPerPixel();
- SkDEBUGCODE(result = ) codec->skipScanlines(subsetStartY);
- SkASSERT(SkCodec::kSuccess == result);
+ SkDEBUGCODE(int lines = ) codec->skipScanlines(subsetStartY);
+ SkASSERT(subsetStartY == lines);
switch (codec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder:
for (int y = 0; y < subsetHeight; y++) {
- SkDEBUGCODE(result = ) codec->getScanlines(row.get(), 1, 0);
- SkASSERT(SkCodec::kSuccess == result);
+ SkDEBUGCODE(lines = ) codec->getScanlines(row.get(), 1, 0);
+ SkASSERT(1 == lines);
memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp,
subsetWidth * bpp);
@@ -108,9 +108,9 @@ void SubsetZoomBench::onDraw(int n, SkCanvas* canvas) {
SkBitmap stripeBm;
alloc_pixels(&stripeBm, stripeInfo, colors, colorCount);
- SkDEBUGCODE(result = ) codec->getScanlines(stripeBm.getPixels(),
+ SkDEBUGCODE(lines = ) codec->getScanlines(stripeBm.getPixels(),
subsetHeight, stripeBm.rowBytes());
- SkASSERT(SkCodec::kSuccess == result);
+ SkASSERT(subsetHeight == lines);
for (int y = 0; y < subsetHeight; y++) {
memcpy(bitmap.getAddr(0, y),
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 2ea115bc9a..88371a8e28 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -28,6 +28,8 @@
#include "SkStream.h"
#include "SkTLogic.h"
#include "SkXMLWriter.h"
+#include "SkScaledCodec.h"
+#include "SkSwizzler.h"
DEFINE_bool(multiPage, false, "For document-type backends, render the source"
" into multiple pages");
@@ -340,36 +342,30 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
return Error::Nonfatal("Could not start scanline decoder");
}
- SkCodec::Result result = SkCodec::kUnimplemented;
+ void* dst = bitmap.getAddr(0, 0);
+ size_t rowBytes = bitmap.rowBytes();
+ uint32_t height = decodeInfo.height();
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());
+ // We do not need to check the return value. On an incomplete
+ // image, memory will be filled with a default value.
+ codec->getScanlines(dst, height, rowBytes);
break;
case SkCodec::kOutOfOrder_SkScanlineOrder: {
for (int y = 0; y < decodeInfo.height(); y++) {
- int dstY = codec->nextScanline();
+ int dstY = codec->outputScanline(y);
void* dstPtr = bitmap.getAddr(0, dstY);
- 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);
- }
+ // We complete the loop, even if this call begins to fail
+ // due to an incomplete image. This ensures any uninitialized
+ // memory will be filled with the proper value.
+ codec->getScanlines(dstPtr, 1, bitmap.rowBytes());
}
break;
}
}
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("%s failed with error message %d",
- fPath.c_str(), (int) result);
- }
canvas->drawBitmap(bitmap, 0, 0);
break;
}
@@ -429,41 +425,21 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
return "Error scanline decoder is nullptr";
}
}
- //skip to first line of subset
- const SkCodec::Result skipResult = codec->skipScanlines(y);
- switch (skipResult) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("%s failed after attempting to skip %d scanlines"
- "with error message %d", fPath.c_str(), y, (int) skipResult);
- }
+ // Skip to the first line of subset. We ignore the result value here.
+ // If the skip value fails, this will indicate an incomplete image.
+ // This means that the call to getScanlines() will also fail, but it
+ // will fill the buffer with a default value, so we can still draw the
+ // image.
+ codec->skipScanlines(y);
+
//create and set size of subsetBm
SkBitmap subsetBm;
SkIRect bounds = SkIRect::MakeWH(currentSubsetWidth, currentSubsetHeight);
SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds));
SkAutoLockPixels autlockSubsetBm(subsetBm, true);
- const SkCodec::Result subsetResult =
- codec->getScanlines(buffer, currentSubsetHeight, rowBytes);
- switch (subsetResult) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("%s failed with error message %d",
- fPath.c_str(), (int) subsetResult);
- }
+ codec->getScanlines(buffer, currentSubsetHeight, rowBytes);
+
const size_t bpp = decodeInfo.bytesPerPixel();
- /*
- * we copy all the lines at once becuase when calling getScanlines for
- * interlaced pngs the entire image must be read regardless of the number
- * of lines requested. Reading an interlaced png in a loop, line-by-line, would
- * decode the entire image height times, which is very slow
- * it is aknowledged that copying each line as you read it in a loop
- * may be faster for other types of images. Since this is a correctness test
- * that's okay.
- */
char* bufferRow = buffer;
for (int subsetY = 0; subsetY < currentSubsetHeight; ++subsetY) {
memcpy(subsetBm.getAddr(0, subsetY), bufferRow + x*bpp,
@@ -493,31 +469,17 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
// to run this test for image types that do not have this scanline ordering.
return Error::Nonfatal("Could not start top-down scanline decoder");
}
+
for (int i = 0; i < numStripes; i += 2) {
// Skip a stripe
const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
- SkCodec::Result result = codec->skipScanlines(linesToSkip);
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("Cannot skip scanlines for %s.", fPath.c_str());
- }
+ codec->skipScanlines(linesToSkip);
// Read a stripe
const int startY = (i + 1) * stripeHeight;
const int linesToRead = SkTMin(stripeHeight, height - startY);
if (linesToRead > 0) {
- result = codec->getScanlines(bitmap.getAddr(0, startY),
- linesToRead, bitmap.rowBytes());
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("Cannot get scanlines for %s.", fPath.c_str());
- }
+ codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
}
}
@@ -531,27 +493,12 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
// Read a stripe
const int startY = i * stripeHeight;
const int linesToRead = SkTMin(stripeHeight, height - startY);
- SkCodec::Result result = codec->getScanlines(bitmap.getAddr(0, startY),
- linesToRead, bitmap.rowBytes());
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("Cannot get scanlines for %s.", fPath.c_str());
- }
+ codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
// Skip a stripe
const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
if (linesToSkip > 0) {
- result = codec->skipScanlines(linesToSkip);
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- return SkStringPrintf("Cannot skip scanlines for %s.", fPath.c_str());
- }
+ codec->skipScanlines(linesToSkip);
}
}
canvas->drawBitmap(bitmap, 0, 0);
@@ -592,8 +539,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
// And scale
// FIXME: Should we have a version of getScaledDimensions that takes a subset
// into account?
- decodeInfo = decodeInfo.makeWH(SkScalarRoundToInt(preScaleW * fScale),
- SkScalarRoundToInt(preScaleH * fScale));
+ decodeInfo = decodeInfo.makeWH(
+ SkTMax(1, SkScalarRoundToInt(preScaleW * fScale)),
+ SkTMax(1, SkScalarRoundToInt(preScaleH * fScale)));
size_t rowBytes = decodeInfo.minRowBytes();
if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes, colorTable.get(),
nullptr, nullptr)) {
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
index 205ff917cc..1f3287ab52 100644
--- a/gyp/codec.gyp
+++ b/gyp/codec.gyp
@@ -46,6 +46,7 @@
'../src/codec/SkJpegUtility_codec.cpp',
'../src/codec/SkMaskSwizzler.cpp',
'../src/codec/SkMasks.cpp',
+ '../src/codec/SkSampler.cpp',
'../src/codec/SkScaledCodec.cpp',
'../src/codec/SkSwizzler.cpp',
'../src/codec/SkWebpCodec.cpp',
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 52d7e45273..accdd61681 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -60,7 +60,8 @@
'../tools/SkBitmapRegionSampler.cpp',
],
'include_dirs': [
- '../include/private'
+ '../include/private',
+ '../src/codec',
],
'dependencies': [
'skia_lib.gyp:skia_lib',
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 7300a5c732..1b9cb0f5d7 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -132,10 +132,6 @@ public:
*/
kCouldNotRewind,
/**
- * startScanlineDecode() was not called before calling getScanlines.
- */
- kScanlineDecodingNotStarted,
- /**
* This method is not implemented by this codec.
* FIXME: Perhaps this should be kUnsupported?
*/
@@ -281,8 +277,11 @@ public:
* @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.
+ * @return the number of lines successfully decoded. If this value is
+ * less than countLines, this will fill the remaining lines with a
+ * default value.
*/
- Result getScanlines(void* dst, int countLines, size_t rowBytes);
+ int getScanlines(void* dst, int countLines, size_t rowBytes);
/**
* Skip count scanlines.
@@ -293,8 +292,15 @@ public:
* NOTE: If skipped lines are the only lines with alpha, this default
* will make reallyHasAlpha return true, when it could have returned
* false.
+ *
+ * @return true if the scanlines were successfully skipped
+ * false on failure, possible reasons for failure include:
+ * An incomplete input image stream.
+ * Calling this function before calling startScanlineDecode().
+ * If countLines is less than zero or so large that it moves
+ * the current scanline past the end of the image.
*/
- Result skipScanlines(int countLines);
+ bool skipScanlines(int countLines);
/**
* The order in which rows are output from the scanline decoder is not the
@@ -365,15 +371,24 @@ public:
/**
* 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.
+ * decoder.
+ *
+ * This will equal fCurrScanline, except in the case of strangely
+ * encoded image types (bottom-up bmps, interlaced gifs).
*
* Results are undefined when not in scanline decoding mode.
*/
- int nextScanline() const {
- return this->onNextScanline();
- }
+ int nextScanline() const { return this->outputScanline(fCurrScanline); }
+
+ /**
+ * Returns the output y-coordinate of the row that corresponds to an input
+ * y-coordinate. The input y-coordinate represents where the scanline
+ * is located in the encoded data.
+ *
+ * This will equal inputScanline, except in the case of strangely
+ * encoded image types (bottom-up bmps, interlaced gifs).
+ */
+ int outputScanline(int inputScanline) const;
protected:
SkCodec(const SkImageInfo&, SkStream*);
@@ -394,9 +409,16 @@ protected:
virtual SkEncodedFormat onGetEncodedFormat() const = 0;
+ /**
+ * @param rowsDecoded When the encoded image stream is incomplete, this function
+ * will return kIncompleteInput and rowsDecoded will be set to
+ * the number of scanlines that were successfully decoded.
+ * This will allow getPixels() to fill the uninitialized memory.
+ */
virtual Result onGetPixels(const SkImageInfo& info,
void* pixels, size_t rowBytes, const Options&,
- SkPMColor ctable[], int* ctableCount) = 0;
+ SkPMColor ctable[], int* ctableCount,
+ int* rowsDecoded) = 0;
virtual bool onGetValidSubset(SkIRect* /* desiredSubset */) const {
// By default, subsets are not supported.
@@ -427,6 +449,36 @@ protected:
}
/**
+ * On an incomplete input, getPixels() and getScanlines() will fill any uninitialized
+ * scanlines. This allows the subclass to indicate what value to fill with.
+ *
+ * @param colorType Destination color type.
+ * @param alphaType Destination alpha type.
+ * @return The value with which to fill uninitialized pixels.
+ *
+ * Note that we can interpret the return value as an SkPMColor, a 16-bit 565 color,
+ * an 8-bit gray color, or an 8-bit index into a color table, depending on the color
+ * type.
+ */
+ uint32_t getFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ return this->onGetFillValue(colorType, alphaType);
+ }
+
+ /**
+ * Some subclasses will override this function, but this is a useful default for the color
+ * types that we support. Note that for color types that do not use the full 32-bits,
+ * we will simply take the low bits of the fill value.
+ *
+ * kN32_SkColorType: Transparent or Black
+ * kRGB_565_SkColorType: Black
+ * kGray_8_SkColorType: Black
+ * kIndex_8_SkColorType: First color in color table
+ */
+ virtual uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ return kOpaque_SkAlphaType == alphaType ? SK_ColorBLACK : SK_ColorTRANSPARENT;
+ }
+
+ /**
* Get method for the input stream
*/
SkStream* stream() {
@@ -443,11 +495,6 @@ protected:
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; }
@@ -456,6 +503,8 @@ protected:
const SkCodec::Options& options() const { return fOptions; }
+ virtual int onOutputScanline(int inputScanline) const;
+
private:
const SkImageInfo fSrcInfo;
SkAutoTDelete<SkStream> fStream;
@@ -485,20 +534,36 @@ private:
}
// Naive default version just calls onGetScanlines on temp memory.
- virtual SkCodec::Result onSkipScanlines(int countLines) {
+ virtual bool onSkipScanlines(int countLines) {
+ // FIXME (msarett): Make this a pure virtual and always override this.
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);
+ return countLines == this->onGetScanlines(storage.get(), countLines, 0);
}
- virtual SkCodec::Result onGetScanlines(void* dst, int countLines,
- size_t rowBytes) {
- return kUnimplemented;
- }
+ virtual int onGetScanlines(void* dst, int countLines, size_t rowBytes) { return 0; }
+
+ /**
+ * On an incomplete decode, getPixels() and getScanlines() will call this function
+ * to fill any uinitialized memory.
+ *
+ * @param dstInfo Contains the destination color type
+ * Contains the destination alpha type
+ * Contains the destination width
+ * The height stored in this info is unused
+ * @param dst Pointer to the start of destination pixel memory
+ * @param rowBytes Stride length in destination pixel memory
+ * @param zeroInit Indicates if memory is zero initialized
+ * @param linesRequested Number of lines that the client requested
+ * @param linesDecoded Number of lines that were successfully decoded
+ */
+ void fillIncompleteImage(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
+ ZeroInitialized zeroInit, int linesRequested, int linesDecoded);
/**
* Return an object which will allow forcing scanline decodes to sample in X.
@@ -508,9 +573,8 @@ private:
*
* Only valid during scanline decoding.
*/
- virtual SkSampler* getSampler() { return nullptr; }
+ virtual SkSampler* getSampler(bool createIfNecessary) { return nullptr; }
- // Needed to call getSampler and dimensionsSupported.
friend class SkScaledCodec;
};
#endif // SkCodec_DEFINED
diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h
index 61208e316a..028706bc94 100644
--- a/include/codec/SkScaledCodec.h
+++ b/include/codec/SkScaledCodec.h
@@ -34,7 +34,7 @@ protected:
SkISize onGetScaledDimensions(float desiredScale) const override;
bool onDimensionsSupported(const SkISize&) override;
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
+ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
override;
SkEncodedFormat onGetEncodedFormat() const override {
return fCodec->getEncodedFormat();
@@ -44,6 +44,10 @@ protected:
return fCodec->reallyHasAlpha();
}
+ uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override;
+
+ SkScanlineOrder onGetScanlineOrder() const override;
+
private:
SkAutoTDelete<SkCodec> fCodec;
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index da3e0a2807..1aa43f583c 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -550,18 +550,6 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
}
/*
- * Get the destination row to start filling from
- * Used to fill the remainder of the image on incomplete input for bmps
- * This is tricky since bmps may be kTopDown or kBottomUp. For kTopDown,
- * we start filling from where we left off, but for kBottomUp we start
- * filling at the top of the image.
- */
-void* SkBmpCodec::getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const {
- return (SkCodec::kTopDown_SkScanlineOrder == fRowOrder) ?
- SkTAddOffset<void*>(dst, y * dstRowBytes) : dst;
-}
-
-/*
* Compute the number of colors in the color table
*/
uint32_t SkBmpCodec::computeNumColors(uint32_t numColors) {
@@ -588,14 +576,10 @@ SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
}
-SkCodec::Result SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+int 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);
// Decode the requested rows
return this->decodeRows(rowInfo, dst, rowBytes, this->options());
}
-
-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 19f66037e8..65662ff1f8 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -81,15 +81,6 @@ protected:
int32_t getDstRow(int32_t y, int32_t height) const;
/*
- * Get the destination row to start filling from
- * Used to fill the remainder of the image on incomplete input for bmps
- * This is tricky since bmps may be kTopDown or kBottomUp. For kTopDown,
- * we start filling from where we left off, but for kBottomUp we start
- * filling at the top of the image.
- */
- void* getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const;
-
- /*
* Compute the number of colors in the color table
*/
uint32_t computeNumColors(uint32_t numColors);
@@ -140,16 +131,15 @@ private:
* number of rows to decode at this time.
* @param dst Memory location to store output pixels
* @param dstRowBytes Bytes in a row of the destination
+ * @return Number of rows successfully decoded
*/
- virtual Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+ virtual int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
const Options& opts) = 0;
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;
+ int onGetScanlines(void* dst, int count, size_t rowBytes) override;
// TODO(msarett): Override default skipping with something more clever.
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 81067c7711..336698d319 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -29,7 +29,8 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
SkPMColor* inputColorPtr,
- int* inputColorCount) {
+ int* inputColorCount,
+ int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -49,7 +50,12 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
return result;
}
- return this->decodeRows(dstInfo, dst, dstRowBytes, opts);
+ uint32_t rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
+ if (rows != dstInfo.height()) {
+ *rowsDecoded = rows;
+ return kIncompleteInput;
+ }
+ return kSuccess;
}
bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo) {
@@ -78,7 +84,7 @@ SkCodec::Result SkBmpMaskCodec::prepareToDecode(const SkImageInfo& dstInfo,
/*
* Performs the decoding
*/
-SkCodec::Result SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo,
+int SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts) {
// Iterate over rows of the image
@@ -88,12 +94,7 @@ SkCodec::Result SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo,
// Read a row of the input
if (this->stream()->read(srcRow, fSrcRowBytes) != fSrcRowBytes) {
SkCodecPrintf("Warning: incomplete input stream.\n");
- // Fill the destination image on failure
- void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
- uint32_t fillColor = get_fill_color_or_index(dstInfo.alphaType());
- SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, height - y,
- fillColor, nullptr, opts.fZeroInitialized);
- return kIncompleteInput;
+ return y;
}
// Decode the row in destination format
@@ -103,5 +104,5 @@ SkCodec::Result SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo,
}
// Finished decoding the entire image
- return kSuccess;
+ return height;
}
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 1c44c7fb40..1c1d1d8c11 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -36,7 +36,7 @@ protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t dstRowBytes, const Options&, SkPMColor*,
- int*) override;
+ int*, int*) override;
SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo,
const SkCodec::Options& options, SkPMColor inputColorPtr[],
@@ -45,10 +45,13 @@ protected:
private:
bool initializeSwizzler(const SkImageInfo& dstInfo);
- SkSampler* getSampler() override { return fMaskSwizzler; }
+ SkSampler* getSampler(bool createIfNecessary) override {
+ SkASSERT(fMaskSwizzler);
+ return fMaskSwizzler;
+ }
- Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
- const Options& opts) override;
+ int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+ const Options& opts) override;
SkAutoTDelete<SkMasks> fMasks; // owned
SkAutoTDelete<SkMaskSwizzler> fMaskSwizzler;
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index fe499c6a6d..37af47600b 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -38,7 +38,8 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
SkPMColor* inputColorPtr,
- int* inputColorCount) {
+ int* inputColorCount,
+ int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -54,7 +55,16 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
}
// Perform the decode
- return this->decodeRows(dstInfo, dst, dstRowBytes, opts);
+ uint32_t rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
+ if (rows != dstInfo.height()) {
+ // We set rowsDecoded equal to the height because the background has already
+ // been filled. RLE encodings sometimes skip pixels, so we always start by
+ // filling the background.
+ *rowsDecoded = dstInfo.height();
+ return kIncompleteInput;
+ }
+
+ return kSuccess;
}
/*
@@ -275,9 +285,8 @@ SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo,
* Performs the bitmap decoding for RLE input format
* RLE decoding is performed all at once, rather than a one row at a time
*/
-SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
- void* dst, size_t dstRowBytes,
- const Options& opts) {
+int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowBytes,
+ const Options& opts) {
// Set RLE flags
static const uint8_t RLE_ESCAPE = 0;
static const uint8_t RLE_EOL = 0;
@@ -300,24 +309,23 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
// Because of the need for transparent pixels, kN32 is the only color
// type that makes sense for the destination format.
SkASSERT(kN32_SkColorType == dstInfo.colorType());
- SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT,
- NULL, opts.fZeroInitialized);
+ SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized);
while (true) {
// If we have reached a row that is beyond the requested height, we have
// succeeded.
if (y >= height) {
- // It would be better to check for the EOF marker before returning
+ // It would be better to check for the EOF marker before indicating
// success, but we may be performing a scanline decode, which
- // may require us to stop before decoding the full height.
- return kSuccess;
+ // would require us to stop before decoding the full height.
+ return height;
}
// Every entry takes at least two bytes
if ((int) fRLEBytes - fCurrRLEByte < 2) {
SkCodecPrintf("Warning: might be incomplete RLE input.\n");
if (this->checkForMoreData() < 2) {
- return kIncompleteInput;
+ return y;
}
}
@@ -342,7 +350,7 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
if ((int) fRLEBytes - fCurrRLEByte < 2) {
SkCodecPrintf("Warning: might be incomplete RLE input.\n");
if (this->checkForMoreData() < 2) {
- return kIncompleteInput;
+ return y;
}
}
// Modify x and y
@@ -352,7 +360,7 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
y += dy;
if (x > width || y > height) {
SkCodecPrintf("Warning: invalid RLE input.\n");
- return kInvalidInput;
+ return y - dy;
}
break;
}
@@ -368,14 +376,14 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
// image.
if (x + numPixels > width) {
SkCodecPrintf("Warning: invalid RLE input.\n");
- return kInvalidInput;
+ return y;
}
// Also abort if there are not enough bytes
// remaining in the stream to set numPixels.
if ((int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
SkCodecPrintf("Warning: might be incomplete RLE input.\n");
if (this->checkForMoreData() < SkAlign2(rowBytes)) {
- return kIncompleteInput;
+ return y;
}
}
// Set numPixels number of pixels
@@ -411,7 +419,7 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
}
default:
SkASSERT(false);
- return kInvalidInput;
+ return y;
}
}
// Skip a byte if necessary to maintain alignment
@@ -434,7 +442,7 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
if ((int) fRLEBytes - fCurrRLEByte < 2) {
SkCodecPrintf("Warning: might be incomplete RLE input.\n");
if (this->checkForMoreData() < 2) {
- return kIncompleteInput;
+ return y;
}
}
@@ -467,6 +475,9 @@ SkCodec::Result SkBmpRLECodec::decodeRows(const SkImageInfo& info,
}
}
+// FIXME: Make SkBmpRLECodec have no knowledge of sampling.
+// Or it should do all sampling natively.
+// It currently is a hybrid that needs to know what SkScaledCodec is doing.
class SkBmpRLESampler : public SkSampler {
public:
SkBmpRLESampler(SkBmpRLECodec* codec)
@@ -476,7 +487,7 @@ public:
}
private:
- int onSetSampleX(int sampleX) {
+ int onSetSampleX(int sampleX) override {
return fCodec->setSampleX(sampleX);
}
@@ -484,15 +495,15 @@ private:
SkBmpRLECodec* fCodec;
};
-SkSampler* SkBmpRLECodec::getSampler() {
- if (!fSampler) {
+SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) {
+ if (!fSampler && createIfNecessary) {
fSampler.reset(new SkBmpRLESampler(this));
}
return fSampler;
}
-int SkBmpRLECodec::setSampleX(int sampleX) {
+int SkBmpRLECodec::setSampleX(int sampleX){
fSampleX = sampleX;
return get_scaled_dimension(this->getInfo().width(), sampleX);
}
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
index 8721969a85..fcf910d8e5 100644
--- a/src/codec/SkBmpRLECodec.h
+++ b/src/codec/SkBmpRLECodec.h
@@ -46,7 +46,7 @@ protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t dstRowBytes, const Options&, SkPMColor*,
- int*) override;
+ int*, int*) override;
SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo,
const SkCodec::Options& options, SkPMColor inputColorPtr[],
@@ -84,10 +84,10 @@ private:
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
uint8_t red, uint8_t green, uint8_t blue);
- Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
- const Options& opts) override;
+ int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+ const Options& opts) override;
- SkSampler* getSampler() override;
+ SkSampler* getSampler(bool createIfNecessary) override;
SkAutoTUnref<SkColorTable> fColorTable; // owned
const uint32_t fNumColors;
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 540b9f3b2b..938fe8c788 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -36,7 +36,8 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
SkPMColor* inputColorPtr,
- int* inputColorCount) {
+ int* inputColorCount,
+ int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -54,9 +55,10 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
if (kSuccess != result) {
return result;
}
- result = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
- if (kSuccess != result) {
- return result;
+ uint32_t rows = this->decodeRows(dstInfo, dst, dstRowBytes, opts);
+ if (rows != dstInfo.height()) {
+ *rowsDecoded = rows;
+ return kIncompleteInput;
}
if (fInIco) {
return this->decodeIcoMask(dstInfo, dst, dstRowBytes);
@@ -227,7 +229,7 @@ SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo,
/*
* Performs the bitmap decoding for standard input format
*/
-SkCodec::Result SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo,
+int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts) {
// Iterate over rows of the image
@@ -236,13 +238,7 @@ SkCodec::Result SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo,
// Read a row of the input
if (this->stream()->read(fSrcBuffer.get(), fSrcRowBytes) != fSrcRowBytes) {
SkCodecPrintf("Warning: incomplete input stream.\n");
- // Fill the destination image on failure
- void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
- const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
- uint32_t fillColorOrIndex = get_fill_color_or_index(dstInfo.alphaType());
- SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y,
- fillColorOrIndex, colorPtr, opts.fZeroInitialized);
- return kIncompleteInput;
+ return y;
}
// Decode the row in destination format
@@ -253,7 +249,7 @@ SkCodec::Result SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo,
}
// Finished decoding the entire image
- return kSuccess;
+ return height;
}
// TODO (msarett): This function will need to be modified in order to perform row by row decodes
@@ -294,3 +290,11 @@ SkCodec::Result SkBmpStandardCodec::decodeIcoMask(const SkImageInfo& dstInfo,
}
return kSuccess;
}
+
+uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+ if (colorPtr) {
+ return get_color_table_fill_value(colorType, colorPtr, 0);
+ }
+ return INHERITED::onGetFillValue(colorType, alphaType);
+}
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index 0263570b85..d687eaad28 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -44,7 +44,7 @@ protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t dstRowBytes, const Options&, SkPMColor*,
- int*) override;
+ int*, int*) override;
bool onInIco() const override {
return fInIco;
@@ -54,7 +54,13 @@ protected:
const SkCodec::Options& options, SkPMColor inputColorPtr[],
int* inputColorCount) override;
- SkSampler* getSampler() override { return fSwizzler; }
+
+ uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override;
+
+ SkSampler* getSampler(bool createIfNecessary) override {
+ SkASSERT(fSwizzler);
+ return fSwizzler;
+ }
private:
@@ -66,8 +72,8 @@ private:
bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
- Result decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
- const Options& opts) override;
+ int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+ const Options& opts) override;
Result decodeIcoMask(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes);
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 1a901a97ac..0047d599ac 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -167,11 +167,26 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
return kInvalidScale;
}
- const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount);
+ // On an incomplete decode, the subclass will specify the number of scanlines that it decoded
+ // successfully.
+ int rowsDecoded = 0;
+ const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
+ &rowsDecoded);
if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
}
+
+ // A return value of kIncompleteInput indicates a truncated image stream.
+ // In this case, we will fill any uninitialized memory with a default value.
+ // Some subclasses will take care of filling any uninitialized memory on
+ // their own. They indicate that all of the memory has been filled by
+ // setting rowsDecoded equal to the height.
+ if (kIncompleteInput == result && rowsDecoded != info.height()) {
+ this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(),
+ rowsDecoded);
+ }
+
return result;
}
@@ -233,36 +248,101 @@ 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) {
+int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
if (fCurrScanline < 0) {
- return kScanlineDecodingNotStarted;
+ return 0;
}
SkASSERT(!fDstInfo.isEmpty());
-
if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
- return kInvalidParameters;
+ return 0;
}
- const Result result = this->onGetScanlines(dst, countLines, rowBytes);
+ const int linesDecoded = this->onGetScanlines(dst, countLines, rowBytes);
+ if (linesDecoded < countLines) {
+ this->fillIncompleteImage(this->dstInfo(), dst, rowBytes, this->options().fZeroInitialized,
+ countLines, linesDecoded);
+ }
fCurrScanline += countLines;
- return result;
+ return linesDecoded;
}
-SkCodec::Result SkCodec::skipScanlines(int countLines) {
+bool SkCodec::skipScanlines(int countLines) {
if (fCurrScanline < 0) {
- return kScanlineDecodingNotStarted;
+ return false;
}
SkASSERT(!fDstInfo.isEmpty());
- if (fCurrScanline + countLines > fDstInfo.height()) {
+ if (countLines < 0 || 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
+ // and return true. We choose to return false so the client
// can catch their bug.
- return SkCodec::kInvalidParameters;
+ return false;
}
- const Result result = this->onSkipScanlines(countLines);
+ bool result = this->onSkipScanlines(countLines);
fCurrScanline += countLines;
return result;
}
+
+int SkCodec::outputScanline(int inputScanline) const {
+ SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
+ return this->onOutputScanline(inputScanline);
+}
+
+int SkCodec::onOutputScanline(int inputScanline) const {
+ switch (this->getScanlineOrder()) {
+ case kTopDown_SkScanlineOrder:
+ case kNone_SkScanlineOrder:
+ return inputScanline;
+ case kBottomUp_SkScanlineOrder:
+ return this->getInfo().height() - inputScanline - 1;
+ default:
+ // This case indicates an interlaced gif and is implemented by SkGifCodec.
+ SkASSERT(false);
+ return 0;
+ }
+}
+
+static void fill_proc(const SkImageInfo& info, void* dst, size_t rowBytes,
+ uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit, SkSampler* sampler) {
+ if (sampler) {
+ sampler->fill(info, dst, rowBytes, colorOrIndex, zeroInit);
+ } else {
+ SkSampler::Fill(info, dst, rowBytes, colorOrIndex, zeroInit);
+ }
+}
+
+void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes,
+ ZeroInitialized zeroInit, int linesRequested, int linesDecoded) {
+
+ void* fillDst;
+ const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType());
+ const int linesRemaining = linesRequested - linesDecoded;
+ SkSampler* sampler = this->getSampler(false);
+
+ switch (this->getScanlineOrder()) {
+ case kTopDown_SkScanlineOrder:
+ case kNone_SkScanlineOrder: {
+ const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
+ fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
+ break;
+ }
+ case kBottomUp_SkScanlineOrder: {
+ fillDst = dst;
+ const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining);
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
+ break;
+ }
+ case kOutOfOrder_SkScanlineOrder: {
+ SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested);
+ const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
+ for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
+ fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes);
+ fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler);
+ }
+ break;
+ }
+ }
+}
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index 2769cec1cd..0442625dd2 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -8,6 +8,7 @@
#ifndef SkCodecPriv_DEFINED
#define SkCodecPriv_DEFINED
+#include "SkColorPriv.h"
#include "SkColorTable.h"
#include "SkImageInfo.h"
#include "SkSwizzler.h"
@@ -34,7 +35,7 @@
* returns a scaled dimension based on the original dimension and the sampleSize
* NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder
*/
-static int get_scaled_dimension(int srcDimension, int sampleSize) {
+inline int get_scaled_dimension(int srcDimension, int sampleSize) {
if (sampleSize > srcDimension) {
return 1;
}
@@ -47,7 +48,7 @@ static int get_scaled_dimension(int srcDimension, int sampleSize) {
*
* This does not need to be called and is not called when sampleFactor == 1.
*/
-static int get_start_coord(int sampleFactor) { return sampleFactor / 2; };
+inline int get_start_coord(int sampleFactor) { return sampleFactor / 2; };
/*
* Given a coordinate in the original image, this returns the corresponding
@@ -57,7 +58,7 @@ static int get_start_coord(int sampleFactor) { return sampleFactor / 2; };
*
* This does not need to be called and is not called when sampleFactor == 1.
*/
-static int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; };
+inline int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sampleFactor; };
/*
* When scaling, we will discard certain y-coordinates (rows) and
@@ -67,7 +68,7 @@ static int get_dst_coord(int srcCoord, int sampleFactor) { return srcCoord / sam
*
* This does not need to be called and is not called when sampleFactor == 1.
*/
-static bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) {
+inline bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) {
// Get the first coordinate that we want to keep
int startCoord = get_start_coord(sampleFactor);
@@ -80,7 +81,7 @@ static bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) {
return ((srcCoord - startCoord) % sampleFactor) == 0;
}
-static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
+inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
// Check for supported alpha types
if (srcAlpha != dstAlpha) {
if (kOpaque_SkAlphaType == srcAlpha) {
@@ -110,7 +111,7 @@ static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
* - always support N32
* - otherwise match the src color type
*/
-static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
+inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
if (dst.profileType() != src.profileType()) {
return false;
}
@@ -134,15 +135,34 @@ static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src)
/*
* If there is a color table, get a pointer to the colors, otherwise return nullptr
*/
-static const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
+inline const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
return nullptr != colorTable ? colorTable->readColors() : nullptr;
}
/*
+ * Given that the encoded image uses a color table, return the fill value
+ */
+inline uint32_t get_color_table_fill_value(SkColorType colorType, const SkPMColor* colorPtr,
+ uint8_t fillIndex) {
+ SkASSERT(nullptr != colorPtr);
+ switch (colorType) {
+ case kN32_SkColorType:
+ return colorPtr[fillIndex];
+ case kRGB_565_SkColorType:
+ return SkPixel32ToPixel16(colorPtr[fillIndex]);
+ case kIndex_8_SkColorType:
+ return fillIndex;
+ default:
+ SkASSERT(false);
+ return 0;
+ }
+}
+
+/*
*
* Copy the codec color table back to the client when kIndex8 color type is requested
*/
-static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
+inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
SkPMColor* inputColorPtr, int* inputColorCount) {
if (kIndex_8_SkColorType == dstInfo.colorType()) {
SkASSERT(nullptr != inputColorPtr);
@@ -155,21 +175,21 @@ static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* co
/*
* Compute row bytes for an image using pixels per byte
*/
-static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
+inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
return (width + pixelsPerByte - 1) / pixelsPerByte;
}
/*
* Compute row bytes for an image using bytes per pixel
*/
-static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
+inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
return width * bytesPerPixel;
}
/*
* Compute row bytes for an image
*/
-static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
+inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
if (bitsPerPixel < 16) {
SkASSERT(0 == 8 % bitsPerPixel);
const uint32_t pixelsPerByte = 8 / bitsPerPixel;
@@ -182,27 +202,10 @@ static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
}
/*
- * On incomplete images, get the color to fill with
- */
-static inline SkPMColor get_fill_color_or_index(SkAlphaType alphaType) {
- // This condition works properly for all supported output color types.
- // kIndex8: The low 8-bits of both possible return values is 0, which is
- // our desired default index.
- // kGray8: The low 8-bits of both possible return values is 0, which is
- // black, our desired fill value.
- // kRGB565: The low 16-bits of both possible return values is 0, which is
- // black, our desired fill value.
- // kN32: Return black for opaque images and transparent for non-opaque
- // images.
- return kOpaque_SkAlphaType == alphaType ?
- SK_ColorBLACK : SK_ColorTRANSPARENT;
-}
-
-/*
* Get a byte from a buffer
* This method is unsafe, the caller is responsible for performing a check
*/
-static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
+inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
return buffer[i];
}
@@ -210,7 +213,7 @@ static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
* Get a short from a buffer
* This method is unsafe, the caller is responsible for performing a check
*/
-static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
+inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
uint16_t result;
memcpy(&result, &(buffer[i]), 2);
#ifdef SK_CPU_BENDIAN
@@ -224,7 +227,7 @@ static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
* Get an int from a buffer
* This method is unsafe, the caller is responsible for performing a check
*/
-static inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
+inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
uint32_t result;
memcpy(&result, &(buffer[i]), 4);
#ifdef SK_CPU_BENDIAN
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
index 7b380473fc..856f69ba3c 100644
--- a/src/codec/SkCodec_libgif.cpp
+++ b/src/codec/SkCodec_libgif.cpp
@@ -95,14 +95,14 @@ static uint32_t find_trans_index(const SavedImage& image) {
return SK_MaxU32;
}
-static inline uint32_t ceil_div(uint32_t a, uint32_t b) {
+inline uint32_t ceil_div(uint32_t a, uint32_t b) {
return (a + b - 1) / b;
}
/*
* Gets the output row corresponding to the encoded row for interlaced gifs
*/
-static uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) {
+inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) {
SkASSERT(encodedRow < height);
// First pass
if (encodedRow * 8 < height) {
@@ -460,11 +460,8 @@ SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo,
return kUnimplemented;
}
-SkCodec::Result SkGifCodec::readRow() {
- if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width())) {
- return kIncompleteInput;
- }
- return kSuccess;
+bool SkGifCodec::readRow() {
+ return GIF_ERROR != DGifGetLine(fGif, fSrcBuffer.get(), fFrameRect.width());
}
/*
@@ -474,7 +471,8 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
SkPMColor* inputColorPtr,
- int* inputColorCount) {
+ int* inputColorCount,
+ int* rowsDecoded) {
Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
if (kSuccess != result) {
return result;
@@ -492,9 +490,9 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
}
// Fill the background
- const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
- SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(),
- fFillIndex, colorPtr, opts.fZeroInitialized);
+ SkSampler::Fill(dstInfo, dst, dstRowBytes,
+ this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()),
+ opts.fZeroInitialized);
// Modify the dst pointer
const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorType());
@@ -506,49 +504,30 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
}
}
- // Check the interlace flag and iterate over rows of the input
- uint32_t width = fFrameRect.width();
+ // Iterate over rows of the input
uint32_t height = fFrameRect.height();
- if (fGif->Image.Interlace) {
- // In interlace mode, the rows of input are rearranged in
- // the output image. We a helper function to help us
- // rearrange the decoded rows.
- for (uint32_t y = 0; y < height; y++) {
- if (kSuccess != this->readRow()) {
- // Recover from error by filling remainder of image
- memset(fSrcBuffer.get(), fFillIndex, width);
- for (; y < height; y++) {
- void* dstRow = SkTAddOffset<void>(dst,
- dstRowBytes * get_output_row_interlaced(y, height));
- fSwizzler->swizzle(dstRow, fSrcBuffer.get());
- }
- return gif_error("Could not decode line.\n", kIncompleteInput);
- }
- void* dstRow = SkTAddOffset<void>(dst,
- dstRowBytes * get_output_row_interlaced(y, height));
- fSwizzler->swizzle(dstRow, fSrcBuffer.get());
- }
- } else {
- // Standard mode
- void* dstRow = dst;
- for (uint32_t y = 0; y < height; y++) {
- if (kSuccess != this->readRow()) {
- const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
- SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes,
- height - y, fFillIndex, colorPtr, opts.fZeroInitialized);
- return gif_error("Could not decode line\n", kIncompleteInput);
- }
- fSwizzler->swizzle(dstRow, fSrcBuffer.get());
- dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
+ for (uint32_t y = 0; y < height; y++) {
+ if (!this->readRow()) {
+ *rowsDecoded = y;
+ return gif_error("Could not decode line.\n", kIncompleteInput);
}
+ void* dstRow = SkTAddOffset<void>(dst, dstRowBytes * this->outputScanline(y));
+ fSwizzler->swizzle(dstRow, fSrcBuffer.get());
}
return kSuccess;
}
+// FIXME: This is similar to the implementation for bmp and png. Can we share more code or
+// possibly make this non-virtual?
+uint32_t SkGifCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+ return get_color_table_fill_value(colorType, colorPtr, fFillIndex);
+}
+
SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
const SkCodec::Options& opts, SkPMColor inputColorPtr[], int* inputColorCount) {
- Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount,
- this->options());
+
+ Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, this->options());
if (kSuccess != result) {
return result;
}
@@ -568,58 +547,56 @@ SkCodec::Result SkGifCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kSuccess;
}
-SkCodec::Result SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+ int rowsBeforeFrame = 0;
+ int rowsAfterFrame = 0;
+ int rowsInFrame = count;
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);
+ SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count);
+ uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType(),
+ this->dstInfo().alphaType());
+ SkSampler::Fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized);
// Do nothing for rows before the image frame
- int rowsBeforeFrame = SkTMax(0, fFrameRect.top() - this->INHERITED::onNextScanline());
- count = SkTMax(0, count - rowsBeforeFrame);
+ rowsBeforeFrame = SkTMax(0, fFrameRect.top() - this->INHERITED::nextScanline());
+ rowsInFrame = SkTMax(0, rowsInFrame - rowsBeforeFrame);
dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame);
// Do nothing for rows after the image frame
- int rowsAfterFrame = SkTMax(0,
- this->INHERITED::onNextScanline() + count - fFrameRect.bottom());
- count = SkTMax(0, count - rowsAfterFrame);
+ rowsAfterFrame = SkTMax(0,
+ this->INHERITED::nextScanline() + rowsInFrame - fFrameRect.bottom());
+ rowsInFrame = SkTMax(0, rowsInFrame - rowsAfterFrame);
// Adjust dst pointer for left offset
int offset = SkColorTypeBytesPerPixel(this->dstInfo().colorType()) * fFrameRect.left();
dst = SkTAddOffset<void>(dst, offset);
}
- for (int i = 0; i < count; i++) {
- if (kSuccess != this->readRow()) {
- const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
- SkSwizzler::Fill(dst, this->dstInfo().makeWH(fFrameRect.width(),
- this->dstInfo().height()), rowBytes, count - i, fFillIndex, colorPtr,
- this->options().fZeroInitialized);
- return kIncompleteInput;
+ for (int i = 0; i < rowsInFrame; i++) {
+ if (!this->readRow()) {
+ return i + rowsBeforeFrame;
}
fSwizzler->swizzle(dst, fSrcBuffer.get());
dst = SkTAddOffset<void>(dst, rowBytes);
}
- return kSuccess;
+
+ return count;
}
SkCodec::SkScanlineOrder SkGifCodec::onGetScanlineOrder() const {
if (fGif->Image.Interlace) {
return kOutOfOrder_SkScanlineOrder;
- } else {
- return kTopDown_SkScanlineOrder;
}
+ return kTopDown_SkScanlineOrder;
}
-int SkGifCodec::onNextScanline() const {
- int nextScanline = this->INHERITED::onNextScanline();
+int SkGifCodec::onOutputScanline(int inputScanline) const {
if (fGif->Image.Interlace) {
- if (nextScanline < fFrameRect.top() || nextScanline >= fFrameRect.bottom()) {
- return nextScanline;
+ if (inputScanline < fFrameRect.top() || inputScanline >= fFrameRect.bottom()) {
+ return inputScanline;
}
- return get_output_row_interlaced(nextScanline - fFrameRect.top(), fFrameRect.height());
+ return get_output_row_interlaced(inputScanline - fFrameRect.top(), fFrameRect.height());
}
- return nextScanline;
+ return inputScanline;
}
-
diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h
index f777e58094..10fdac97f9 100644
--- a/src/codec/SkCodec_libgif.h
+++ b/src/codec/SkCodec_libgif.h
@@ -60,7 +60,7 @@ protected:
* Performs the full gif decode
*/
Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&,
- SkPMColor*, int32_t*) override;
+ SkPMColor*, int*, int*) override;
SkEncodedFormat onGetEncodedFormat() const override {
return kGIF_SkEncodedFormat;
@@ -68,6 +68,10 @@ protected:
bool onRewind() override;
+ uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override;
+
+ int onOutputScanline(int inputScanline) const;
+
private:
/*
@@ -129,23 +133,23 @@ private:
*/
Result initializeSwizzler(const SkImageInfo& dstInfo, ZeroInitialized zeroInit);
- SkSampler* getSampler() override { return fSwizzler; }
+ SkSampler* getSampler(bool createIfNecessary) override {
+ SkASSERT(fSwizzler);
+ return fSwizzler;
+ }
/*
- * @return kSuccess if the read is successful and kIncompleteInput if the
- * read fails.
+ * @return true if the read is successful and false if the read fails.
*/
- Result readRow();
+ bool readRow();
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opts,
SkPMColor inputColorPtr[], int* inputColorCount) override;
- Result onGetScanlines(void* dst, int count, size_t rowBytes) override;
+ int 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
* It is used in a SkAutoTCallIProc template
diff --git a/src/codec/SkCodec_libico.cpp b/src/codec/SkCodec_libico.cpp
index 62562a19d5..8c5a1b3412 100644
--- a/src/codec/SkCodec_libico.cpp
+++ b/src/codec/SkCodec_libico.cpp
@@ -113,7 +113,7 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
for (uint32_t i = 0; i < numImages; i++) {
uint32_t offset = directoryEntries.get()[i].offset;
uint32_t size = directoryEntries.get()[i].size;
-
+
// Ensure that the offset is valid
if (offset < bytesRead) {
SkCodecPrintf("Warning: invalid ico offset.\n");
@@ -242,8 +242,8 @@ bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) {
*/
SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
- const Options& opts, SkPMColor* ct,
- int* ptr) {
+ const Options& opts, SkPMColor* colorTable,
+ int* colorCount, int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -279,7 +279,11 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
break;
}
SkImageInfo info = dstInfo.makeAlphaType(embeddedAlpha);
- result = embeddedCodec->getPixels(info, dst, dstRowBytes, &opts, ct, ptr);
+ result = embeddedCodec->getPixels(info, dst, dstRowBytes, &opts, colorTable,
+ colorCount);
+ // The embedded codec will handle filling incomplete images, so we will indicate
+ // that all of the rows are initialized.
+ *rowsDecoded = info.height();
// On a fatal error, keep trying to find an image to decode
if (kInvalidConversion == result || kInvalidInput == result ||
diff --git a/src/codec/SkCodec_libico.h b/src/codec/SkCodec_libico.h
index a815e300dd..92675f4d74 100644
--- a/src/codec/SkCodec_libico.h
+++ b/src/codec/SkCodec_libico.h
@@ -40,9 +40,8 @@ protected:
/*
* Initiates the Ico decode
*/
- Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options&, SkPMColor*, int*)
- override;
+ Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
+ SkPMColor*, int*, int*) override;
SkEncodedFormat onGetEncodedFormat() const override {
return kICO_SkEncodedFormat;
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index ee8d49263f..e828e24999 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -466,7 +466,8 @@ bool SkPngCodec::onRewind() {
SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
size_t dstRowBytes, const Options& options,
- SkPMColor ctable[], int* ctableCount) {
+ SkPMColor ctable[], int* ctableCount,
+ int* rowsDecoded) {
if (!conversion_possible(requestedInfo, this->getInfo())) {
return kInvalidConversion;
}
@@ -483,14 +484,32 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
}
// FIXME: Could we use the return value of setjmp to specify the type of
// error?
+ int row = 0;
+ // This must be declared above the call to setjmp to avoid memory leaks on incomplete images.
+ SkAutoMalloc storage;
if (setjmp(png_jmpbuf(fPng_ptr))) {
- SkCodecPrintf("setjmp long jump!\n");
- return kInvalidInput;
+ // Assume that any error that occurs while reading rows is caused by an incomplete input.
+ if (fNumberPasses > 1) {
+ // FIXME (msarett): Handle incomplete interlaced pngs.
+ return kInvalidInput;
+ }
+ // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium,
+ // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192
+ // bytes), and if we can't fill the buffer, we immediately fail.
+ // For example, if we try to read 8192 bytes, and the image (incorrectly) only contains
+ // half that, which may have been enough to contain a non-zero number of lines, we fail
+ // when we could have decoded a few more lines and then failed.
+ // The read function that we provide for libpng has no way of indicating that we have
+ // made a partial read.
+ // Making our buffer size smaller improves our incomplete decodes, but what impact does
+ // it have on regular decode performance? Should we investigate using a different API
+ // instead of png_read_row(s)? Chromium uses png_process_data.
+ *rowsDecoded = row;
+ return kIncompleteInput;
}
bool hasAlpha = false;
// FIXME: We could split these out based on subclass.
- SkAutoMalloc storage;
void* dstRow = dst;
if (fNumberPasses > 1) {
const int width = requestedInfo.width();
@@ -520,7 +539,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
} else {
storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
- for (int y = 0; y < requestedInfo.height(); y++) {
+ for (; row < requestedInfo.height(); row++) {
png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
// FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines.
hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
@@ -549,6 +568,14 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
return kSuccess;
}
+uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
+ if (colorPtr) {
+ return get_color_table_fill_value(colorType, colorPtr, 0);
+ }
+ return INHERITED::onGetFillValue(colorType, alphaType);
+}
+
bool SkPngCodec::onReallyHasAlpha() const {
switch (fAlphaState) {
case kOpaque_AlphaState:
@@ -603,15 +630,17 @@ public:
return kSuccess;
}
- Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
+ int onGetScanlines(void* dst, int count, size_t rowBytes) override {
+ // Assume that an error in libpng indicates an incomplete input.
+ int row = 0;
if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return kInvalidInput;
+ return row;
}
void* dstRow = dst;
bool hasAlpha = false;
- for (int i = 0; i < count; i++) {
+ for (; row < count; row++) {
png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow));
dstRow = SkTAddOffset<void>(dstRow, rowBytes);
@@ -626,23 +655,22 @@ public:
// Otherwise, the AlphaState is unchanged.
}
- return kSuccess;
+ return row;
}
- Result onSkipScanlines(int count) override {
- // FIXME: Could we use the return value of setjmp to specify the type of
- // error?
+ bool onSkipScanlines(int count) override {
+ // Assume that an error in libpng indicates an incomplete input.
if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return kInvalidInput;
+ return false;
}
//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++) {
+ for (int row = 0; row < count; row++) {
png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
}
- return SkCodec::kSuccess;
+ return true;
}
AlphaState alphaInScanlineDecode() const override {
@@ -694,7 +722,7 @@ public:
return SkCodec::kSuccess;
}
- Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
+ int 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) {
@@ -707,7 +735,7 @@ public:
// 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();
+ const int currScanline = this->nextScanline();
// This method would never be called if currScanline is -1
SkASSERT(currScanline != -1);
@@ -719,12 +747,15 @@ public:
if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return kInvalidInput;
+ // FIXME (msarett): Returning 0 is pessimistic. If we can complete a single pass,
+ // we may be able to report that all of the memory has been initialized. Even if we
+ // fail on the first pass, we can still report than some scanlines are initialized.
+ return 0;
}
SkAutoMalloc storage(count * fSrcRowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
uint8_t* srcRow;
- const int startRow = this->onNextScanline();
+ const int startRow = this->nextScanline();
for (int i = 0; i < this->numberPasses(); i++) {
// read rows we planned to skip into garbage row
for (int y = 0; y < startRow; y++){
@@ -760,12 +791,12 @@ public:
// Otherwise, the AlphaState is unchanged.
}
- return kSuccess;
+ return count;
}
- SkCodec::Result onSkipScanlines(int count) override {
+ bool onSkipScanlines(int count) override {
// The non-virtual version will update fCurrScanline.
- return SkCodec::kSuccess;
+ return true;
}
AlphaState alphaInScanlineDecode() const override {
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index 3a16cc6813..6bdf58065b 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -32,16 +32,20 @@ public:
virtual ~SkPngCodec();
protected:
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
+ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
override;
SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; }
bool onRewind() override;
+ uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override;
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);
- SkSampler* getSampler() override { return fSwizzler; }
+ SkSampler* getSampler(bool createIfNecessary) override {
+ SkASSERT(fSwizzler);
+ return fSwizzler;
+ }
SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int, int);
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
index d7f446bb57..14b720988f 100644
--- a/src/codec/SkCodec_wbmp.cpp
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -86,11 +86,8 @@ SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info,
opts.fZeroInitialized);
}
-SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) {
- if (this->stream()->read(row, fSrcRowBytes) != fSrcRowBytes) {
- return kIncompleteInput;
- }
- return kSuccess;
+bool SkWbmpCodec::readRow(uint8_t* row) {
+ return this->stream()->read(row, fSrcRowBytes) == fSrcRowBytes;
}
SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
@@ -109,7 +106,8 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
size_t rowBytes,
const Options& options,
SkPMColor ctable[],
- int* ctableCount) {
+ int* ctableCount,
+ int* rowsDecoded) {
if (options.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -133,9 +131,9 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
SkAutoTMalloc<uint8_t> src(fSrcRowBytes);
void* dstRow = dst;
for (int y = 0; y < size.height(); ++y) {
- Result rowResult = this->readRow(src.get());
- if (kSuccess != rowResult) {
- return rowResult;
+ if (!this->readRow(src.get())) {
+ *rowsDecoded = y;
+ return kIncompleteInput;
}
swizzler->swizzle(dstRow, src.get());
dstRow = SkTAddOffset<void>(dstRow, rowBytes);
@@ -158,17 +156,16 @@ SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
return new SkWbmpCodec(info, streamDeleter.detach());
}
-SkCodec::Result SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) {
+int 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;
+ if (!this->readRow(fSrcBuffer.get())) {
+ return y;
}
fSwizzler->swizzle(dstRow, fSrcBuffer.get());
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
}
- return kSuccess;
+ return count;
}
SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
@@ -201,4 +198,3 @@ SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kSuccess;
}
-
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
index 976a5a22f0..f54dd0f166 100644
--- a/src/codec/SkCodec_wbmp.h
+++ b/src/codec/SkCodec_wbmp.h
@@ -25,7 +25,7 @@ public:
protected:
SkEncodedFormat onGetEncodedFormat() const override;
Result onGetPixels(const SkImageInfo&, void*, size_t,
- const Options&, SkPMColor[], int*) override;
+ const Options&, SkPMColor[], int*, int*) override;
bool onRewind() override;
private:
/*
@@ -33,24 +33,27 @@ private:
*/
SkSwizzler* initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
const Options& opts);
- SkSampler* getSampler() override { return fSwizzler; }
+ SkSampler* getSampler(bool createIfNecessary) override {
+ SkASSERT(fSwizzler || !createIfNecessary);
+ return fSwizzler;
+ }
/*
* Read a src row from the encoded stream
*/
- Result readRow(uint8_t* row);
+ bool readRow(uint8_t* row);
SkWbmpCodec(const SkImageInfo&, SkStream*);
- const size_t fSrcRowBytes;
+ const size_t fSrcRowBytes;
// Used for scanline decodes:
- SkAutoTUnref<SkColorTable> fColorTable;
SkAutoTDelete<SkSwizzler> fSwizzler;
+ SkAutoTUnref<SkColorTable> fColorTable;
SkAutoTMalloc<uint8_t> fSrcBuffer;
// FIXME: Override onSkipScanlines to avoid swizzling.
- Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override;
+ int onGetScanlines(void* dst, int count, size_t dstRowBytes) override;
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
SkPMColor inputColorTable[], int* inputColorCount) override;
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 30c1a39f06..196543b682 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -325,7 +325,8 @@ bool SkJpegCodec::onDimensionsSupported(const SkISize& size) {
*/
SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
- const Options& options, SkPMColor*, int*) {
+ const Options& options, SkPMColor*, int*,
+ int* rowsDecoded) {
if (options.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -358,20 +359,11 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
JSAMPLE* dstRow = (JSAMPLE*) dst;
for (uint32_t y = 0; y < dstHeight; y++) {
// Read rows of the image
- uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, &dstRow, 1);
+ uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1);
// If we cannot read enough rows, assume the input is incomplete
- if (rowsDecoded != 1) {
- // Fill the remainder of the image with black. This error handling
- // behavior is unspecified but SkCodec consistently uses black as
- // the fill color for opaque images. If the destination is kGray,
- // the low 8 bits of SK_ColorBLACK will be used. Conveniently,
- // these are zeros, which is the representation for black in kGray.
- // If the destination is kRGB_565, the low 16 bits of SK_ColorBLACK
- // will be used. Conveniently, these are zeros, which is the
- // representation for black in kRGB_565.
- SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, dstHeight - y,
- SK_ColorBLACK, nullptr, options.fZeroInitialized);
+ if (lines != 1) {
+ *rowsDecoded = y;
return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
}
@@ -388,9 +380,9 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
return kSuccess;
}
-SkSampler* SkJpegCodec::getSampler() {
- if (fSwizzler) {
- SkASSERT(fSrcRow && static_cast<uint8_t*>(fStorage.get()) == fSrcRow);
+SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
+ if (!createIfNecessary || fSwizzler) {
+ SkASSERT(!fSwizzler || (fSrcRow && static_cast<uint8_t*>(fStorage.get()) == fSrcRow));
return fSwizzler;
}
@@ -452,7 +444,7 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kSuccess;
}
-SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
+int 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);
@@ -471,10 +463,8 @@ SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowByte
// 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;
+ return y;
}
// Convert to RGBA if necessary
@@ -490,7 +480,7 @@ SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowByte
dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes);
}
}
- return kSuccess;
+ return count;
}
#ifndef TURBO_HAS_SKIP
@@ -504,14 +494,11 @@ SkCodec::Result SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowByte
}
#endif
-SkCodec::Result SkJpegCodec::onSkipScanlines(int count) {
+bool SkJpegCodec::onSkipScanlines(int count) {
// Set the jump location for libjpeg errors
if (setjmp(fDecoderMgr->getJmpBuf())) {
- return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
+ return fDecoderMgr->returnFalse("setjmp");
}
- jpeg_skip_scanlines(fDecoderMgr->dinfo(), count);
-
- return kSuccess;
+ return count == jpeg_skip_scanlines(fDecoderMgr->dinfo(), count);
}
-
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 6377c9d469..67680d66e8 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -50,7 +50,7 @@ protected:
* Initiates the jpeg decode
*/
Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
- SkPMColor*, int*) override;
+ SkPMColor*, int*, int*) override;
SkEncodedFormat onGetEncodedFormat() const override {
return kJPEG_SkEncodedFormat;
@@ -103,11 +103,11 @@ private:
bool setOutputColorSpace(const SkImageInfo& dst);
// scanline decoding
- SkSampler* getSampler() override;
+ SkSampler* getSampler(bool createIfNecessary) override;
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;
+ int onGetScanlines(void* dst, int count, size_t rowBytes) override;
+ bool onSkipScanlines(int count) override;
SkAutoTDelete<JpegDecoderMgr> fDecoderMgr;
// We will save the state of the decompress struct after reading the header.
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
index fbc951a070..0513d838c7 100644
--- a/src/codec/SkMaskSwizzler.h
+++ b/src/codec/SkMaskSwizzler.h
@@ -35,6 +35,15 @@ public:
*/
SkSwizzler::ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT src);
+ /**
+ * Implement fill using a custom width.
+ */
+ void fill(const SkImageInfo& info, void* dst, size_t rowBytes, uint32_t colorOrIndex,
+ SkCodec::ZeroInitialized zeroInit) override {
+ const SkImageInfo fillInfo = info.makeWH(fDstWidth, info.height());
+ SkSampler::Fill(fillInfo, dst, rowBytes, colorOrIndex, zeroInit);
+ }
+
private:
/*
diff --git a/src/codec/SkSampler.cpp b/src/codec/SkSampler.cpp
new file mode 100644
index 0000000000..c69d003c0f
--- /dev/null
+++ b/src/codec/SkSampler.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkSampler.h"
+#include "SkUtils.h"
+
+void SkSampler::Fill(const SkImageInfo& info, void* dst, size_t rowBytes,
+ uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit) {
+ SkASSERT(dst != nullptr);
+
+ // Calculate bytes to fill. We use getSafeSize since the last row may not be padded.
+ const size_t bytesToFill = info.getSafeSize(rowBytes);
+ const int width = info.width();
+ const int numRows = info.height();
+
+ // Use the proper memset routine to fill the remaining bytes
+ switch (info.colorType()) {
+ case kN32_SkColorType: {
+ // If memory is zero initialized, we may not need to fill
+ uint32_t color = colorOrIndex;
+ if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) {
+ return;
+ }
+
+ // We must fill row by row in the case of unaligned row bytes
+ if (SkIsAlign4((size_t) dst) && SkIsAlign4(rowBytes)) {
+ sk_memset32((uint32_t*) dst, color,
+ (uint32_t) bytesToFill / sizeof(SkPMColor));
+ } else {
+ // We must fill row by row in the case of unaligned row bytes. This is an
+ // unlikely, slow case.
+ SkCodecPrintf("Warning: Strange number of row bytes, fill will be slow.\n");
+ uint32_t* dstRow = (uint32_t*) dst;
+ for (int row = 0; row < numRows; row++) {
+ for (int col = 0; col < width; col++) {
+ dstRow[col] = color;
+ }
+ dstRow = SkTAddOffset<uint32_t>(dstRow, rowBytes);
+ }
+ }
+ break;
+ }
+ case kRGB_565_SkColorType: {
+ // If the destination is k565, the caller passes in a 16-bit color.
+ // We will not assert that the high bits of colorOrIndex must be zeroed.
+ // This allows us to take advantage of the fact that the low 16 bits of an
+ // SKPMColor may be a valid a 565 color. For example, the low 16
+ // bits of SK_ColorBLACK are identical to the 565 representation
+ // for black.
+
+ // If memory is zero initialized, we may not need to fill
+ uint16_t color = (uint16_t) colorOrIndex;
+ if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) {
+ return;
+ }
+
+ if (SkIsAlign2((size_t) dst) && SkIsAlign2(rowBytes)) {
+ sk_memset16((uint16_t*) dst, color, (uint32_t) bytesToFill / sizeof(uint16_t));
+ } else {
+ // We must fill row by row in the case of unaligned row bytes. This is an
+ // unlikely, slow case.
+ SkCodecPrintf("Warning: Strange number of row bytes, fill will be slow.\n");
+ uint16_t* dstRow = (uint16_t*) dst;
+ for (int row = 0; row < numRows; row++) {
+ for (int col = 0; col < width; col++) {
+ dstRow[col] = color;
+ }
+ dstRow = SkTAddOffset<uint16_t>(dstRow, rowBytes);
+ }
+ }
+ break;
+ }
+ case kIndex_8_SkColorType:
+ // On an index destination color type, always assume the input is an index.
+ // Fall through
+ case kGray_8_SkColorType:
+ // If the destination is kGray, the caller passes in an 8-bit color.
+ // We will not assert that the high bits of colorOrIndex must be zeroed.
+ // This allows us to take advantage of the fact that the low 8 bits of an
+ // SKPMColor may be a valid a grayscale color. For example, the low 8
+ // bits of SK_ColorBLACK are identical to the grayscale representation
+ // for black.
+
+ // If memory is zero initialized, we may not need to fill
+ if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == (uint8_t) colorOrIndex) {
+ return;
+ }
+
+ memset(dst, (uint8_t) colorOrIndex, bytesToFill);
+ break;
+ default:
+ SkCodecPrintf("Error: Unsupported dst color type for fill(). Doing nothing.\n");
+ SkASSERT(false);
+ break;
+ }
+}
diff --git a/src/codec/SkSampler.h b/src/codec/SkSampler.h
index d7b4c98f23..afabc1f9eb 100644
--- a/src/codec/SkSampler.h
+++ b/src/codec/SkSampler.h
@@ -19,8 +19,43 @@ public:
return this->onSetSampleX(sampleX);
}
+ /**
+ * Fill the remainder of the destination with a single color
+ *
+ * @param info
+ * Contains the color type of the rows to fill.
+ * Contains the width of the destination rows to fill
+ * Contains the number of rows that we need to fill.
+ *
+ * @param dst
+ * The destination row to fill from.
+ *
+ * @param rowBytes
+ * Stride in bytes of the destination.
+ *
+ * @param colorOrIndex
+ * If colorType is kN32, colorOrIndex is treated as a 32-bit color.
+ * If colorType is k565, colorOrIndex is treated as a 16-bit color.
+ * If colorType is kGray, colorOrIndex is treated as an 8-bit color.
+ * If colorType is kIndex, colorOrIndex is treated as an 8-bit index.
+ * Other SkColorTypes are not supported.
+ *
+ * @param zeroInit
+ * Indicates whether memory is already zero initialized.
+ *
+ */
+ static void Fill(const SkImageInfo& info, void* dst, size_t rowBytes,
+ uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit);
+
+ /**
+ * Allow subclasses to implement unique versions of fill().
+ */
+ virtual void fill(const SkImageInfo& info, void* dst, size_t rowBytes,
+ uint32_t colorOrIndex, SkCodec::ZeroInitialized zeroInit) {}
+
virtual ~SkSampler() {}
private:
+
virtual int onSetSampleX(int) = 0;
};
diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp
index fc51613f31..6b50a09f26 100644
--- a/src/codec/SkScaledCodec.cpp
+++ b/src/codec/SkScaledCodec.cpp
@@ -201,7 +201,8 @@ void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcD
SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
size_t rowBytes, const Options& options,
- SkPMColor ctable[], int* ctableCount) {
+ SkPMColor ctable[], int* ctableCount,
+ int* rowsDecoded) {
if (options.fSubset) {
// Subsets are not supported.
@@ -209,6 +210,9 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
}
if (fCodec->dimensionsSupported(requestedInfo.dimensions())) {
+ // Make sure that the parent class does not fill on an incomplete decode, since
+ // fCodec will take care of filling the uninitialized lines.
+ *rowsDecoded = requestedInfo.height();
return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount);
}
@@ -237,7 +241,7 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
return result;
}
- SkSampler* sampler = fCodec->getSampler();
+ SkSampler* sampler = fCodec->getSampler(true);
if (!sampler) {
return kUnimplemented;
}
@@ -248,61 +252,97 @@ SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
switch(fCodec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder: {
- result = fCodec->skipScanlines(Y0);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
+ if (!fCodec->skipScanlines(Y0)) {
+ *rowsDecoded = 0;
+ return kIncompleteInput;
}
for (int y = 0; y < dstHeight; y++) {
- result = fCodec->getScanlines(dst, 1, rowBytes);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
+ if (1 != fCodec->getScanlines(dst, 1, rowBytes)) {
+ // The failed call to getScanlines() will take care of
+ // filling the failed row, so we indicate that we have
+ // decoded (y + 1) rows.
+ *rowsDecoded = y + 1;
+ return kIncompleteInput;
}
if (y < dstHeight - 1) {
- result = fCodec->skipScanlines(sampleY - 1);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
+ if (!fCodec->skipScanlines(sampleY - 1)) {
+ *rowsDecoded = y + 1;
+ return kIncompleteInput;
}
}
dst = SkTAddOffset<void>(dst, rowBytes);
}
- return result;
+ return kSuccess;
}
case SkCodec::kBottomUp_SkScanlineOrder:
case SkCodec::kOutOfOrder_SkScanlineOrder: {
- for (int y = 0; y < srcHeight; y++) {
+ Result result = kSuccess;
+ int y;
+ for (y = 0; y < srcHeight; y++) {
int srcY = fCodec->nextScanline();
if (is_coord_necessary(srcY, sampleY, dstHeight)) {
void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY));
- result = fCodec->getScanlines(dstPtr, 1, rowBytes);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
+ if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) {
+ result = kIncompleteInput;
+ break;
}
} else {
- result = fCodec->skipScanlines(1);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
+ if (!fCodec->skipScanlines(1)) {
+ result = kIncompleteInput;
+ break;
}
}
}
+
+ // We handle filling uninitialized memory here instead of in the parent class.
+ // The parent class does not know that we are sampling.
+ if (kIncompleteInput == result) {
+ const uint32_t fillValue = fCodec->getFillValue(requestedInfo.colorType(),
+ requestedInfo.alphaType());
+ for (; y < srcHeight; y++) {
+ int srcY = fCodec->outputScanline(y);
+ if (is_coord_necessary(srcY, sampleY, dstHeight)) {
+ void* dstRow = SkTAddOffset<void>(dst,
+ rowBytes * get_dst_coord(srcY, sampleY));
+ SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width(), 1), dstRow,
+ rowBytes, fillValue, options.fZeroInitialized);
+ }
+ }
+ *rowsDecoded = dstHeight;
+ }
return result;
}
case SkCodec::kNone_SkScanlineOrder: {
SkAutoMalloc storage(srcHeight * rowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
- result = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
- if (kSuccess != result && kIncompleteInput != result) {
- return result;
- }
+ int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes);
storagePtr += Y0 * rowBytes;
- for (int y = 0; y < dstHeight; y++) {
+ scanlines -= Y0;
+ int y = 0;
+ while (y < dstHeight && scanlines > 0) {
memcpy(dst, storagePtr, rowBytes);
storagePtr += sampleY * rowBytes;
dst = SkTAddOffset<void>(dst, rowBytes);
+ scanlines -= sampleY;
+ y++;
}
- return result;
+ if (y < dstHeight) {
+ // fCodec has already handled filling uninitialized memory.
+ *rowsDecoded = dstHeight;
+ return kIncompleteInput;
+ }
+ return kSuccess;
}
default:
SkASSERT(false);
return kUnimplemented;
}
}
+
+uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const {
+ return fCodec->onGetFillValue(colorType, alphaType);
+}
+
+SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const {
+ return fCodec->onGetScanlineOrder();
+}
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index 214655b85b..8d13e56bd2 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -10,7 +10,6 @@
#include "SkScaledCodec.h"
#include "SkSwizzler.h"
#include "SkTemplates.h"
-#include "SkUtils.h"
SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha,
uint8_t maxAlpha) {
@@ -718,81 +717,3 @@ SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRIC
return fRowProc(dst, src, fDstWidth, fDeltaSrc, fSampleX * fDeltaSrc,
fX0 * fDeltaSrc, fColorTable);
}
-
-void SkSwizzler::Fill(void* dstStartRow, const SkImageInfo& dstInfo, size_t dstRowBytes,
- uint32_t numRows, uint32_t colorOrIndex, const SkPMColor* colorTable,
- SkCodec::ZeroInitialized zeroInit) {
- SkASSERT(dstStartRow != nullptr);
- SkASSERT(numRows <= (uint32_t) dstInfo.height());
-
- // Calculate bytes to fill. We use getSafeSize since the last row may not be padded.
- const size_t bytesToFill = dstInfo.makeWH(dstInfo.width(), numRows).getSafeSize(dstRowBytes);
-
- // Use the proper memset routine to fill the remaining bytes
- switch(dstInfo.colorType()) {
- case kN32_SkColorType:
- // Assume input is an index if we have a color table
- uint32_t color;
- if (nullptr != colorTable) {
- color = colorTable[(uint8_t) colorOrIndex];
- // Otherwise, assume the input is a color
- } else {
- color = colorOrIndex;
- }
-
- // If memory is zero initialized, we may not need to fill
- if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) {
- return;
- }
-
- // We must fill row by row in the case of unaligned row bytes
- if (SkIsAlign4((size_t) dstStartRow) && SkIsAlign4(dstRowBytes)) {
- sk_memset32((uint32_t*) dstStartRow, color,
- (uint32_t) bytesToFill / sizeof(SkPMColor));
- } else {
- // This is an unlikely, slow case
- SkCodecPrintf("Warning: Strange number of row bytes, fill will be slow.\n");
- uint32_t* dstRow = (uint32_t*) dstStartRow;
- for (uint32_t row = 0; row < numRows; row++) {
- for (int32_t col = 0; col < dstInfo.width(); col++) {
- dstRow[col] = color;
- }
- dstRow = SkTAddOffset<uint32_t>(dstRow, dstRowBytes);
- }
- }
- break;
- case kRGB_565_SkColorType:
- // If the destination is k565, the caller passes in a 16-bit color.
- // We will not assert that the high bits of colorOrIndex must be zeroed.
- // This allows us to take advantage of the fact that the low 16 bits of an
- // SKPMColor may be a valid a 565 color. For example, the low 16
- // bits of SK_ColorBLACK are identical to the 565 representation
- // for black.
- // If we ever want to fill with colorOrIndex != 0, we will probably need
- // to implement this with sk_memset16().
- SkASSERT((uint16_t) colorOrIndex == (uint8_t) colorOrIndex);
- // Fall through
- case kIndex_8_SkColorType:
- // On an index destination color type, always assume the input is an index.
- // Fall through
- case kGray_8_SkColorType:
- // If the destination is kGray, the caller passes in an 8-bit color.
- // We will not assert that the high bits of colorOrIndex must be zeroed.
- // This allows us to take advantage of the fact that the low 8 bits of an
- // SKPMColor may be a valid a grayscale color. For example, the low 8
- // bits of SK_ColorBLACK are identical to the grayscale representation
- // for black.
-
- // If memory is zero initialized, we may not need to fill
- if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == (uint8_t) colorOrIndex) {
- return;
- }
-
- memset(dstStartRow, (uint8_t) colorOrIndex, bytesToFill);
- break;
- default:
- SkCodecPrintf("Error: Unsupported dst color type for fill(). Doing nothing.\n");
- SkASSERT(false);
- break;
- }
-}
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
index 9bffccbb47..d7f6337553 100644
--- a/src/codec/SkSwizzler.h
+++ b/src/codec/SkSwizzler.h
@@ -130,44 +130,6 @@ public:
const SkImageInfo& dstInfo, SkCodec::ZeroInitialized);
/**
- * Fill the remainder of the destination with a single color
- *
- * @param dstStartRow
- * The destination row to fill from.
- *
- * @param numRows
- * The number of rows to fill.
- *
- * @param colorOrIndex
- * @param colorTable
- * If dstInfo.colorType() is kIndex8, colorOrIndex is assumed to be a uint8_t
- * index, and colorTable is ignored. Each 8-bit pixel will be set to (uint8_t)
- * index.
- *
- * If dstInfo.colorType() is kN32, colorOrIndex is treated differently depending on
- * whether colorTable is nullptr:
- *
- * A nullptr colorTable means colorOrIndex is treated as an SkPMColor (premul or
- * unpremul, depending on dstInfo.alphaType()). Each 4-byte pixel will be set to
- * colorOrIndex.
-
- * A non-nullptr colorTable means colorOrIndex is treated as a uint8_t index into
- * the colorTable. i.e. each 4-byte pixel will be set to
- * colorTable[(uint8_t) colorOrIndex].
- *
- * If dstInfo.colorType() is kGray, colorOrIndex is always treated as an 8-bit color.
- *
- * Other SkColorTypes are not supported.
- *
- * @param zeroInit
- * Indicates whether memory is already zero initialized.
- *
- */
- static void Fill(void* dstStartRow, const SkImageInfo& dstInfo, size_t dstRowBytes,
- uint32_t numRows, uint32_t colorOrIndex, const SkPMColor* colorTable,
- SkCodec::ZeroInitialized zeroInit);
-
- /**
* Swizzle a line. Generally this will be called height times, once
* for each row of source.
* By allowing the caller to pass in the dst pointer, we give the caller
@@ -181,6 +143,15 @@ public:
*/
ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT src);
+ /**
+ * Implement fill using a custom width.
+ */
+ void fill(const SkImageInfo& info, void* dst, size_t rowBytes, uint32_t colorOrIndex,
+ SkCodec::ZeroInitialized zeroInit) override {
+ const SkImageInfo fillInfo = info.makeWH(fDstWidth, info.height());
+ SkSampler::Fill(fillInfo, dst, rowBytes, colorOrIndex, zeroInit);
+ }
+
private:
/**
@@ -214,5 +185,6 @@ private:
SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, int srcWidth);
int onSetSampleX(int) override;
+
};
#endif // SkSwizzler_DEFINED
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index ccffda9276..a0fab0a153 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -158,7 +158,8 @@ bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
}
SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
- const Options& options, SkPMColor*, int*) {
+ const Options& options, SkPMColor*, int*,
+ int* rowsDecoded) {
if (!webp_conversion_possible(dstInfo, this->getInfo())) {
return kInvalidConversion;
}
@@ -234,10 +235,8 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
while (true) {
const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE);
if (0 == bytesRead) {
- // FIXME: Maybe this is an incomplete image? How to decide? Based
- // on the number of rows decoded? We can know the number of rows
- // decoded using WebPIDecGetRGB.
- return kInvalidInput;
+ WebPIDecGetRGB(idec, rowsDecoded, NULL, NULL, NULL);
+ return kIncompleteInput;
}
switch (WebPIAppend(idec, buffer, bytesRead)) {
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index d60acede7b..97b3920ce9 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -21,7 +21,7 @@ public:
static SkCodec* NewFromStream(SkStream*);
static bool IsWebp(SkStream*);
protected:
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
+ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
override;
SkEncodedFormat onGetEncodedFormat() const override { return kWEBP_SkEncodedFormat; }
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
index 12b42e3ebb..91fb6897b5 100644
--- a/tests/CodexTest.cpp
+++ b/tests/CodexTest.cpp
@@ -8,6 +8,7 @@
#include "Resources.h"
#include "SkBitmap.h"
#include "SkCodec.h"
+#include "SkData.h"
#include "SkMD5.h"
#include "SkRandom.h"
#include "SkScaledCodec.h"
@@ -75,14 +76,15 @@ SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
}
static void test_codec(skiatest::Reporter* r, SkCodec* codec, SkBitmap& bm, const SkImageInfo& info,
- const SkISize& size, bool supports565, SkMD5::Digest* digest,
- const SkMD5::Digest* goodDigest) {
+ const SkISize& size, bool supports565, SkCodec::Result expectedResult,
+ SkMD5::Digest* digest, const SkMD5::Digest* goodDigest) {
+
REPORTER_ASSERT(r, info.dimensions() == size);
bm.allocPixels(info);
SkAutoLockPixels autoLockPixels(bm);
SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, result == expectedResult);
md5(bm, digest);
if (goodDigest) {
@@ -92,16 +94,16 @@ static void test_codec(skiatest::Reporter* r, SkCodec* codec, SkBitmap& bm, cons
{
// Test decoding to 565
SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
- SkCodec::Result expected = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ?
- SkCodec::kSuccess : SkCodec::kInvalidConversion;
- test_info(r, codec, info565, expected, nullptr);
+ SkCodec::Result expected565 = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ?
+ expectedResult : SkCodec::kInvalidConversion;
+ test_info(r, codec, info565, expected565, nullptr);
}
// Verify that re-decoding gives the same result. It is interesting to check this after
// a decode to 565, since choosing to decode to 565 may result in some of the decode
// options being modified. These options should return to their defaults on another
// decode to kN32, so the new digest should match the old digest.
- test_info(r, codec, info, SkCodec::kSuccess, digest);
+ test_info(r, codec, info, expectedResult, digest);
{
// Check alpha type conversions
@@ -121,7 +123,7 @@ static void test_codec(skiatest::Reporter* r, SkCodec* codec, SkBitmap& bm, cons
otherAt = kPremul_SkAlphaType;
}
// The other non-opaque alpha type should always succeed, but not match.
- test_info(r, codec, info.makeAlphaType(otherAt), SkCodec::kSuccess, nullptr);
+ test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
}
}
}
@@ -147,14 +149,24 @@ static void check(skiatest::Reporter* r,
SkISize size,
bool supportsScanlineDecoding,
bool supportsSubsetDecoding,
- bool supports565 = true) {
+ bool supports565 = true,
+ bool supportsIncomplete = true) {
SkAutoTDelete<SkStream> stream(resource(path));
if (!stream) {
SkDebugf("Missing resource '%s'\n", path);
return;
}
- SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
+
+ SkAutoTDelete<SkCodec> codec(nullptr);
+ bool isIncomplete = supportsIncomplete;
+ if (isIncomplete) {
+ size_t size = stream->getLength();
+ SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
+ codec.reset(SkCodec::NewFromData(data));
+ } else {
+ codec.reset(SkCodec::NewFromStream(stream.detach()));
+ }
if (!codec) {
ERRORF(r, "Unable to decode '%s'", path);
return;
@@ -164,14 +176,15 @@ static void check(skiatest::Reporter* r,
SkMD5::Digest codecDigest;
SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
SkBitmap bm;
- test_codec(r, codec, bm, info, size, supports565, &codecDigest, nullptr);
+ SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess;
+ test_codec(r, codec, bm, info, size, supports565, expectedResult, &codecDigest, nullptr);
// Scanline decoding follows.
// Need to call startScanlineDecode() first.
REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
- == SkCodec::kScanlineDecodingNotStarted);
+ == 0);
REPORTER_ASSERT(r, codec->skipScanlines(1)
- == SkCodec::kScanlineDecodingNotStarted);
+ == 0);
const SkCodec::Result startResult = codec->startScanlineDecode(info);
if (supportsScanlineDecoding) {
@@ -180,8 +193,10 @@ static void check(skiatest::Reporter* r,
REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
for (int y = 0; y < info.height(); y++) {
- SkCodec::Result result = codec->getScanlines(bm.getAddr(0, y), 1, 0);
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
+ if (!isIncomplete) {
+ REPORTER_ASSERT(r, 1 == lines);
+ }
}
// verify that scanline decoding gives the same result.
if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
@@ -190,19 +205,21 @@ static void check(skiatest::Reporter* r,
// Cannot continue to decode scanlines beyond the end
REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
- == SkCodec::kInvalidParameters);
+ == 0);
// 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);
+ const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
+ if (!isIncomplete) {
+ REPORTER_ASSERT(r, lines == 1);
+ }
REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
- == SkCodec::kSuccess);
+ == expectedResult);
REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
- == SkCodec::kScanlineDecodingNotStarted);
+ == 0);
REPORTER_ASSERT(r, codec->skipScanlines(1)
- == SkCodec::kScanlineDecodingNotStarted);
+ == 0);
} else {
REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
}
@@ -232,7 +249,7 @@ static void check(skiatest::Reporter* r,
&opts, nullptr, nullptr);
if (supportsSubsetDecoding) {
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, result == expectedResult);
// Webp is the only codec that supports subsets, and it will have modified the subset
// to have even left/top.
REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
@@ -250,7 +267,15 @@ static void check(skiatest::Reporter* r,
SkDebugf("Missing resource '%s'\n", path);
return;
}
- SkAutoTDelete<SkCodec> codec(SkScaledCodec::NewFromStream(stream.detach()));
+
+ SkAutoTDelete<SkCodec> codec(nullptr);
+ if (isIncomplete) {
+ size_t size = stream->getLength();
+ SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
+ codec.reset(SkScaledCodec::NewFromData(data));
+ } else {
+ codec.reset(SkScaledCodec::NewFromStream(stream.detach()));
+ }
if (!codec) {
ERRORF(r, "Unable to decode '%s'", path);
return;
@@ -258,7 +283,13 @@ static void check(skiatest::Reporter* r,
SkBitmap bm;
SkMD5::Digest scaledCodecDigest;
- test_codec(r, codec, bm, info, size, supports565, &scaledCodecDigest, &codecDigest);
+ test_codec(r, codec, bm, info, size, supports565, expectedResult, &scaledCodecDigest,
+ &codecDigest);
+ }
+
+ // If we've just tested incomplete decodes, let's run the same test again on full decodes.
+ if (isIncomplete) {
+ check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, supports565, false);
}
}
@@ -275,39 +306,45 @@ DEF_TEST(Codec, r) {
check(r, "randPixels.bmp", SkISize::Make(8, 8), true, false);
// ICO
+ // FIXME: We are not ready to test incomplete ICOs
// These two tests examine interestingly different behavior:
// Decodes an embedded BMP image
- check(r, "color_wheel.ico", SkISize::Make(128, 128), false, false);
+ check(r, "color_wheel.ico", SkISize::Make(128, 128), false, false, true, false);
// Decodes an embedded PNG image
- check(r, "google_chrome.ico", SkISize::Make(256, 256), false, false);
+ check(r, "google_chrome.ico", SkISize::Make(256, 256), false, false, true, false);
// GIF
- check(r, "box.gif", SkISize::Make(200, 55), true, false);
- check(r, "color_wheel.gif", SkISize::Make(128, 128), true, false);
- check(r, "randPixels.gif", SkISize::Make(8, 8), true, false);
+ // FIXME: We are not ready to test incomplete GIFs
+ check(r, "box.gif", SkISize::Make(200, 55), true, false, true, false);
+ check(r, "color_wheel.gif", SkISize::Make(128, 128), true, false, true, false);
+ // randPixels.gif is too small to test incomplete
+ check(r, "randPixels.gif", SkISize::Make(8, 8), true, false, true, false);
// JPG
check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false, false);
check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false);
- check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false);
+ // grayscale.jpg is too small to test incomplete
+ check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, true, false);
check(r, "mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false);
- check(r, "randPixels.jpg", SkISize::Make(8, 8), true, false);
+ // randPixels.jpg is too small to test incomplete
+ check(r, "randPixels.jpg", SkISize::Make(8, 8), true, false, true, false);
// PNG
- check(r, "arrow.png", SkISize::Make(187, 312), true, false);
- check(r, "baby_tux.png", SkISize::Make(240, 246), true, false);
- check(r, "color_wheel.png", SkISize::Make(128, 128), true, false);
- check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), true, false);
- check(r, "mandrill_128.png", SkISize::Make(128, 128), true, false);
- check(r, "mandrill_16.png", SkISize::Make(16, 16), true, false);
- check(r, "mandrill_256.png", SkISize::Make(256, 256), true, false);
- check(r, "mandrill_32.png", SkISize::Make(32, 32), true, false);
- 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);
+ check(r, "arrow.png", SkISize::Make(187, 312), true, false, true, false);
+ check(r, "baby_tux.png", SkISize::Make(240, 246), true, false, true, false);
+ check(r, "color_wheel.png", SkISize::Make(128, 128), true, false, true, false);
+ check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), true, false, true, false);
+ check(r, "mandrill_128.png", SkISize::Make(128, 128), true, false, true, false);
+ check(r, "mandrill_16.png", SkISize::Make(16, 16), true, false, true, false);
+ check(r, "mandrill_256.png", SkISize::Make(256, 256), true, false, true, false);
+ check(r, "mandrill_32.png", SkISize::Make(32, 32), true, false, true, false);
+ check(r, "mandrill_512.png", SkISize::Make(512, 512), true, false, true, false);
+ check(r, "mandrill_64.png", SkISize::Make(64, 64), true, false, true, false);
+ check(r, "plane.png", SkISize::Make(250, 126), true, false, true, false);
+ // FIXME: We are not ready to test incomplete interlaced pngs
+ check(r, "plane_interlaced.png", SkISize::Make(250, 126), true, false, true, false);
+ check(r, "randPixels.png", SkISize::Make(8, 8), true, false, true, false);
+ check(r, "yellow_rose.png", SkISize::Make(400, 301), true, false, true, false);
}
// Test interlaced PNG in stripes, similar to DM's kStripe_Mode
@@ -361,12 +398,12 @@ DEF_TEST(Codec_stripes, r) {
// Odd stripes
for (int i = 1; i < numStripes; i += 2) {
// Skip the even stripes
- result = codec->skipScanlines(stripeHeight);
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ bool skipResult = codec->skipScanlines(stripeHeight);
+ REPORTER_ASSERT(r, skipResult);
- result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
+ int linesDecoded = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
bm.rowBytes());
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, linesDecoded == stripeHeight);
}
// Even stripes
@@ -374,14 +411,14 @@ DEF_TEST(Codec_stripes, r) {
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
for (int i = 0; i < numStripes; i += 2) {
- result = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
+ int linesDecoded = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight,
bm.rowBytes());
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, linesDecoded == stripeHeight);
// Skip the odd stripes
if (i + 1 < numStripes) {
- result = codec->skipScanlines(stripeHeight);
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ bool skipResult = codec->skipScanlines(stripeHeight);
+ REPORTER_ASSERT(r, skipResult);
}
}
@@ -390,12 +427,12 @@ DEF_TEST(Codec_stripes, r) {
result = codec->startScanlineDecode(info);
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
- result = codec->skipScanlines(height - remainingLines);
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ bool skipResult = codec->skipScanlines(height - remainingLines);
+ REPORTER_ASSERT(r, skipResult);
- result = codec->getScanlines(bm.getAddr(0, height - remainingLines),
+ int linesDecoded = codec->getScanlines(bm.getAddr(0, height - remainingLines),
remainingLines, bm.rowBytes());
- REPORTER_ASSERT(r, result == SkCodec::kSuccess);
+ REPORTER_ASSERT(r, linesDecoded == remainingLines);
}
compare_to_good_digest(r, digest, bm);
diff --git a/tests/SwizzlerTest.cpp b/tests/SwizzlerTest.cpp
index 256a4b2bcd..95aaf38639 100644
--- a/tests/SwizzlerTest.cpp
+++ b/tests/SwizzlerTest.cpp
@@ -9,8 +9,10 @@
#include "Test.h"
// These are the values that we will look for to indicate that the fill was successful
-static const uint8_t kFillIndex = 0x1;
-static const uint32_t kFillColor = 0x22334455;
+static const uint8_t kFillIndex = 0x11;
+static const uint8_t kFillGray = 0x22;
+static const uint16_t kFill565 = 0x3344;
+static const uint32_t kFillColor = 0x55667788;
static void check_fill(skiatest::Reporter* r,
const SkImageInfo& imageInfo,
@@ -18,8 +20,7 @@ static void check_fill(skiatest::Reporter* r,
uint32_t endRow,
size_t rowBytes,
uint32_t offset,
- uint32_t colorOrIndex,
- SkPMColor* colorTable) {
+ uint32_t colorOrIndex) {
// Calculate the total size of the image in bytes. Use the smallest possible size.
// The offset value tells us to adjust the pointer from the memory we allocate in order
@@ -34,16 +35,15 @@ static void check_fill(skiatest::Reporter* r,
// Adjust the pointer in order to test on different memory alignments
uint8_t* imageData = storage.get() + offset;
uint8_t* imageStart = imageData + rowBytes * startRow;
-
- // Fill image with the fill value starting at the indicated row
- SkSwizzler::Fill(imageStart, imageInfo, rowBytes, endRow - startRow + 1, colorOrIndex,
- colorTable, SkCodec::kNo_ZeroInitialized);
+ const SkImageInfo fillInfo = imageInfo.makeWH(imageInfo.width(), endRow - startRow + 1);
+ SkSampler::Fill(fillInfo, imageStart, rowBytes, colorOrIndex, SkCodec::kNo_ZeroInitialized);
// Ensure that the pixels are filled properly
// The bots should catch any memory corruption
uint8_t* indexPtr = imageData + startRow * rowBytes;
uint8_t* grayPtr = indexPtr;
uint32_t* colorPtr = (uint32_t*) indexPtr;
+ uint16_t* color565Ptr = (uint16_t*) indexPtr;
for (uint32_t y = startRow; y <= endRow; y++) {
for (int32_t x = 0; x < imageInfo.width(); x++) {
switch (imageInfo.colorType()) {
@@ -54,8 +54,10 @@ static void check_fill(skiatest::Reporter* r,
REPORTER_ASSERT(r, kFillColor == colorPtr[x]);
break;
case kGray_8_SkColorType:
- // We always fill kGray with black
- REPORTER_ASSERT(r, (uint8_t) kFillColor == grayPtr[x]);
+ REPORTER_ASSERT(r, kFillGray == grayPtr[x]);
+ break;
+ case kRGB_565_SkColorType:
+ REPORTER_ASSERT(r, kFill565 == color565Ptr[x]);
break;
default:
REPORTER_ASSERT(r, false);
@@ -69,12 +71,6 @@ static void check_fill(skiatest::Reporter* r,
// Test Fill() with different combinations of dimensions, alignment, and padding
DEF_TEST(SwizzlerFill, r) {
- // Set up a color table
- SkPMColor colorTable[kFillIndex + 1];
- colorTable[kFillIndex] = kFillColor;
- // Apart from the fill index, we will leave the other colors in the color table uninitialized.
- // If we incorrectly try to fill with this uninitialized memory, the bots will catch it.
-
// Test on an invalid width and representative widths
const uint32_t widths[] = { 0, 10, 50 };
@@ -83,48 +79,44 @@ DEF_TEST(SwizzlerFill, r) {
const uint32_t heights[] = { 1, 5, 10 };
// Test on interesting possibilities for row padding
- const uint32_t paddings[] = { 0, 1, 2, 3, 4 };
+ const uint32_t paddings[] = { 0, 4 };
// Iterate over test dimensions
for (uint32_t width : widths) {
for (uint32_t height : heights) {
// Create image info objects
- const SkImageInfo colorInfo = SkImageInfo::MakeN32(width, height,
- kUnknown_SkAlphaType);
- const SkImageInfo indexInfo = colorInfo.makeColorType(kIndex_8_SkColorType);
+ const SkImageInfo colorInfo = SkImageInfo::MakeN32(width, height, kUnknown_SkAlphaType);
const SkImageInfo grayInfo = colorInfo.makeColorType(kGray_8_SkColorType);
+ const SkImageInfo indexInfo = colorInfo.makeColorType(kIndex_8_SkColorType);
+ const SkImageInfo color565Info = colorInfo.makeColorType(kRGB_565_SkColorType);
for (uint32_t padding : paddings) {
// Calculate row bytes
- size_t colorRowBytes = SkColorTypeBytesPerPixel(kN32_SkColorType) * width +
- padding;
- size_t indexRowBytes = width + padding;
- size_t grayRowBytes = indexRowBytes;
+ const size_t colorRowBytes = SkColorTypeBytesPerPixel(kN32_SkColorType) * width
+ + padding;
+ const size_t indexRowBytes = width + padding;
+ const size_t grayRowBytes = indexRowBytes;
+ const size_t color565RowBytes =
+ SkColorTypeBytesPerPixel(kRGB_565_SkColorType) * width + padding;
// If there is padding, we can invent an offset to change the memory alignment
- for (uint32_t offset = 0; offset <= padding; offset++) {
+ for (uint32_t offset = 0; offset <= padding; offset += 4) {
// Test all possible start rows with all possible end rows
for (uint32_t startRow = 0; startRow < height; startRow++) {
for (uint32_t endRow = startRow; endRow < height; endRow++) {
- // Fill with an index that we use to look up a color
+ // Test fill with each color type
check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset,
- kFillIndex, colorTable);
-
- // Fill with a color
- check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset,
- kFillColor, nullptr);
-
- // Fill with an index
+ kFillColor);
check_fill(r, indexInfo, startRow, endRow, indexRowBytes, offset,
- kFillIndex, nullptr);
-
- // Fill a grayscale image
+ kFillIndex);
check_fill(r, grayInfo, startRow, endRow, grayRowBytes, offset,
- kFillColor, nullptr);
+ kFillGray);
+ check_fill(r, color565Info, startRow, endRow, color565RowBytes, offset,
+ kFill565);
}
}
}
diff --git a/tools/SkBitmapRegionCanvas.cpp b/tools/SkBitmapRegionCanvas.cpp
index c54d936edc..086ac19864 100644
--- a/tools/SkBitmapRegionCanvas.cpp
+++ b/tools/SkBitmapRegionCanvas.cpp
@@ -128,22 +128,13 @@ SkBitmap* SkBitmapRegionCanvas::decodeRegion(int inputX, int inputY,
}
// Skip the unneeded rows
- if (SkCodec::kSuccess != fDecoder->skipScanlines(imageSubsetY)) {
+ if (!fDecoder->skipScanlines(imageSubsetY)) {
SkDebugf("Error: Failed to skip scanlines.\n");
return nullptr;
}
// Decode the necessary rows
- SkCodec::Result result = fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubsetHeight,
- tmp.rowBytes());
- switch (result) {
- case SkCodec::kSuccess:
- case SkCodec::kIncompleteInput:
- break;
- default:
- SkDebugf("Error: Failed to get scanlines.\n");
- return nullptr;
- }
+ fDecoder->getScanlines(tmp.getAddr(0, 0), imageSubsetHeight, tmp.rowBytes());
// Calculate the size of the output
const int outWidth = get_scaled_dimension(inputWidth, sampleSize);
diff --git a/tools/SkBitmapRegionDecoderInterface.cpp b/tools/SkBitmapRegionDecoderInterface.cpp
index 5c769d676e..47de31f4ec 100644
--- a/tools/SkBitmapRegionDecoderInterface.cpp
+++ b/tools/SkBitmapRegionDecoderInterface.cpp
@@ -29,12 +29,12 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
return new SkBitmapRegionSampler(decoder, width, height);
}
case kCanvas_Strategy: {
- SkCodec* decoder = SkCodec::NewFromStream(stream);
- if (nullptr == decoder) {
+ SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream));
+ if (nullptr == codec) {
SkDebugf("Error: Failed to create decoder.\n");
return nullptr;
}
- switch (decoder->getScanlineOrder()) {
+ switch (codec->getScanlineOrder()) {
case SkCodec::kTopDown_SkScanlineOrder:
case SkCodec::kNone_SkScanlineOrder:
break;
@@ -42,7 +42,7 @@ SkBitmapRegionDecoderInterface* SkBitmapRegionDecoderInterface::CreateBitmapRegi
SkDebugf("Error: Scanline ordering not supported.\n");
return nullptr;
}
- return new SkBitmapRegionCanvas(decoder);
+ return new SkBitmapRegionCanvas(codec.detach());
}
default:
SkASSERT(false);
diff --git a/tools/dm_flags.json b/tools/dm_flags.json
index 5dc0b5295e..11baa6d469 100644
--- a/tools/dm_flags.json
+++ b/tools/dm_flags.json
@@ -125,6 +125,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"gpu",
"skp",
"_",
@@ -275,6 +351,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"--match",
"~WritePixels",
"~tabl_mozilla_0",
@@ -398,6 +550,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"--match",
"~tabl_mozilla_0",
"~desk_yahoonews_0",
@@ -523,6 +751,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"--match",
"~tabl_mozilla_0",
"~desk_yahoonews_0"
@@ -645,6 +949,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"--match",
"~tabl_mozilla_0",
"~desk_yahoonews_0"
@@ -765,6 +1145,82 @@
"Hopstarter-Mac-Folders-Apple.ico",
"_",
"image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
+ "_",
+ "image",
"_",
"interlaced1.png",
"_",
@@ -910,6 +1366,82 @@
"Hopstarter-Mac-Folders-Apple.ico",
"_",
"image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
+ "_",
+ "image",
"_",
"interlaced1.png",
"_",
@@ -1051,6 +1583,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"pdf",
"_",
"_",
@@ -1217,6 +1825,82 @@
"image",
"decode",
"Hopstarter-Mac-Folders-Apple.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
"pdf",
"_",
"_",
@@ -1386,6 +2070,82 @@
"_",
"image",
"decode",
+ "inc0.gif",
+ "_",
+ "image",
+ "decode",
+ "inc1.gif",
+ "_",
+ "image",
+ "decode",
+ "incInterlaced.gif",
+ "_",
+ "image",
+ "decode",
+ "inc0.jpg",
+ "_",
+ "image",
+ "decode",
+ "incGray.jpg",
+ "_",
+ "image",
+ "decode",
+ "inc0.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc1.wbmp",
+ "_",
+ "image",
+ "decode",
+ "inc0.webp",
+ "_",
+ "image",
+ "decode",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
+ "inc0.ico",
+ "_",
+ "image",
+ "decode",
+ "inc1.ico",
+ "_",
+ "image",
+ "decode",
+ "inc0.png",
+ "_",
+ "image",
+ "decode",
+ "inc1.png",
+ "_",
+ "image",
+ "decode",
+ "inc2.png",
+ "_",
+ "image",
+ "decode",
+ "inc12.png",
+ "_",
+ "image",
+ "decode",
+ "inc13.png",
+ "_",
+ "image",
+ "decode",
+ "inc14.png",
+ "_",
+ "image",
+ "subset",
+ "inc0.webp",
+ "_",
+ "image",
+ "subset",
+ "inc1.webp",
+ "_",
+ "image",
+ "decode",
"_",
"_",
"image",
diff --git a/tools/dm_flags.py b/tools/dm_flags.py
index 64b4cec93e..e8f39182fd 100755
--- a/tools/dm_flags.py
+++ b/tools/dm_flags.py
@@ -112,6 +112,27 @@ def get_args(bot):
# New ico files that fail on SkImageDecoder
blacklist.extend('_ image decode Hopstarter-Mac-Folders-Apple.ico'.split(' '))
+ # Incomplete image tests that fail on SkImageDecoder
+ blacklist.extend('_ image decode inc0.gif'.split(' '))
+ blacklist.extend('_ image decode inc1.gif'.split(' '))
+ blacklist.extend('_ image decode incInterlaced.gif'.split(' '))
+ blacklist.extend('_ image decode inc0.jpg'.split(' '))
+ blacklist.extend('_ image decode incGray.jpg'.split(' '))
+ blacklist.extend('_ image decode inc0.wbmp'.split(' '))
+ blacklist.extend('_ image decode inc1.wbmp'.split(' '))
+ blacklist.extend('_ image decode inc0.webp'.split(' '))
+ blacklist.extend('_ image decode inc1.webp'.split(' '))
+ blacklist.extend('_ image decode inc0.ico'.split(' '))
+ blacklist.extend('_ image decode inc1.ico'.split(' '))
+ blacklist.extend('_ image decode inc0.png'.split(' '))
+ blacklist.extend('_ image decode inc1.png'.split(' '))
+ blacklist.extend('_ image decode inc2.png'.split(' '))
+ blacklist.extend('_ image decode inc12.png'.split(' '))
+ blacklist.extend('_ image decode inc13.png'.split(' '))
+ blacklist.extend('_ image decode inc14.png'.split(' '))
+ blacklist.extend('_ image subset inc0.webp'.split(' '))
+ blacklist.extend('_ image subset inc1.webp'.split(' '))
+
# Leon doesn't care about this, so why run it?
if 'Win' in bot:
blacklist.extend('_ image decode _'.split(' '))