From 4946b9419f650ef7a2dc684ce98bb771e1ce8f40 Mon Sep 17 00:00:00 2001 From: msarett Date: Thu, 11 Feb 2016 08:41:01 -0800 Subject: Fix scanline decoding of rare RLE bmps This also exposed a bug in rewinding RLE bmps, which I have also fixed in this CL. This should fix testcase7.bmp on Gold. The image that I am adding to resources is in the public domain. BUG=skia:4730 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1689953002 Review URL: https://codereview.chromium.org/1689953002 --- resources/rle.bmp | Bin 0 -> 40400 bytes src/codec/SkBmpRLECodec.cpp | 31 ++++++++++++++++++++++++------- src/codec/SkBmpRLECodec.h | 7 +++++++ tests/CodexTest.cpp | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 resources/rle.bmp diff --git a/resources/rle.bmp b/resources/rle.bmp new file mode 100644 index 0000000000..a08745fb2d Binary files /dev/null and b/resources/rle.bmp differ diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp index b01012644b..57707d60c9 100644 --- a/src/codec/SkBmpRLECodec.cpp +++ b/src/codec/SkBmpRLECodec.cpp @@ -26,6 +26,7 @@ SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream, , fOffset(offset) , fStreamBuffer(new uint8_t[RLEBytes]) , fRLEBytes(RLEBytes) + , fOrigRLEBytes(RLEBytes) , fCurrRLEByte(0) , fSampleX(1) {} @@ -270,6 +271,8 @@ SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo, // Reset fSampleX. If it needs to be a value other than 1, it will get modified by // the sampler. fSampleX = 1; + fLinesToSkip = 0; + // Create the color table if necessary and prepare the stream for decode // Note that if it is non-NULL, inputColorCount will be modified if (!this->createColorTable(inputColorCount)) { @@ -281,6 +284,7 @@ SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo, copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); // Initialize a buffer for encoded RLE data + fRLEBytes = fOrigRLEBytes; if (!this->initializeStreamBuffer()) { SkCodecPrintf("Error: cannot initialize stream buffer.\n"); return SkCodec::kInvalidConversion; @@ -301,17 +305,12 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB static const uint8_t RLE_EOF = 1; static const uint8_t RLE_DELTA = 2; - // Set constant values const int width = this->getInfo().width(); - const int height = info.height(); + int height = info.height(); // Account for sampling. SkImageInfo dstInfo = info.makeWH(get_scaled_dimension(width, fSampleX), height); - // Destination parameters - int x = 0; - int y = 0; - // Set the background as transparent. Then, if the RLE code skips pixels, // the skipped pixels will be transparent. // Because of the need for transparent pixels, kN32 is the only color @@ -319,6 +318,21 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB SkASSERT(kN32_SkColorType == dstInfo.colorType()); SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized); + // Adjust the height and the dst if the previous call to decodeRows() left us + // with lines that need to be skipped. + if (height > fLinesToSkip) { + height -= fLinesToSkip; + dst = SkTAddOffset(dst, fLinesToSkip * dstRowBytes); + fLinesToSkip = 0; + } else { + fLinesToSkip -= height; + return height; + } + + // Destination parameters + int x = 0; + int y = 0; + while (true) { // If we have reached a row that is beyond the requested height, we have // succeeded. @@ -366,9 +380,12 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++]; x += dx; y += dy; - if (x > width || y > height) { + if (x > width) { SkCodecPrintf("Warning: invalid RLE input.\n"); return y - dy; + } else if (y > height) { + fLinesToSkip = y - height; + return height; } break; } diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h index df2a97d845..e319a71052 100644 --- a/src/codec/SkBmpRLECodec.h +++ b/src/codec/SkBmpRLECodec.h @@ -96,9 +96,16 @@ private: const uint32_t fOffset; SkAutoTDeleteArray fStreamBuffer; size_t fRLEBytes; + const size_t fOrigRLEBytes; uint32_t fCurrRLEByte; int fSampleX; SkAutoTDelete fSampler; + // Scanline decodes allow the client to ask for a single scanline at a time. + // This can be tricky when the RLE encoding instructs the decoder to jump down + // multiple lines. This field keeps track of lines that need to be skipped + // on subsequent calls to decodeRows(). + int fLinesToSkip; + typedef SkBmpCodec INHERITED; }; diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp index 1261d2b556..1aa3cdc4dd 100644 --- a/tests/CodexTest.cpp +++ b/tests/CodexTest.cpp @@ -344,6 +344,7 @@ DEF_TEST(Codec, r) { // BMP check(r, "randPixels.bmp", SkISize::Make(8, 8), true, false); + check(r, "rle.bmp", SkISize::Make(320, 240), true, false); // ICO // FIXME: We are not ready to test incomplete ICOs -- cgit v1.2.3