Index: ui/gfx/codec/png_codec.cc |
diff --git a/ui/gfx/codec/png_codec.cc b/ui/gfx/codec/png_codec.cc |
index 474fbc9f3999b9464a990aaef0f255080c0e2a6e..82ffcd2b28e2840820ac65d78deee8527c0a6cfe 100644 |
--- a/ui/gfx/codec/png_codec.cc |
+++ b/ui/gfx/codec/png_codec.cc |
@@ -94,6 +94,8 @@ const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library. |
const double kDefaultGamma = 2.2; |
const double kInverseGamma = 1.0 / kDefaultGamma; |
+const png_byte kPngScaleChunk[5] = { 'c', 's', 'C', 'l', 0 }; |
+ |
class PngDecoderState { |
public: |
// Output is a vector<unsigned char>. |
@@ -105,6 +107,7 @@ class PngDecoderState { |
output(o), |
width(0), |
height(0), |
+ fell_back_to_1x(false), |
done(false) { |
} |
@@ -117,6 +120,7 @@ class PngDecoderState { |
output(NULL), |
width(0), |
height(0), |
+ fell_back_to_1x(false), |
done(false) { |
} |
@@ -138,6 +142,10 @@ class PngDecoderState { |
int width; |
int height; |
+ // True if a csCl chunk is present, indicating that GRIT didn't find an image |
+ // at the proper scale and fell back to 100%. |
+ bool fell_back_to_1x; |
+ |
// Set to true when we've found the end of the data. |
bool done; |
@@ -330,6 +338,18 @@ void DecodeEndCallback(png_struct* png_ptr, png_info* info) { |
state->done = true; |
} |
+int DecodeUserChunkCallback(png_struct* png_ptr, png_unknown_chunk* chunk) { |
+ PngDecoderState* state = static_cast<PngDecoderState*>( |
+ png_get_user_chunk_ptr(png_ptr)); |
+ // We instructed libpng to call us only for csCl chunks. |
+ DCHECK(memcmp(chunk->name, kPngScaleChunk, 5) == 0); |
+ if (chunk->size == 0) { |
+ state->fell_back_to_1x = true; |
+ return 1; // processed |
+ } |
+ return 0; // unrecognized |
+} |
+ |
// Automatically destroys the given read structs on destruction to make |
// cleanup and error handling code cleaner. |
class PngReadStructDestroyer { |
@@ -406,12 +426,8 @@ void LogLibPNGEncodeWarning(png_structp png_ptr, png_const_charp warning_msg) { |
DLOG(ERROR) << "libpng encode warning: " << warning_msg; |
} |
-} // namespace |
- |
-// static |
-bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
- ColorFormat format, std::vector<unsigned char>* output, |
- int* w, int* h) { |
+bool DecodeInternal(const unsigned char* input, size_t input_size, |
+ PngDecoderState* state) { |
png_struct* png_ptr = NULL; |
png_info* info_ptr = NULL; |
if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) |
@@ -425,23 +441,29 @@ bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
return false; |
} |
- PngDecoderState state(format, output); |
- |
png_set_error_fn(png_ptr, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning); |
- png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, |
- &DecodeRowCallback, &DecodeEndCallback); |
- png_process_data(png_ptr, |
- info_ptr, |
- const_cast<unsigned char*>(input), |
- input_size); |
- |
- if (!state.done) { |
- // Fed it all the data but the library didn't think we got all the data, so |
- // this file must be truncated. |
+ png_set_progressive_read_fn(png_ptr, state, DecodeInfoCallback, |
+ DecodeRowCallback, DecodeEndCallback); |
+ png_set_keep_unknown_chunks(png_ptr, 3, const_cast<png_byte*>(kPngScaleChunk), |
+ 1); // keep only csCl |
+ png_set_read_user_chunk_fn(png_ptr, state, DecodeUserChunkCallback); |
+ png_process_data(png_ptr, info_ptr, |
+ const_cast<unsigned char*>(input), input_size); |
+ return state->done; |
+} |
+ |
+} // namespace |
+ |
+ |
+// static |
+bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
+ ColorFormat format, std::vector<unsigned char>* output, |
+ int* w, int* h) { |
+ PngDecoderState state(format, output); |
+ if (!DecodeInternal(input, input_size, &state)) { |
output->clear(); |
return false; |
} |
- |
*w = state.width; |
*h = state.height; |
return true; |
@@ -449,41 +471,24 @@ bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
// static |
bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
- SkBitmap* bitmap) { |
+ SkBitmap* bitmap, bool* fell_back_to_1x) { |
DCHECK(bitmap); |
- png_struct* png_ptr = NULL; |
- png_info* info_ptr = NULL; |
- if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) |
- return false; |
- |
- PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); |
- if (setjmp(png_jmpbuf(png_ptr))) { |
- // The destroyer will ensure that the structures are cleaned up in this |
- // case, even though we may get here as a jump from random parts of the |
- // PNG library called below. |
- return false; |
- } |
- |
PngDecoderState state(bitmap); |
- |
- png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, |
- &DecodeRowCallback, &DecodeEndCallback); |
- png_process_data(png_ptr, |
- info_ptr, |
- const_cast<unsigned char*>(input), |
- input_size); |
- |
- if (!state.done) { |
+ if (!DecodeInternal(input, input_size, &state)) |
return false; |
- } |
- |
- // Set the bitmap's opaqueness based on what we saw. |
bitmap->setIsOpaque(state.is_opaque); |
- |
+ if (fell_back_to_1x) |
+ *fell_back_to_1x = state.fell_back_to_1x; |
return true; |
} |
// static |
+bool PNGCodec::Decode(const unsigned char* input, size_t input_size, |
+ SkBitmap* bitmap) { |
+ return Decode(input, input_size, bitmap, NULL); |
+} |
+ |
+// static |
SkBitmap* PNGCodec::CreateSkBitmapFromBGRAFormat( |
std::vector<unsigned char>& bgra, int width, int height) { |
SkBitmap* bitmap = new SkBitmap(); |