Make image decoding more fault resistant, less verbose.
authorhalcanary@google.com <halcanary@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 4 Oct 2013 12:46:45 +0000 (12:46 +0000)
committerhalcanary@google.com <halcanary@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 4 Oct 2013 12:46:45 +0000 (12:46 +0000)
This change address what happens when a jpeg is partially downloaded
before failing.  Many browsers will render it anyway: we want Skia to
do the same.  The JpegTest takes a perfectly cromulent jpeg file and
only passes into the ImageDecoder the first half of the image.  We
then verify that the image decoder returns a valid bitmap of the
correct dimensions.

We also fixed some png library errors, including issue 1691.

Also, suppressed the majority of warnings from using libpng and
libjpeg.  By default, most warnings are *not* suppressed in debug mode.
If you have a debug binary and wish to suppress warnings, set the
following environment variables to true
    skia_images_png_suppressDecoderWarnings
    skia_images_jpeg_suppressDecoderWarnings
or from within a program that links to Skia:
    #if defined(SK_DEBUG)
    #include "SkRTConf.h"
    SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
    SK_CONF_SET("images.png.suppressDecoderWarnings", true);
    #endif

I tested this, before (control) and after these changes (test), on
364,295 skps from the cluster telemetry.
-   number of errors+warnings in control = 2804
-   number of errors+warnings fixed = 2283
-   number of PNG verbosity fixed =  2152
-   number of PNG error fixed = 4
-   number of PNG segfault fixed = 3
-   number of PNG errors changed to warnings = 62
-   number of JPG verbosity fixed =  26
-   number of JPG error fixed = 91
Not all errors and warning have been fixed.

These numbers were generated using the find_bad_images_in_skps.py
program.  This program may be useful going forward for testing
image-decoding libraries on skp files from the cluster telemetry.
find_bad_images_in_skps.py depends on the test_image_decoder program,
which simply executes the SkImageDecoder::DecodeFile function and uses
its exit status to report success or failure.

BUG=skia:1649
BUG=skia:1691
BUG=skia:1680
R=scroggo@google.com

Review URL: https://codereview.chromium.org/24449003

git-svn-id: http://skia.googlecode.com/svn/trunk@11597 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/tests.gyp
gyp/tools.gyp
src/images/SkImageDecoder_libjpeg.cpp
src/images/SkImageDecoder_libpng.cpp
tests/JpegTest.cpp [new file with mode: 0644]
tools/find_bad_images_in_skps.py [new file with mode: 0755]
tools/test_image_decoder.cpp [new file with mode: 0644]

index e08a175..62cd2b0 100644 (file)
@@ -77,6 +77,7 @@
         '../tests/ImageDecodingTest.cpp',
         '../tests/ImageFilterTest.cpp',
         '../tests/InfRectTest.cpp',
+        '../tests/JpegTest.cpp',
         '../tests/LListTest.cpp',
         '../tests/LayerDrawLooperTest.cpp',
         '../tests/MD5Test.cpp',
index 3eae3cc..afd4934 100644 (file)
@@ -25,6 +25,7 @@
         'skpdiff',
         'skhello',
         'skimage',
+        'test_image_decoder',
       ],
       'conditions': [
         ['skia_shared_lib',
         'tools.gyp:picture_utils',
       ],
     },
+    {
+      'target_name': 'test_image_decoder',
+      'type': 'executable',
+      'sources': [
+        '../tools/test_image_decoder.cpp',
+      ],
+      'dependencies': [
+        'skia_lib.gyp:skia_lib',
+      ],
+    },
   ],
   'conditions': [
     ['skia_shared_lib',
index 3b3ea88..7652249 100644 (file)
 #include "SkRect.h"
 #include "SkCanvas.h"
 
+#if defined(SK_DEBUG)
+#include "SkRTConf.h"  // SK_CONF_DECLARE
+#endif  // defined(SK_DEBUG)
+
 #include <stdio.h>
 extern "C" {
     #include "jpeglib.h"
@@ -35,6 +39,12 @@ extern "C" {
 // If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers
 // support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565.
 
+#if defined(SK_DEBUG)
+SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings,
+    "images.jpeg.suppressDecoderWarnings", false,
+    "Suppress most JPG warnings when calling decode functions.");
+#endif  // defined(SK_DEBUG)
+
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
@@ -54,12 +64,27 @@ static void overwrite_mem_buffer_size(jpeg_decompress_struct* cinfo) {
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
+static void do_nothing_emit_message(jpeg_common_struct*, int) {
+    /* do nothing */
+}
+
 static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) {
     SkASSERT(cinfo != NULL);
     SkASSERT(src_mgr != NULL);
     jpeg_create_decompress(cinfo);
     overwrite_mem_buffer_size(cinfo);
     cinfo->src = src_mgr;
+#if defined(SK_DEBUG)
+    /* To suppress warnings with a SK_DEBUG binary, set the
+     * environment variable "skia_images_jpeg_suppressDecoderWarnings"
+     * to "true".  Inside a program that links to skia:
+     * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */
+    if (c_suppressJPEGImageDecoderWarnings) {
+        cinfo->err->emit_message = &do_nothing_emit_message;
+    }
+#else  // Always suppress in release mode.
+    cinfo->err->emit_message = &do_nothing_emit_message;
+#endif  // defined(SK_DEBUG)
 }
 
 #ifdef SK_BUILD_FOR_ANDROID
@@ -443,6 +468,19 @@ static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo,
 }
 #endif
 
+
+/**
+   Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y.
+   Used when decoding fails partway through reading scanlines to fill
+   remaining lines. */
+static void fill_below_level(int y, SkBitmap* bitmap) {
+    SkRect rect = SkRect::MakeLTRB(0, y, bitmap->width(), bitmap->height());
+    SkCanvas canvas(*bitmap);
+    canvas.clipRect(rect);
+    canvas.drawColor(SK_ColorWHITE);
+}
+
+
 bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 #ifdef TIME_DECODE
     SkAutoTime atm("JPEG Decode");
@@ -553,10 +591,12 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
 
         while (cinfo.output_scanline < cinfo.output_height) {
             int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
-            // if row_count == 0, then we didn't get a scanline, so abort.
-            // if we supported partial images, we might return true in this case
             if (0 == row_count) {
-                return return_false(cinfo, *bm, "read_scanlines");
+                // if row_count == 0, then we didn't get a scanline,
+                // so return early.  We will return a partial image.
+                fill_below_level(cinfo.output_scanline, bm);
+                cinfo.output_scanline = cinfo.output_height;
+                break;  // Skip to jpeg_finish_decompress()
             }
             if (this->shouldCancelDecode()) {
                 return return_false(cinfo, *bm, "shouldCancelDecode");
@@ -606,7 +646,11 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
         JSAMPLE* rowptr = (JSAMPLE*)srcRow;
         int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);
         if (0 == row_count) {
-            return return_false(cinfo, *bm, "read_scanlines");
+            // if row_count == 0, then we didn't get a scanline,
+            // so return early.  We will return a partial image.
+            fill_below_level(y, bm);
+            cinfo.output_scanline = cinfo.output_height;
+            break;  // Skip to jpeg_finish_decompress()
         }
         if (this->shouldCancelDecode()) {
             return return_false(cinfo, *bm, "shouldCancelDecode");
@@ -785,8 +829,9 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
             int rowCount = jpeg_read_tile_scanline(cinfo,
                                                    fImageIndex->huffmanIndex(),
                                                    &rowptr);
-            // if row_count == 0, then we didn't get a scanline, so abort.
-            // if we supported partial images, we might return true in this case
+            // if rowCount == 0, then we didn't get a scanline, so abort.
+            // onDecodeSubset() relies on onBuildTileIndex(), which
+            // needs a complete image to succeed.
             if (0 == rowCount) {
                 return return_false(*cinfo, bitmap, "read_scanlines");
             }
@@ -844,6 +889,9 @@ bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
     for (int y = 0;; y++) {
         JSAMPLE* rowptr = (JSAMPLE*)srcRow;
         int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
+        // if row_count == 0, then we didn't get a scanline, so abort.
+        // onDecodeSubset() relies on onBuildTileIndex(), which
+        // needs a complete image to succeed.
         if (0 == row_count) {
             return return_false(*cinfo, bitmap, "read_scanlines");
         }
index d3b45c3..4e4106a 100644 (file)
 #include "SkUtils.h"
 #include "transform_scanline.h"
 
+#if defined(SK_DEBUG)
+#include "SkRTConf.h"  // SK_CONF_DECLARE
+#endif  // defined(SK_DEBUG)
+
 extern "C" {
 #include "png.h"
 }
@@ -40,6 +44,13 @@ extern "C" {
 #define png_flush_ptr_NULL NULL
 #endif
 
+#if defined(SK_DEBUG)
+SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings,
+    "images.png.suppressDecoderWarnings", false,
+    "Suppress most PNG warnings when calling image decode functions.");
+#endif  // defined(SK_DEBUG)
+
+
 class SkPNGImageIndex {
 public:
     SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr)
@@ -199,6 +210,10 @@ static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) {
     return false;
 }
 
+void do_nothing_warning_fn(png_structp, png_const_charp) {
+    /* do nothing */
+}
+
 bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
                                      png_infop *info_ptrp) {
     /* Create and initialize the png_struct with the desired error handler
@@ -206,12 +221,27 @@ bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
     * you can supply NULL for the last three parameters.  We also supply the
     * the compiler header file version, so that we know if the application
     * was compiled with a compatible version of the library.  */
+
+#if defined(SK_DEBUG)
+    png_error_ptr user_warning_fn =
+        (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;
+    /* NULL means to leave as default library behavior. */
+    /* c_suppressPNGImageDecoderWarnings defaults to false. */
+    /* To suppress warnings with a SK_DEBUG binary, set the
+     * environment variable "skia_images_png_suppressDecoderWarnings"
+     * to "true".  Inside a program that links to skia:
+     * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */
+#else  // Always suppress in release mode
+    png_error_ptr user_warning_fn = &do_nothing_warning_fn;
+#endif  // defined(SK_DEBUG)
+
     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
-        NULL, sk_error_fn, NULL);
+        NULL, sk_error_fn, user_warning_fn);
     //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);
     if (png_ptr == NULL) {
         return false;
     }
+
     *png_ptrp = png_ptr;
 
     /* Allocate/initialize the memory for image information. */
@@ -498,9 +528,13 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
                                                     transpColor->green >> 8,
                                                     transpColor->blue >> 8);
                 } else {
-                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->red,
-                                                    transpColor->green,
-                                                    transpColor->blue);
+                    /* We apply the mask because in a very small
+                       number of corrupt PNGs, (transpColor->red > 255)
+                       and (bitDepth == 8), for certain versions of libpng. */
+                    *theTranspColorp = SkPackARGB32(0xFF,
+                                                    0xFF & (transpColor->red),
+                                                    0xFF & (transpColor->green),
+                                                    0xFF & (transpColor->blue));
                 }
             } else {    // gray
                 if (16 == bitDepth) {
@@ -508,9 +542,15 @@ bool SkPNGImageDecoder::getBitmapConfig(png_structp png_ptr, png_infop info_ptr,
                                                     transpColor->gray >> 8,
                                                     transpColor->gray >> 8);
                 } else {
-                    *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray,
-                                                    transpColor->gray,
-                                                    transpColor->gray);
+                    /* We apply the mask because in a very small
+                       number of corrupt PNGs, (transpColor->red >
+                       255) and (bitDepth == 8), for certain versions
+                       of libpng.  For safety we assume the same could
+                       happen with a grayscale PNG.  */
+                    *theTranspColorp = SkPackARGB32(0xFF,
+                                                    0xFF & (transpColor->gray),
+                                                    0xFF & (transpColor->gray),
+                                                    0xFF & (transpColor->gray));
                 }
             }
         }
diff --git a/tests/JpegTest.cpp b/tests/JpegTest.cpp
new file mode 100644 (file)
index 0000000..b0c76fc
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkForceLinking.h"
+#include "SkImageDecoder.h"
+#include "SkImage.h"
+#include "SkStream.h"
+#include "Test.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+#define JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING 0  // do not do this for
+                                                 // normal unit testing.
+
+namespace {
+    char goodJpegImage[] = {
+        0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46,
+        0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x8F,
+        0x00, 0x8F, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43,
+        0x00, 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05,
+        0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07,
+        0x0C, 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B,
+        0x0B, 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11,
+        0x0F, 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13,
+        0x14, 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18,
+        0x1A, 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17,
+        0x22, 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F,
+        0x1E, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x05, 0x05,
+        0x05, 0x07, 0x06, 0x07, 0x0E, 0x08, 0x08, 0x0E,
+        0x1E, 0x14, 0x11, 0x14, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0xFF, 0xC0,
+        0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, 0x03,
+        0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11,
+        0x01, 0xFF, 0xC4, 0x00, 0x18, 0x00, 0x01, 0x01,
+        0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
+        0x08, 0x06, 0x05, 0xFF, 0xC4, 0x00, 0x4C, 0x10,
+        0x00, 0x00, 0x01, 0x07, 0x08, 0x05, 0x08, 0x05,
+        0x0A, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x11,
+        0x05, 0x08, 0x12, 0x13, 0x14, 0x15, 0x38, 0xB4,
+        0x16, 0x17, 0x21, 0x31, 0x84, 0x18, 0x22, 0x23,
+        0x24, 0x58, 0xA5, 0xA6, 0xD2, 0x32, 0x51, 0x56,
+        0x61, 0xD3, 0x28, 0x33, 0x41, 0x48, 0x67, 0x85,
+        0x86, 0xC3, 0xE4, 0xF0, 0x25, 0x49, 0x55, 0x09,
+        0x34, 0x35, 0x36, 0x53, 0x68, 0x72, 0x81, 0xA7,
+        0xE2, 0xFF, 0xC4, 0x00, 0x14, 0x01, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+        0xC4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00,
+        0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+        0x00, 0x3F, 0x00, 0xD9, 0x62, 0x10, 0x80, 0x40,
+        0x65, 0xED, 0x62, 0x75, 0xC8, 0x7D, 0xFF, 0x00,
+        0x92, 0x30, 0x33, 0x01, 0x97, 0xB5, 0x89, 0xD7,
+        0x21, 0xF7, 0xFE, 0x48, 0xC0, 0x0C, 0xC2, 0x10,
+        0x80, 0x40, 0x66, 0x64, 0xB8, 0x62, 0x64, 0x78,
+        0xDC, 0xEA, 0x70, 0xCC, 0x06, 0x66, 0x4B, 0x86,
+        0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x00, 0xCC, 0x21,
+        0x08, 0x04, 0x31, 0x9F, 0xF2, 0xC5, 0xFD, 0xFF,
+        0x00, 0x5A, 0x1B, 0x30, 0x63, 0x3F, 0xE5, 0x8B,
+        0xFB, 0xFE, 0xB4, 0x03, 0x66, 0x01, 0x99, 0x92,
+        0xE1, 0x89, 0x91, 0xE3, 0x73, 0xA9, 0xC3, 0x30,
+        0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A,
+        0x9C, 0x03, 0x30, 0x84, 0x33, 0x33, 0x92, 0x55,
+        0x7E, 0xCF, 0x29, 0xD8, 0x49, 0x0D, 0xAE, 0xBD,
+        0xAE, 0xAB, 0xC6, 0xBB, 0xAA, 0x68, 0x92, 0x92,
+        0x6A, 0xBA, 0xB4, 0xE9, 0x11, 0x7A, 0x7C, 0xD8,
+        0xC6, 0x84, 0x77, 0x12, 0x11, 0x87, 0xBC, 0x07,
+        0x67, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA,
+        0x5E, 0x51, 0x6B, 0x11, 0xFB, 0x76, 0x74, 0xF1,
+        0xAA, 0x97, 0x94, 0x33, 0x08, 0x00, 0xCE, 0xB1,
+        0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79, 0x41,
+        0x9B, 0xC4, 0x6C, 0xDE, 0xC2, 0xCB, 0xF6, 0x75,
+        0x92, 0x84, 0xA0, 0xE5, 0xEC, 0x12, 0xB2, 0x9D,
+        0xEF, 0x76, 0xC9, 0xBA, 0x50, 0xAA, 0x92, 0xF1,
+        0xA6, 0xAA, 0x69, 0x12, 0xF4, 0xA4, 0x36, 0x8A,
+        0x2A, 0xB3, 0x60, 0x77, 0x3A, 0x34, 0xA3, 0x02,
+        0x6D, 0x1A, 0xC8, 0x0C, 0xBD, 0xAC, 0x4E, 0xB9,
+        0x0F, 0xBF, 0xF2, 0x46, 0x00, 0xB5, 0x88, 0xFD,
+        0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D, 0x62,
+        0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x86,
+        0x61, 0x00, 0x19, 0xD6, 0x23, 0xF6, 0xEC, 0xE9,
+        0xE3, 0x55, 0x2F, 0x28, 0x33, 0x9A, 0xE3, 0x66,
+        0xF6, 0x24, 0x97, 0x12, 0xCE, 0xC9, 0xEC, 0xCB,
+        0x97, 0xD2, 0x49, 0x25, 0x15, 0xAA, 0xCF, 0x29,
+        0x69, 0x42, 0xAA, 0xA5, 0x7C, 0x56, 0x92, 0x94,
+        0xEE, 0x88, 0xF3, 0x4A, 0x71, 0xB4, 0x4E, 0x29,
+        0xC6, 0xED, 0xDF, 0x46, 0x3B, 0x8A, 0x35, 0x90,
+        0x19, 0x99, 0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A,
+        0x9C, 0x01, 0x9B, 0xE4, 0x79, 0x73, 0x93, 0x59,
+        0x69, 0xD9, 0x36, 0x65, 0x99, 0x62, 0x34, 0x1E,
+        0x56, 0x95, 0xAD, 0x96, 0x75, 0x7B, 0xD6, 0x4F,
+        0x94, 0x6F, 0x1A, 0xA3, 0x0C, 0x3C, 0xEE, 0x71,
+        0xE6, 0x51, 0x45, 0x56, 0x6D, 0x22, 0xED, 0x29,
+        0x29, 0x53, 0xFA, 0x4A, 0x41, 0xE2, 0xFC, 0xBB,
+        0x3F, 0x77, 0x28, 0x66, 0x7B, 0x58, 0x9D, 0x72,
+        0x1F, 0x7F, 0xE4, 0x8C, 0x0C, 0xC0, 0x31, 0x9F,
+        0xCB, 0xB3, 0xF7, 0x72, 0x8F, 0x19, 0xB6, 0x76,
+        0x8F, 0x61, 0x8B, 0x99, 0xDA, 0xDA, 0x16, 0x99,
+        0xB7, 0xB0, 0x49, 0x2A, 0x74, 0x2D, 0x0C, 0x9D,
+        0xD4, 0xAA, 0x92, 0x85, 0x39, 0x40, 0xD2, 0x9B,
+        0xD7, 0x0C, 0x3C, 0xA7, 0x16, 0x27, 0x1C, 0x6A,
+        0x5D, 0x91, 0xDF, 0x43, 0x70, 0xDC, 0xA2, 0x01,
+        0x8C, 0xF5, 0xC1, 0xFE, 0xF1, 0x3F, 0xF3, 0x4F,
+        0xFE, 0x07, 0xB5, 0x35, 0xC6, 0x31, 0xEC, 0x4A,
+        0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97, 0xD1, 0xA3,
+        0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D, 0xD1, 0x75,
+        0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21, 0xDD, 0x29,
+        0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43, 0x8E, 0xDB,
+        0xBA, 0x94, 0x37, 0x10, 0x6B, 0x21, 0x00, 0x19,
+        0xD5, 0xDB, 0xF6, 0xED, 0x17, 0xE0, 0xA5, 0x2F,
+        0x30, 0x33, 0x9A, 0xE3, 0x18, 0xF6, 0x25, 0x67,
+        0x12, 0xCE, 0xCA, 0x0C, 0xCB, 0xE8, 0xD1, 0xB9,
+        0x25, 0x2D, 0xAA, 0xCF, 0x26, 0xE8, 0xBA, 0xAA,
+        0xDD, 0x44, 0x16, 0x92, 0x90, 0xEE, 0x94, 0xF3,
+        0x88, 0x71, 0xD4, 0x8E, 0x21, 0xC7, 0x6D, 0xDD,
+        0x4A, 0x1B, 0x88, 0x35, 0x90, 0x19, 0x99, 0x2E,
+        0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x03, 0x30,
+        0x80, 0x04, 0xDB, 0x99, 0x69, 0x09, 0x8B, 0x7E,
+        0xCF, 0x8D, 0x99, 0x66, 0x54, 0x6C, 0x12, 0x4A,
+        0x9D, 0xC7, 0x67, 0x57, 0xAD, 0x3D, 0x25, 0x0A,
+        0x6A, 0xA9, 0x4F, 0x3B, 0x9C, 0x79, 0x4A, 0x71,
+        0x62, 0x71, 0xC7, 0x17, 0x69, 0x4B, 0xBF, 0xD4,
+        0x1F, 0xC0, 0x43, 0x8C, 0x79, 0xAE, 0xB5, 0x84,
+        0x79, 0x57, 0x7E, 0x9A, 0xC8, 0x57, 0xAD, 0xDD,
+        0x5B, 0x64, 0xEB, 0x69, 0xD0, 0xD5, 0xD6, 0x50,
+        0xA7, 0xF3, 0x47, 0x9B, 0x18, 0xD0, 0x33, 0x7C,
+        0x61, 0x0D, 0x9F, 0x48, 0xEC, 0xC0, 0x03, 0x12,
+        0xFB, 0x5E, 0xC3, 0x68, 0xCC, 0x2A, 0x34, 0xCC,
+        0xCB, 0x83, 0xB7, 0xC9, 0x2B, 0x94, 0xEC, 0xEB,
+        0x1A, 0x5E, 0xAA, 0x8E, 0x9D, 0x03, 0xCE, 0x30,
+        0xEE, 0x69, 0xE8, 0xC8, 0x71, 0x20, 0x71, 0xA7,
+        0x13, 0x69, 0x09, 0xBB, 0xD4, 0x03, 0xD9, 0xE4,
+        0xB8, 0xE2, 0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C,
+        0x2E, 0x4B, 0x8E, 0x27, 0xD8, 0x6E, 0xF6, 0x5D,
+        0xF8, 0xC2, 0xD6, 0x23, 0xF6, 0xEC, 0xE9, 0xE3,
+        0x55, 0x2F, 0x28, 0xB5, 0x88, 0xFD, 0xBB, 0x3A,
+        0x78, 0xD5, 0x4B, 0xCA, 0x02, 0xE4, 0xB8, 0xE2,
+        0x7D, 0x86, 0xEF, 0x65, 0xDF, 0x8C, 0x0C, 0xE6,
+        0xB8, 0xE1, 0x1D, 0x3B, 0x68, 0xE2, 0x59, 0xD6,
+        0x99, 0xA6, 0x65, 0x2D, 0xF2, 0xB2, 0xE5, 0xAA,
+        0xD0, 0xB1, 0x78, 0x2D, 0x23, 0xA7, 0x41, 0x69,
+        0x29, 0x86, 0xF3, 0x4C, 0x48, 0x43, 0x49, 0x03,
+        0x4D, 0x34, 0x9B, 0x08, 0x4D, 0xDE, 0xB0, 0x99,
+        0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA, 0x5E,
+        0x50, 0x67, 0x35, 0xC6, 0xCD, 0xEC, 0x49, 0x2E,
+        0x25, 0x9D, 0x93, 0xD9, 0x97, 0x2F, 0xA4, 0x92,
+        0x4A, 0x2B, 0x55, 0x9E, 0x52, 0xD2, 0x85, 0x55,
+        0x4A, 0xF8, 0xAD, 0x25, 0x29, 0xDD, 0x11, 0xE6,
+        0x94, 0xE3, 0x68, 0x9C, 0x53, 0x8D, 0xDB, 0xBE,
+        0x8C, 0x77, 0x14, 0x04, 0xF1, 0x1C, 0x23, 0xA7,
+        0x92, 0x5F, 0xB3, 0xAC, 0x66, 0x64, 0xF6, 0x52,
+        0xA6, 0x49, 0x97, 0xAF, 0x7B, 0xC9, 0x5E, 0xF0,
+        0x5A, 0x3A, 0xBE, 0xA1, 0x54, 0xD3, 0xD1, 0x73,
+        0x8A, 0x90, 0xA7, 0x1B, 0x44, 0xE2, 0x94, 0xBC,
+        0xD2, 0x92, 0x3F, 0x4C, 0x48, 0x13, 0x39, 0x2E,
+        0x38, 0x9F, 0x61, 0xBB, 0xD9, 0x77, 0xE3, 0x01,
+        0x97, 0xF4, 0xF7, 0x1B, 0xB6, 0x51, 0xE7, 0xBB,
+        0x76, 0xD5, 0xB5, 0x74, 0xB7, 0x15, 0xCD, 0x7A,
+        0x59, 0x15, 0x34, 0x89, 0x02, 0xCD, 0xBA, 0xB9,
+        0x02, 0x34, 0x47, 0xF3, 0xD1, 0x18, 0x5A, 0xBA,
+        0x14, 0x8C, 0x2E, 0xD2, 0x16, 0x95, 0x28, 0x12,
+        0x10, 0x29, 0x46, 0xCC, 0x00, 0x33, 0xC9, 0x71,
+        0xC4, 0xFB, 0x0D, 0xDE, 0xCB, 0xBF, 0x18, 0x5C,
+        0x97, 0x1C, 0x4F, 0xB0, 0xDD, 0xEC, 0xBB, 0xF1,
+        0x83, 0x30, 0x80, 0x0C, 0xF2, 0x5C, 0x71, 0x3E,
+        0xC3, 0x77, 0xB2, 0xEF, 0xC6, 0x17, 0x25, 0xC7,
+        0x13, 0xEC, 0x37, 0x7B, 0x2E, 0xFC, 0x60, 0xCC,
+        0x20, 0x03, 0x3C, 0x97, 0x1C, 0x4F, 0xB0, 0xDD,
+        0xEC, 0xBB, 0xF1, 0x81, 0x9C, 0xD7, 0x1C, 0x23,
+        0xA7, 0x6D, 0x1C, 0x4B, 0x3A, 0xD3, 0x34, 0xCC,
+        0xA5, 0xBE, 0x56, 0x5C, 0xB5, 0x5A, 0x16, 0x2F,
+        0x05, 0xA4, 0x74, 0xE8, 0x2D, 0x25, 0x30, 0xDE,
+        0x69, 0x89, 0x08, 0x69, 0x20, 0x69, 0xA6, 0x93,
+        0x61, 0x09, 0xBB, 0xD6, 0x35, 0x90, 0x19, 0x99,
+        0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x07,
+        0x8D, 0x36, 0xE6, 0xA6, 0x42, 0x6D, 0x1F, 0xB3,
+        0xE3, 0x69, 0x99, 0x95, 0xEB, 0x7C, 0x92, 0xB9,
+        0x71, 0xD9, 0xD6, 0x2A, 0x8F, 0x47, 0x4E, 0x82,
+        0xAA, 0x53, 0x0E, 0xE6, 0x9E, 0x42, 0x1C, 0x48,
+        0x1C, 0x69, 0xC4, 0xDA, 0x42, 0x6E, 0xF5, 0x07,
+        0xF1, 0x08, 0x00, 0xCB, 0x40, 0xF7, 0x1B, 0xBD,
+        0x67, 0xB4, 0xEC, 0x53, 0x14, 0xE9, 0x74, 0xAB,
+        0x47, 0x2C, 0x96, 0xB5, 0xBD, 0x22, 0x40, 0xA5,
+        0xFD, 0xE1, 0x01, 0x12, 0x99, 0xCC, 0x4A, 0x67,
+        0xFC, 0xC9, 0xB0, 0xA5, 0xF4, 0x62, 0x58, 0x44,
+        0x84, 0x06, 0x73, 0x5C, 0x6C, 0xDE, 0xC4, 0x92,
+        0xE2, 0x59, 0xD9, 0x3D, 0x99, 0x72, 0xFA, 0x49,
+        0x24, 0xA2, 0xB5, 0x59, 0xE5, 0x2D, 0x28, 0x55,
+        0x54, 0xAF, 0x8A, 0xD2, 0x52, 0x9D, 0xD1, 0x1E,
+        0x69, 0x4E, 0x36, 0x89, 0xC5, 0x38, 0xDD, 0xBB,
+        0xE8, 0xC7, 0x71, 0x42, 0x63, 0xA5, 0xC4, 0xEB,
+        0xEF, 0xFB, 0x83, 0x24, 0x78, 0xA6, 0x4B, 0x86,
+        0x26, 0x47, 0x8D, 0xCE, 0xA7, 0x01, 0x6B, 0x11,
+        0xFB, 0x76, 0x74, 0xF1, 0xAA, 0x97, 0x94, 0x5A,
+        0xC4, 0x7E, 0xDD, 0x9D, 0x3C, 0x6A, 0xA5, 0xE5,
+        0x0C, 0xC2, 0x00, 0x33, 0xAC, 0x47, 0xED, 0xD9,
+        0xD3, 0xC6, 0xAA, 0x5E, 0x50, 0x67, 0x35, 0xC6,
+        0xCD, 0xEC, 0x49, 0x2E, 0x25, 0x9D, 0x93, 0xD9,
+        0x97, 0x2F, 0xA4, 0x92, 0x4A, 0x2B, 0x55, 0x9E,
+        0x52, 0xD2, 0x85, 0x55, 0x4A, 0xF8, 0xAD, 0x25,
+        0x29, 0xDD, 0x11, 0xE6, 0x94, 0xE3, 0x68, 0x9C,
+        0x53, 0x8D, 0xDB, 0xBE, 0x8C, 0x77, 0x14, 0x6B,
+        0x20, 0x33, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
+        0x75, 0x38, 0x0C, 0xCD, 0x3E, 0x76, 0x89, 0xBB,
+        0x97, 0xF4, 0x3B, 0x4D, 0x5D, 0xD6, 0x86, 0xD4,
+        0x5B, 0xAC, 0x9F, 0xC6, 0x90, 0x2F, 0xDA, 0xA9,
+        0x59, 0xE9, 0xFC, 0xD1, 0x09, 0x42, 0x8C, 0x0C,
+        0xDF, 0xBE, 0x9E, 0xCD, 0xC5, 0x1A, 0x67, 0x58,
+        0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC, 0xA3,
+        0x8C, 0xFE, 0xD0, 0x76, 0x16, 0xFF, 0x00, 0x76,
+        0x0A, 0xAD, 0xAD, 0xE9, 0x66, 0xD1, 0x5A, 0x7D,
+        0x52, 0xCF, 0x4E, 0xD5, 0x6A, 0x4E, 0xAC, 0x8B,
+        0xD3, 0xA4, 0x4A, 0x14, 0x61, 0x1D, 0xC7, 0x47,
+        0x76, 0xCD, 0xE2, 0x7D, 0xAA, 0xAF, 0xD9, 0xDA,
+        0xBB, 0x09, 0x5D, 0xB5, 0xD7, 0xB5, 0xEB, 0x77,
+        0x54, 0xF5, 0x4D, 0x12, 0x52, 0x43, 0x59, 0x58,
+        0x9D, 0x1A, 0x2F, 0x4F, 0x9D, 0x08, 0x53, 0x8E,
+        0xE2, 0xC6, 0x10, 0xF7, 0x80, 0xEC, 0xF5, 0x88,
+        0xFD, 0xBB, 0x3A, 0x78, 0xD5, 0x4B, 0xCA, 0x2D,
+        0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2,
+        0x8C, 0x67, 0xCA, 0x8D, 0xFB, 0x7B, 0x73, 0xDD,
+        0x2A, 0x5F, 0x04, 0x5C, 0xA8, 0xDF, 0xB7, 0xB7,
+        0x3D, 0xD2, 0xA5, 0xF0, 0x40, 0x6C, 0xCD, 0x62,
+        0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52, 0xF2, 0x8B,
+        0x58, 0x8F, 0xDB, 0xB3, 0xA7, 0x8D, 0x54, 0xBC,
+        0xA3, 0x33, 0x3B, 0x27, 0xA5, 0x3B, 0x17, 0x95,
+        0x78, 0x68, 0x54, 0xBB, 0x7A, 0xDD, 0xD5, 0x56,
+        0xBE, 0xA9, 0x25, 0xA1, 0xAB, 0xAC, 0xA7, 0x43,
+        0xE7, 0x4C, 0x36, 0x31, 0xA0, 0x7E, 0xE8, 0xC2,
+        0x1B, 0x7E, 0x81, 0xD9, 0xFC, 0xBB, 0x3F, 0x77,
+        0x28, 0x06, 0x6D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E,
+        0x35, 0x52, 0xF2, 0x83, 0x39, 0xAE, 0x36, 0x6F,
+        0x62, 0x49, 0x71, 0x2C, 0xEC, 0x9E, 0xCC, 0xB9,
+        0x7D, 0x24, 0x92, 0x51, 0x5A, 0xAC, 0xF2, 0x96,
+        0x94, 0x2A, 0xAA, 0x57, 0xC5, 0x69, 0x29, 0x4E,
+        0xE8, 0x8F, 0x34, 0xA7, 0x1B, 0x44, 0xE2, 0x9C,
+        0x6E, 0xDD, 0xF4, 0x63, 0xB8, 0xA3, 0xC5, 0xF9,
+        0x76, 0x7E, 0xEE, 0x51, 0xC6, 0x39, 0x2E, 0x56,
+        0x3A, 0xB0, 0x92, 0x35, 0x69, 0xFE, 0x53, 0xE9,
+        0xAC, 0x1F, 0xE1, 0x7F, 0xEB, 0xA4, 0xAC, 0xF9,
+        0xFE, 0x93, 0xE7, 0x2B, 0x3D, 0x2F, 0xFA, 0xD9,
+        0x00, 0x1B, 0xFC, 0x42, 0x10, 0x0C, 0x9A, 0xD4,
+        0xBE, 0x39, 0x09, 0xCF, 0xBF, 0x67, 0xD5, 0x28,
+        0x4A, 0x08, 0x6D, 0xF2, 0xB2, 0xE5, 0xC3, 0x76,
+        0xC9, 0xB4, 0x8F, 0x47, 0x6B, 0xA0, 0xAA, 0x42,
+        0x25, 0xE9, 0x48, 0x8C, 0xF3, 0x4C, 0xA0, 0x6A,
+        0x42, 0x1D, 0xCE, 0x84, 0x61, 0x02, 0x6D, 0xDC,
+        0x64, 0xE4, 0xA7, 0x5B, 0xAB, 0x57, 0x61, 0x24,
+        0x31, 0x5A, 0x05, 0x7A, 0xDD, 0xD5, 0xDD, 0x6E,
+        0xF7, 0xA9, 0xAC, 0xAC, 0x4E, 0x91, 0x2F, 0xA1,
+        0x52, 0x74, 0x21, 0x4E, 0x1B, 0xCB, 0x18, 0x47,
+        0xDC, 0x34, 0xCC, 0xF6, 0xB0, 0xC4, 0xD7, 0x70,
+        0x59, 0xD4, 0x02, 0x99, 0x2E, 0x18, 0x99, 0x1E,
+        0x37, 0x3A, 0x9C, 0x00, 0xCF, 0x2E, 0x7F, 0xB2,
+        0xEE, 0xFF, 0x00, 0xFD, 0x38, 0xB9, 0x73, 0xFD,
+        0x97, 0x77, 0xFF, 0x00, 0xE9, 0xC6, 0xCC, 0x10,
+        0x0C, 0x67, 0xCB, 0x9F, 0xEC, 0xBB, 0xBF, 0xFF,
+        0x00, 0x4E, 0x38, 0xC7, 0x25, 0x3A, 0xDD, 0x5A,
+        0xBB, 0x09, 0x21, 0x8A, 0xD0, 0x2B, 0xD6, 0xEE,
+        0xAE, 0xEB, 0x77, 0xBD, 0x4D, 0x65, 0x62, 0x74,
+        0x89, 0x7D, 0x0A, 0x93, 0xA1, 0x0A, 0x70, 0xDE,
+        0x58, 0xC2, 0x3E, 0xE1, 0xBF, 0xC0, 0xCC, 0xC9,
+        0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x33,
+        0x33, 0xED, 0x9D, 0x6E, 0xB2, 0x9D, 0x84, 0xAE,
+        0xC5, 0x68, 0x15, 0xD5, 0x78, 0xD4, 0xF5, 0xBB,
+        0xDE, 0xBA, 0xAE, 0xAD, 0x3A, 0x34, 0xBE, 0x85,
+        0x49, 0xB1, 0x8D, 0x08, 0x6F, 0x24, 0x23, 0x1F,
+        0x70, 0x9F, 0x6C, 0xEB, 0x75, 0x94, 0xEC, 0x25,
+        0x76, 0x2B, 0x40, 0xAE, 0xAB, 0xC6, 0xA7, 0xAD,
+        0xDE, 0xF5, 0xD5, 0x75, 0x69, 0xD1, 0xA5, 0xF4,
+        0x2A, 0x4D, 0x8C, 0x68, 0x43, 0x79, 0x21, 0x18,
+        0xFB, 0x86, 0x99, 0x9E, 0xD6, 0x18, 0x9A, 0xEE,
+        0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3, 0x13, 0x5D,
+        0xC1, 0x67, 0x50, 0x00, 0xCC, 0xCE, 0x4A, 0x75,
+        0xBA, 0xB5, 0x76, 0x12, 0x43, 0x15, 0xA0, 0x57,
+        0xAD, 0xDD, 0x5D, 0xD6, 0xEF, 0x7A, 0x9A, 0xCA,
+        0xC4, 0xE9, 0x12, 0xFA, 0x15, 0x27, 0x42, 0x14,
+        0xE1, 0xBC, 0xB1, 0x84, 0x7D, 0xC3, 0xB3, 0xE5,
+        0xCF, 0xF6, 0x5D, 0xDF, 0xFF, 0x00, 0xA7, 0x0C,
+        0xD3, 0x25, 0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53,
+        0x86, 0x60, 0x18, 0x01, 0x92, 0x9D, 0x6D, 0xC0,
+        0xF3, 0xDB, 0x76, 0xD7, 0x40, 0xAD, 0x3A, 0x55,
+        0x60, 0xEA, 0x97, 0xBD, 0x0B, 0x2D, 0x95, 0x01,
+        0x51, 0x7A, 0x75, 0x25, 0xA7, 0x4A, 0x31, 0xDC,
+        0x6C, 0x37, 0x6D, 0xDE, 0x3B, 0x3E, 0x5C, 0xFF,
+        0x00, 0x65, 0xDD, 0xFF, 0x00, 0xFA, 0x70, 0xCC,
+        0xE9, 0x71, 0x3A, 0xFB, 0xFE, 0xE0, 0xC9, 0x1E,
+        0x19, 0x80, 0x63, 0x3E, 0x5C, 0xFF, 0x00, 0x65,
+        0xDD, 0xFF, 0x00, 0xFA, 0x71, 0xC6, 0x39, 0x29,
+        0xD6, 0xEA, 0xD5, 0xD8, 0x49, 0x0C, 0x56, 0x81,
+        0x5E, 0xB7, 0x75, 0x77, 0x5B, 0xBD, 0xEA, 0x6B,
+        0x2B, 0x13, 0xA4, 0x4B, 0xE8, 0x54, 0x9D, 0x08,
+        0x53, 0x86, 0xF2, 0xC6, 0x11, 0xF7, 0x0D, 0xFE,
+        0x06, 0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE,
+        0xA7, 0x00, 0xCC, 0x21, 0x08, 0x00, 0xCC, 0xF6,
+        0xB0, 0xC4, 0xD7, 0x70, 0x59, 0xD4, 0x02, 0x99,
+        0x2E, 0x18, 0x99, 0x1E, 0x37, 0x3A, 0x9C, 0x53,
+        0xDA, 0xC3, 0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A,
+        0x64, 0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70,
+        0x0C, 0xC2, 0x10, 0x80, 0x40, 0x66, 0x64, 0xB8,
+        0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0xCC, 0x06,
+        0x66, 0x4B, 0x86, 0x26, 0x47, 0x8D, 0xCE, 0xA7,
+        0x01, 0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D,
+        0x40, 0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3,
+        0xA8, 0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16,
+        0x75, 0x00, 0xA7, 0xB5, 0x86, 0x26, 0xBB, 0x82,
+        0xCE, 0xA0, 0x01, 0x4C, 0x97, 0x0C, 0x4C, 0x8F,
+        0x1B, 0x9D, 0x4E, 0x19, 0x86, 0x4D, 0x9A, 0xE3,
+        0xFB, 0x74, 0xEC, 0x5B, 0x89, 0x67, 0x59, 0x96,
+        0x99, 0xAB, 0xB0, 0x4A, 0xCA, 0x76, 0xAB, 0x42,
+        0xBD, 0xDE, 0xB4, 0x92, 0x85, 0x35, 0xA4, 0xA7,
+        0x9B, 0xCE, 0x31, 0x19, 0x4D, 0x2C, 0x4D, 0x38,
+        0xD2, 0xEC, 0x29, 0x77, 0xFA, 0xC2, 0x67, 0x2A,
+        0x37, 0x13, 0xED, 0xCF, 0x74, 0xAE, 0xFC, 0x10,
+        0x03, 0x33, 0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC,
+        0xB0, 0xCD, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
+        0x75, 0x38, 0x00, 0x79, 0x2D, 0x4C, 0x84, 0xDA,
+        0x33, 0x13, 0x91, 0x69, 0x99, 0x95, 0xEB, 0x7C,
+        0x92, 0xB9, 0xA2, 0xF6, 0x75, 0x8A, 0xA3, 0xD1,
+        0xD3, 0xA0, 0x79, 0xA6, 0x1D, 0xCD, 0x3C, 0x84,
+        0x38, 0x90, 0x38, 0xD3, 0x89, 0xB4, 0x84, 0xDD,
+        0xEA, 0x0F, 0xF3, 0x25, 0xC3, 0x13, 0x23, 0xC6,
+        0xE7, 0x53, 0x80, 0x66, 0x03, 0x33, 0x25, 0xC3,
+        0x13, 0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33,
+        0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38,
+        0x06, 0x61, 0x08, 0x40, 0x06, 0x67, 0xB5, 0x86,
+        0x26, 0xBB, 0x82, 0xCE, 0xA0, 0x14, 0xC9, 0x70,
+        0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE2, 0x9E, 0xD6,
+        0x18, 0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0x25,
+        0xC3, 0x13, 0x23, 0xC6, 0xE7, 0x53, 0x80, 0x66,
+        0x10, 0x84, 0x02, 0x03, 0x33, 0x25, 0xC3, 0x13,
+        0x23, 0xC6, 0xE7, 0x53, 0x86, 0x60, 0x33, 0x32,
+        0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38, 0x0A,
+        0x7B, 0x58, 0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x01,
+        0x4F, 0x6B, 0x0C, 0x4D, 0x77, 0x05, 0x9D, 0x40,
+        0x29, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3, 0xA8,
+        0x05, 0x3D, 0xAC, 0x31, 0x35, 0xDC, 0x16, 0x75,
+        0x00, 0x06, 0x61, 0x08, 0x40, 0x31, 0x9C, 0xEB,
+        0x65, 0x86, 0xEE, 0x5F, 0xD7, 0x2C, 0x93, 0xA6,
+        0x36, 0x66, 0x4D, 0x95, 0xB8, 0xFF, 0x00, 0x82,
+        0xDD, 0x88, 0x0F, 0xB5, 0x5A, 0xAA, 0x4E, 0xF9,
+        0xF8, 0x11, 0x21, 0x94, 0x52, 0x12, 0x9E, 0xF3,
+        0xA3, 0xBB, 0x61, 0x07, 0xB5, 0x35, 0xC6, 0x31,
+        0xEC, 0x4A, 0xCE, 0x25, 0x9D, 0x94, 0x19, 0x97,
+        0xD1, 0xA3, 0x72, 0x4A, 0x5B, 0x55, 0x9E, 0x4D,
+        0xD1, 0x75, 0x55, 0xBA, 0x88, 0x2D, 0x25, 0x21,
+        0xDD, 0x29, 0xE7, 0x10, 0xE3, 0xA9, 0x1C, 0x43,
+        0x8E, 0xDB, 0xBA, 0x94, 0x37, 0x10, 0x78, 0xB3,
+        0x80, 0xFA, 0xCE, 0xFE, 0x13, 0xFC, 0xB0, 0xCD,
+        0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E, 0x75, 0x38,
+        0x0B, 0x57, 0x6F, 0xDB, 0xB4, 0x5F, 0x82, 0x94,
+        0xBC, 0xC0, 0xCE, 0x6B, 0x8C, 0x63, 0xD8, 0x95,
+        0x9C, 0x4B, 0x3B, 0x28, 0x33, 0x2F, 0xA3, 0x46,
+        0xE4, 0x94, 0xB6, 0xAB, 0x3C, 0x9B, 0xA2, 0xEA,
+        0xAB, 0x75, 0x10, 0x5A, 0x4A, 0x43, 0xBA, 0x53,
+        0xCE, 0x21, 0xC7, 0x52, 0x38, 0x87, 0x1D, 0xB7,
+        0x75, 0x28, 0x6E, 0x20, 0xD6, 0x40, 0x66, 0x64,
+        0xB8, 0x62, 0x64, 0x78, 0xDC, 0xEA, 0x70, 0x16,
+        0xB1, 0x1F, 0xB7, 0x67, 0x4F, 0x1A, 0xA9, 0x79,
+        0x45, 0xAC, 0x47, 0xED, 0xD9, 0xD3, 0xC6, 0xAA,
+        0x5E, 0x50, 0xCC, 0x20, 0x19, 0x36, 0x74, 0x6D,
+        0x9B, 0xD8, 0x95, 0x9C, 0x4B, 0x45, 0x27, 0xB4,
+        0xCE, 0x5F, 0x46, 0xE4, 0x94, 0xB6, 0x5B, 0x44,
+        0xA5, 0xA5, 0x0A, 0xAB, 0x75, 0x10, 0x5A, 0x44,
+        0x53, 0x7A, 0x23, 0x0D, 0x21, 0xC7, 0x52, 0x38,
+        0x86, 0x9B, 0xB3, 0x75, 0x28, 0xEE, 0x20, 0xA6,
+        0xB8, 0xD9, 0xBD, 0x89, 0x25, 0xC4, 0xB3, 0xB2,
+        0x7B, 0x32, 0xE5, 0xF4, 0x92, 0x49, 0x45, 0x6A,
+        0xB3, 0xCA, 0x5A, 0x50, 0xAA, 0xA9, 0x5F, 0x15,
+        0xA4, 0xA5, 0x3B, 0xA2, 0x3C, 0xD2, 0x9C, 0x6D,
+        0x13, 0x8A, 0x71, 0xBB, 0x77, 0xD1, 0x8E, 0xE2,
+        0x84, 0xC9, 0xED, 0x61, 0x89, 0xAE, 0xE0, 0xB3,
+        0xA8, 0x05, 0x32, 0x5C, 0x31, 0x32, 0x3C, 0x6E,
+        0x75, 0x38, 0x0B, 0x58, 0x8F, 0xDB, 0xB3, 0xA7,
+        0x8D, 0x54, 0xBC, 0xA2, 0xD6, 0x23, 0xF6, 0xEC,
+        0xE9, 0xE3, 0x55, 0x2F, 0x28, 0x66, 0x10, 0x01,
+        0x9D, 0x62, 0x3F, 0x6E, 0xCE, 0x9E, 0x35, 0x52,
+        0xF2, 0x8F, 0x6A, 0x6B, 0x8C, 0xB4, 0xBA, 0xC5,
+        0xB8, 0x96, 0x75, 0x99, 0x69, 0x94, 0x6C, 0x12,
+        0xB2, 0x9D, 0xAA, 0xD0, 0xAF, 0x5A, 0x62, 0x4A,
+        0x14, 0xD6, 0x92, 0x9E, 0x6F, 0x38, 0xC2, 0x94,
+        0xD2, 0xC4, 0xD3, 0x8D, 0x2E, 0xC2, 0x97, 0x7F,
+        0xAC, 0x26, 0x08, 0x00, 0xCC, 0xF6, 0xB0, 0xC4,
+        0xD7, 0x70, 0x59, 0xD4, 0x02, 0x9E, 0xD6, 0x18,
+        0x9A, 0xEE, 0x0B, 0x3A, 0x80, 0x53, 0xDA, 0xC3,
+        0x13, 0x5D, 0xC1, 0x67, 0x50, 0x0A, 0x7B, 0x58,
+        0x62, 0x6B, 0xB8, 0x2C, 0xEA, 0x00, 0x0C, 0xC2,
+        0x10, 0x80, 0x63, 0x39, 0xC0, 0x7D, 0x67, 0x7F,
+        0x09, 0xFE, 0x58, 0x66, 0x99, 0x2E, 0x18, 0x99,
+        0x1E, 0x37, 0x3A, 0x9C, 0x0C, 0xCE, 0x03, 0xEB,
+        0x3B, 0xF8, 0x4F, 0xF2, 0xC3, 0x34, 0xC9, 0x70,
+        0xC4, 0xC8, 0xF1, 0xB9, 0xD4, 0xE0, 0x19, 0x80,
+        0xCC, 0xC9, 0x70, 0xC4, 0xC8, 0xF1, 0xB9, 0xD4,
+        0xE1, 0x98, 0x0C, 0xCC, 0x97, 0x0C, 0x4C, 0x8F,
+        0x1B, 0x9D, 0x4E, 0x03, 0xFF, 0xD9};
+    const int goodJpegImageWidth = 128;
+    const int goodJpegImageHeight = 128;
+};  // namespace
+
+// https://code.google.com/p/android/issues/detail?id=42382
+// https://code.google.com/p/android/issues/detail?id=9064
+// https://code.google.com/p/skia/issues/detail?id=1649
+
+/**
+  This test will test the ability of the SkImageDecoder to deal with
+  Jpeg files which have been mangled somehow.  We want to display as
+  much of the jpeg as possible.
+*/
+static void TestJpeg(skiatest::Reporter* reporter) {
+    size_t len = sizeof(goodJpegImage) / 2;
+    // I am explicitly not putting the entire image into the
+    // DecodeMemory.  This simulates a network error.
+
+    SkBitmap bm8888;
+    bool imageDecodeSuccess = SkImageDecoder::DecodeMemory(
+        static_cast<void *>(goodJpegImage), len, &bm8888);
+    REPORTER_ASSERT(reporter, imageDecodeSuccess);
+    REPORTER_ASSERT(reporter, bm8888.width() == goodJpegImageWidth);
+    REPORTER_ASSERT(reporter, bm8888.height() == goodJpegImageHeight);
+    REPORTER_ASSERT(reporter, !(bm8888.empty()));
+
+    // Pick a few pixels and verify that their colors match the colors
+    // we expect (given the original image).
+    REPORTER_ASSERT(reporter, bm8888.getColor(7, 9) == 0xffffffff);
+    REPORTER_ASSERT(reporter, bm8888.getColor(28, 3) == 0xff000000);
+    REPORTER_ASSERT(reporter, bm8888.getColor(27, 34) == 0xffffffff);
+    REPORTER_ASSERT(reporter, bm8888.getColor(71, 18) == 0xff000000);
+
+    // This is the fill color
+    REPORTER_ASSERT(reporter, bm8888.getColor(127, 127) == SK_ColorWHITE);
+
+    #if JPEG_TEST_WRITE_TO_FILE_FOR_DEBUGGING
+    // Check to see that the resulting bitmap is nice
+    bool writeSuccess = (!(bm8888.empty())) && SkImageEncoder::EncodeFile(
+        "HalfOfAJpeg.png", bm8888, SkImageEncoder::kPNG_Type, 100);
+    SkASSERT(writeSuccess);
+    #endif
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("JpegTest", JpegTestClass, TestJpeg)
+
diff --git a/tools/find_bad_images_in_skps.py b/tools/find_bad_images_in_skps.py
new file mode 100755 (executable)
index 0000000..5dcf6a4
--- /dev/null
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+# Copyright 2013 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+This script will take as an argument either a list of skp files or a
+set of directories that contains skp files.  It will then test each
+skp file with the `render_pictures` program. If that program either
+spits out any unexpected output or doesn't return 0, I will flag that
+skp file as problematic.  We then extract all of the embedded images
+inside the skp and test each one of them against the
+SkImageDecoder::DecodeFile function.  Again, we consider any
+extraneous output or a bad return value an error.  In the event of an
+error, we retain the image and print out information about the error.
+The output (on stdout) is formatted as a csv document.
+
+A copy of each bad image is left in a directory created by
+tempfile.mkdtemp().
+"""
+
+import glob
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+
+import test_rendering  # skia/trunk/tools.  reuse FindPathToProgram()
+
+USAGE = """
+Usage:
+    {command} SKP_FILE [SKP_FILES]
+    {command} SKP_DIR [SKP_DIRS]\n
+Environment variables:
+    To run multiple worker threads, set NUM_THREADS.
+    To use a different temporary storage location, set TMPDIR.
+
+"""
+
+def execute_program(args, ignores=None):
+    """
+    Execute a process and waits for it to complete.  Returns all
+    output (stderr and stdout) after (optional) filtering.
+
+    @param args is passed into subprocess.Popen().
+
+    @param ignores (optional) is a list of regular expression strings
+    that will be ignored in the output.
+
+    @returns a tuple (returncode, output)
+    """
+    if ignores is None:
+        ignores = []
+    else:
+        ignores = [re.compile(ignore) for ignore in ignores]
+    proc = subprocess.Popen(
+        args,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT)
+    output = ''.join(
+        line for line in proc.stdout
+        if not any(bool(ignore.match(line)) for ignore in ignores))
+    returncode = proc.wait()
+    return (returncode, output)
+
+
+def list_files(paths):
+    """
+    Accepts a list of directories or filenames on the command line.
+    We do not choose to recurse into directories beyond one level.
+    """
+    class NotAFileException(Exception):
+        pass
+    for path in paths:
+        for globbedpath in glob.iglob(path): # useful on win32
+            if os.path.isdir(globbedpath):
+                for filename in os.listdir(globbedpath):
+                    newpath = os.path.join(globbedpath, filename)
+                    if os.path.isfile(newpath):
+                        yield newpath
+            elif os.path.isfile(globbedpath):
+                yield globbedpath
+            else:
+                raise NotAFileException('{} is not a file'.format(globbedpath))
+
+
+class BadImageFinder(object):
+
+    def __init__(self, directory=None):
+        self.render_pictures = test_rendering.FindPathToProgram(
+            'render_pictures')
+        self.test_image_decoder = test_rendering.FindPathToProgram(
+            'test_image_decoder')
+        assert os.path.isfile(self.render_pictures)
+        assert os.path.isfile(self.test_image_decoder)
+        if directory is None:
+            self.saved_image_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
+        else:
+            assert os.path.isdir(directory)
+            self.saved_image_dir = directory
+        self.bad_image_count = 0
+
+    def process_files(self, skp_files):
+        for path in skp_files:
+            self.process_file(path)
+
+    def process_file(self, skp_file):
+        assert self.saved_image_dir is not None
+        assert os.path.isfile(skp_file)
+        args = [self.render_pictures, '--readPath', skp_file]
+        ignores = ['^process_in', '^deserializ', '^drawing...', '^Non-defaul']
+        returncode, output = execute_program(args, ignores)
+        if (returncode == 0) and not output:
+            return
+        temp_image_dir = tempfile.mkdtemp(prefix='skia_skp_test___')
+        args = [ self.render_pictures, '--readPath', skp_file,
+                 '--writePath', temp_image_dir, '--writeEncodedImages']
+        subprocess.call(args, stderr=open(os.devnull,'w'),
+                        stdout=open(os.devnull,'w'))
+        for image_name in os.listdir(temp_image_dir):
+            image_path = os.path.join(temp_image_dir, image_name)
+            assert(os.path.isfile(image_path))
+            args = [self.test_image_decoder, image_path]
+            returncode, output = execute_program(args, [])
+            if (returncode == 0) and not output:
+                os.remove(image_path)
+                continue
+            try:
+                shutil.move(image_path, self.saved_image_dir)
+            except (shutil.Error,):
+                # If this happens, don't stop the entire process,
+                # just warn the user.
+                os.remove(image_path)
+                sys.stderr.write('{0} is a repeat.\n'.format(image_name))
+            self.bad_image_count += 1
+            if returncode == 2:
+                returncode = 'SkImageDecoder::DecodeFile returns false'
+            elif returncode == 0:
+                returncode = 'extra verbosity'
+                assert output
+            elif returncode == -11:
+                returncode = 'segmentation violation'
+            else:
+                returncode = 'returncode: {}'.format(returncode)
+            output = output.strip().replace('\n',' ').replace('"','\'')
+            suffix = image_name[-3:]
+            output_line = '"{0}","{1}","{2}","{3}","{4}"\n'.format(
+                returncode, suffix, skp_file, image_name, output)
+            sys.stdout.write(output_line)
+            sys.stdout.flush()
+        os.rmdir(temp_image_dir)
+        return
+
+def main(main_argv):
+    if not main_argv or main_argv[0] in ['-h', '-?', '-help', '--help']:
+        sys.stderr.write(USAGE.format(command=__file__))
+        return 1
+    if 'NUM_THREADS' in os.environ:
+        number_of_threads = int(os.environ['NUM_THREADS'])
+        if number_of_threads < 1:
+            number_of_threads = 1
+    else:
+        number_of_threads = 1
+    os.environ['skia_images_png_suppressDecoderWarnings'] = 'true'
+    os.environ['skia_images_jpeg_suppressDecoderWarnings'] = 'true'
+
+    temp_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
+    sys.stderr.write('Directory for bad images: {}\n'.format(temp_dir))
+    sys.stdout.write('"Error","Filetype","SKP File","Image File","Output"\n')
+    sys.stdout.flush()
+
+    finders = [
+        BadImageFinder(temp_dir) for index in xrange(number_of_threads)]
+    arguments = [[] for index in xrange(number_of_threads)]
+    for index, item in enumerate(list_files(main_argv)):
+        ## split up the given targets among the worker threads
+        arguments[index % number_of_threads].append(item)
+    threads = [
+        threading.Thread(
+            target=BadImageFinder.process_files, args=(finder,argument))
+        for finder, argument in zip(finders, arguments)]
+    for thread in threads:
+        thread.start()
+    for thread in threads:
+        thread.join()
+    number  = sum(finder.bad_image_count for finder in finders)
+    sys.stderr.write('Number of bad images found: {}\n'.format(number))
+    return 0
+
+if __name__ == '__main__':
+    exit(main(sys.argv[1:]))
+
+#  LocalWords:  skp stdout csv
diff --git a/tools/test_image_decoder.cpp b/tools/test_image_decoder.cpp
new file mode 100644 (file)
index 0000000..2ba89aa
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkForceLinking.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+/**
+   Simple program to test Skia's ability to decode images without
+   errors or debug messages. */
+int main(int argc, char ** argv) {
+    if (argc < 2) {
+        SkDebugf("Usage:\n %s imagefile\n\n", argv[0]);
+        return 3;
+    }
+    SkBitmap bitmap;
+    if (!(SkImageDecoder::DecodeFile(argv[1], &bitmap))) {
+        return 2;
+    }
+    if (bitmap.empty()) {
+        return 1;
+    }
+    return 0;
+}
+