Fix scanline decoding of rare RLE bmps
authormsarett <msarett@google.com>
Thu, 11 Feb 2016 16:41:01 +0000 (08:41 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 11 Feb 2016 16:41:01 +0000 (08:41 -0800)
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 [new file with mode: 0644]
src/codec/SkBmpRLECodec.cpp
src/codec/SkBmpRLECodec.h
tests/CodexTest.cpp

diff --git a/resources/rle.bmp b/resources/rle.bmp
new file mode 100644 (file)
index 0000000..a08745f
Binary files /dev/null and b/resources/rle.bmp differ
index b010126..57707d6 100644 (file)
@@ -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<void>(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;
                 }
index df2a97d..e319a71 100644 (file)
@@ -96,9 +96,16 @@ private:
     const uint32_t                      fOffset;
     SkAutoTDeleteArray<uint8_t>         fStreamBuffer;
     size_t                              fRLEBytes;
+    const size_t                        fOrigRLEBytes;
     uint32_t                            fCurrRLEByte;
     int                                 fSampleX;
     SkAutoTDelete<SkSampler>            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;
 };
index 1261d2b..1aa3cdc 100644 (file)
@@ -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