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 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 @@ -131,10 +131,6 @@ public: * supported for this input. */ 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. @@ -426,6 +448,36 @@ protected: return true; } + /** + * 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 */ @@ -442,11 +494,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. */ @@ -456,6 +503,8 @@ protected: const SkCodec::Options& options() const { return fOptions; } + virtual int onOutputScanline(int inputScanline) const; + private: const SkImageInfo fSrcInfo; SkAutoTDelete 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 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 @@ -549,18 +549,6 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const { return height - y - 1; } -/* - * 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(dst, y * dstRowBytes) : dst; -} - /* * Compute the number of colors in the color table */ @@ -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 @@ -80,15 +80,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 */ @@ -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 fMasks; // owned SkAutoTDelete 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 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(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(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; @@ -181,28 +201,11 @@ 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(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(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(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(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(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(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(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(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(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(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 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(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(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 fColorTable; SkAutoTDelete fSwizzler; + SkAutoTUnref fColorTable; SkAutoTMalloc 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(fStorage.get()) == fSrcRow); +SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) { + if (!createIfNecessary || fSwizzler) { + SkASSERT(!fSwizzler || (fSrcRow && static_cast(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(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 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(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(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(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(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(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(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(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(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 @@ -129,44 +129,6 @@ public: static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable, 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. @@ -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 stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } - SkAutoTDelete codec(SkCodec::NewFromStream(stream.detach())); + + SkAutoTDelete codec(nullptr); + bool isIncomplete = supportsIncomplete; + if (isIncomplete) { + size_t size = stream->getLength(); + SkAutoTUnref 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 codec(SkScaledCodec::NewFromStream(stream.detach())); + + SkAutoTDelete codec(nullptr); + if (isIncomplete) { + size_t size = stream->getLength(); + SkAutoTUnref 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 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(' '))