Index: src/images/SkImageDecoder_libjpeg.cpp |
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp |
index 3b3ea887e3cde437d7469d2a43126a2cb604a6eb..7652249f5eb0a0bb0f30f94bcb78b3af2fdf6bba 100644 |
--- a/src/images/SkImageDecoder_libjpeg.cpp |
+++ b/src/images/SkImageDecoder_libjpeg.cpp |
@@ -19,6 +19,10 @@ |
#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"); |
} |