1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/gfx/codec/png_codec.h"
6
7 #include <stdint.h>
8
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/strings/string_util.h"
12 #include "third_party/libpng/png.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "third_party/skia/include/core/SkColorPriv.h"
15 #include "third_party/skia/include/core/SkUnPreMultiply.h"
16 #include "third_party/skia/include/encode/SkPngEncoder.h"
17 #include "third_party/zlib/zlib.h"
18 #include "ui/gfx/codec/vector_wstream.h"
19 #include "ui/gfx/geometry/size.h"
20 #include "ui/gfx/skia_util.h"
21
22 namespace gfx {
23
24 // Decoder --------------------------------------------------------------------
25 //
26 // This code is based on WebKit libpng interface (PNGImageDecoder), which is
27 // in turn based on the Mozilla png decoder.
28
29 namespace {
30
31 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
32 const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library.
33 const double kDefaultGamma = 2.2;
34 const double kInverseGamma = 1.0 / kDefaultGamma;
35
36 class PngDecoderState {
37 public:
38 // Output is a vector<unsigned char>.
PngDecoderState(PNGCodec::ColorFormat ofmt,std::vector<unsigned char> * o)39 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o)
40 : output_format(ofmt),
41 output_channels(0),
42 bitmap(NULL),
43 is_opaque(true),
44 output(o),
45 width(0),
46 height(0),
47 done(false) {
48 }
49
50 // Output is an SkBitmap.
PngDecoderState(SkBitmap * skbitmap)51 explicit PngDecoderState(SkBitmap* skbitmap)
52 : output_format(PNGCodec::FORMAT_SkBitmap),
53 output_channels(0),
54 bitmap(skbitmap),
55 is_opaque(true),
56 output(NULL),
57 width(0),
58 height(0),
59 done(false) {
60 }
61
62 PNGCodec::ColorFormat output_format;
63 int output_channels;
64
65 // An incoming SkBitmap to write to. If NULL, we write to output instead.
66 SkBitmap* bitmap;
67
68 // Used during the reading of an SkBitmap. Defaults to true until we see a
69 // pixel with anything other than an alpha of 255.
70 bool is_opaque;
71
72 // The other way to decode output, where we write into an intermediary buffer
73 // instead of directly to an SkBitmap.
74 std::vector<unsigned char>* output;
75
76 // Size of the image, set in the info callback.
77 int width;
78 int height;
79
80 // Set to true when we've found the end of the data.
81 bool done;
82
83 private:
84 DISALLOW_COPY_AND_ASSIGN(PngDecoderState);
85 };
86
87 // User transform (passed to libpng) which converts a row decoded by libpng to
88 // Skia format. Expects the row to have 4 channels, otherwise there won't be
89 // enough room in |data|.
ConvertRGBARowToSkia(png_structp png_ptr,png_row_infop row_info,png_bytep data)90 void ConvertRGBARowToSkia(png_structp png_ptr,
91 png_row_infop row_info,
92 png_bytep data) {
93 const int channels = row_info->channels;
94 DCHECK_EQ(channels, 4);
95
96 PngDecoderState* state =
97 static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr));
98 DCHECK(state) << "LibPNG user transform pointer is NULL";
99
100 unsigned char* const end = data + row_info->rowbytes;
101 for (unsigned char* p = data; p < end; p += channels) {
102 uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p);
103 const unsigned char alpha = p[channels - 1];
104 if (alpha != 255) {
105 state->is_opaque = false;
106 *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]);
107 } else {
108 *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]);
109 }
110 }
111 }
112
113 // Called when the png header has been read. This code is based on the WebKit
114 // PNGImageDecoder
DecodeInfoCallback(png_struct * png_ptr,png_info * info_ptr)115 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
116 PngDecoderState* state = static_cast<PngDecoderState*>(
117 png_get_progressive_ptr(png_ptr));
118
119 int bit_depth, color_type, interlace_type, compression_type;
120 int filter_type;
121 png_uint_32 w, h;
122 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
123 &interlace_type, &compression_type, &filter_type);
124
125 // Bounds check. When the image is unreasonably big, we'll error out and
126 // end up back at the setjmp call when we set up decoding. "Unreasonably big"
127 // means "big enough that w * h * 32bpp might overflow an int"; we choose this
128 // threshold to match WebKit and because a number of places in code assume
129 // that an image's size (in bytes) fits in a (signed) int.
130 unsigned long long total_size =
131 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
132 if (total_size > ((1 << 29) - 1))
133 longjmp(png_jmpbuf(png_ptr), 1);
134 state->width = static_cast<int>(w);
135 state->height = static_cast<int>(h);
136
137 // The following png_set_* calls have to be done in the order dictated by
138 // the libpng docs. Please take care if you have to move any of them. This
139 // is also why certain things are done outside of the switch, even though
140 // they look like they belong there.
141
142 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
143 if (color_type == PNG_COLOR_TYPE_PALETTE ||
144 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
145 png_set_expand(png_ptr);
146
147 // The '!= 0' is for silencing a Windows compiler warning.
148 bool input_has_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
149
150 // Transparency for paletted images.
151 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
152 png_set_expand(png_ptr);
153 input_has_alpha = true;
154 }
155
156 // Convert 16-bit to 8-bit.
157 if (bit_depth == 16)
158 png_set_strip_16(png_ptr);
159
160 // Pick our row format converter necessary for this data.
161 if (!input_has_alpha) {
162 switch (state->output_format) {
163 case PNGCodec::FORMAT_RGBA:
164 state->output_channels = 4;
165 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
166 break;
167 case PNGCodec::FORMAT_BGRA:
168 state->output_channels = 4;
169 png_set_bgr(png_ptr);
170 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
171 break;
172 case PNGCodec::FORMAT_ARGB:
173 state->output_channels = 4;
174 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_BEFORE);
175 break;
176 case PNGCodec::FORMAT_SkBitmap:
177 state->output_channels = 4;
178 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
179 break;
180 }
181 } else {
182 switch (state->output_format) {
183 case PNGCodec::FORMAT_RGBA:
184 state->output_channels = 4;
185 break;
186 case PNGCodec::FORMAT_BGRA:
187 state->output_channels = 4;
188 png_set_bgr(png_ptr);
189 break;
190 case PNGCodec::FORMAT_ARGB:
191 state->output_channels = 4;
192 png_set_swap_alpha(png_ptr);
193 break;
194 case PNGCodec::FORMAT_SkBitmap:
195 state->output_channels = 4;
196 break;
197 }
198 }
199
200 // Expand grayscale to RGB.
201 if (color_type == PNG_COLOR_TYPE_GRAY ||
202 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
203 png_set_gray_to_rgb(png_ptr);
204
205 // Deal with gamma and keep it under our control.
206 double gamma;
207 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
208 if (gamma <= 0.0 || gamma > kMaxGamma) {
209 gamma = kInverseGamma;
210 png_set_gAMA(png_ptr, info_ptr, gamma);
211 }
212 png_set_gamma(png_ptr, kDefaultGamma, gamma);
213 } else {
214 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
215 }
216
217 // Setting the user transforms here (as opposed to inside the switch above)
218 // because all png_set_* calls need to be done in the specific order
219 // mandated by libpng.
220 if (state->output_format == PNGCodec::FORMAT_SkBitmap) {
221 png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia);
222 png_set_user_transform_info(png_ptr, state, 0, 0);
223 }
224
225 // Tell libpng to send us rows for interlaced pngs.
226 if (interlace_type == PNG_INTERLACE_ADAM7)
227 png_set_interlace_handling(png_ptr);
228
229 png_read_update_info(png_ptr, info_ptr);
230
231 if (state->bitmap) {
232 state->bitmap->allocN32Pixels(state->width, state->height);
233 } else if (state->output) {
234 state->output->resize(
235 state->width * state->output_channels * state->height);
236 }
237 }
238
DecodeRowCallback(png_struct * png_ptr,png_byte * new_row,png_uint_32 row_num,int pass)239 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
240 png_uint_32 row_num, int pass) {
241 if (!new_row)
242 return; // Interlaced image; row didn't change this pass.
243
244 PngDecoderState* state = static_cast<PngDecoderState*>(
245 png_get_progressive_ptr(png_ptr));
246
247 if (static_cast<int>(row_num) > state->height) {
248 NOTREACHED() << "Invalid row";
249 return;
250 }
251
252 unsigned char* base = NULL;
253 if (state->bitmap)
254 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0));
255 else if (state->output)
256 base = &state->output->front();
257
258 unsigned char* dest = &base[state->width * state->output_channels * row_num];
259 png_progressive_combine_row(png_ptr, dest, new_row);
260 }
261
DecodeEndCallback(png_struct * png_ptr,png_info * info)262 void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
263 PngDecoderState* state = static_cast<PngDecoderState*>(
264 png_get_progressive_ptr(png_ptr));
265
266 // Mark the image as complete, this will tell the Decode function that we
267 // have successfully found the end of the data.
268 state->done = true;
269 }
270
271 // Holds png struct and info ensuring the proper destruction.
272 class PngReadStructInfo {
273 public:
PngReadStructInfo()274 PngReadStructInfo(): png_ptr_(nullptr), info_ptr_(nullptr) {
275 }
~PngReadStructInfo()276 ~PngReadStructInfo() {
277 png_destroy_read_struct(&png_ptr_, &info_ptr_, NULL);
278 }
279
Build(const unsigned char * input,size_t input_size)280 bool Build(const unsigned char* input, size_t input_size) {
281 if (input_size < 8)
282 return false; // Input data too small to be a png
283
284 // Have libpng check the signature, it likes the first 8 bytes.
285 if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
286 return false;
287
288 png_ptr_ = png_create_read_struct(
289 PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
290 if (!png_ptr_)
291 return false;
292
293 info_ptr_ = png_create_info_struct(png_ptr_);
294 if (!info_ptr_) {
295 return false;
296 }
297 return true;
298 }
299
300 png_struct* png_ptr_;
301 png_info* info_ptr_;
302 private:
303 DISALLOW_COPY_AND_ASSIGN(PngReadStructInfo);
304 };
305
306 // Holds png struct and info ensuring the proper destruction.
307 class PngWriteStructInfo {
308 public:
PngWriteStructInfo()309 PngWriteStructInfo() : png_ptr_(nullptr), info_ptr_(nullptr) {
310 }
311
~PngWriteStructInfo()312 ~PngWriteStructInfo() {
313 png_destroy_write_struct(&png_ptr_, &info_ptr_);
314 }
315
316 png_struct* png_ptr_;
317 png_info* info_ptr_;
318 private:
319 DISALLOW_COPY_AND_ASSIGN(PngWriteStructInfo);
320 };
321
322 // Libpng user error and warning functions which allows us to print libpng
323 // errors and warnings using Chrome's logging facilities instead of stderr.
324
LogLibPNGDecodeError(png_structp png_ptr,png_const_charp error_msg)325 void LogLibPNGDecodeError(png_structp png_ptr, png_const_charp error_msg) {
326 DLOG(ERROR) << "libpng decode error: " << error_msg;
327 longjmp(png_jmpbuf(png_ptr), 1);
328 }
329
LogLibPNGDecodeWarning(png_structp png_ptr,png_const_charp warning_msg)330 void LogLibPNGDecodeWarning(png_structp png_ptr, png_const_charp warning_msg) {
331 DLOG(ERROR) << "libpng decode warning: " << warning_msg;
332 }
333
334 } // namespace
335
336 // static
Decode(const unsigned char * input,size_t input_size,ColorFormat format,std::vector<unsigned char> * output,int * w,int * h)337 bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
338 ColorFormat format, std::vector<unsigned char>* output,
339 int* w, int* h) {
340 PngReadStructInfo si;
341 if (!si.Build(input, input_size))
342 return false;
343
344 if (setjmp(png_jmpbuf(si.png_ptr_))) {
345 // The destroyer will ensure that the structures are cleaned up in this
346 // case, even though we may get here as a jump from random parts of the
347 // PNG library called below.
348 return false;
349 }
350
351 PngDecoderState state(format, output);
352
353 png_set_error_fn(si.png_ptr_, NULL,
354 LogLibPNGDecodeError, LogLibPNGDecodeWarning);
355 png_set_progressive_read_fn(si.png_ptr_, &state, &DecodeInfoCallback,
356 &DecodeRowCallback, &DecodeEndCallback);
357 png_process_data(si.png_ptr_,
358 si.info_ptr_,
359 const_cast<unsigned char*>(input),
360 input_size);
361
362 if (!state.done) {
363 // Fed it all the data but the library didn't think we got all the data, so
364 // this file must be truncated.
365 output->clear();
366 return false;
367 }
368
369 *w = state.width;
370 *h = state.height;
371 return true;
372 }
373
374 // static
Decode(const unsigned char * input,size_t input_size,SkBitmap * bitmap)375 bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
376 SkBitmap* bitmap) {
377 DCHECK(bitmap);
378 PngReadStructInfo si;
379 if (!si.Build(input, input_size))
380 return false;
381
382 if (setjmp(png_jmpbuf(si.png_ptr_))) {
383 // The destroyer will ensure that the structures are cleaned up in this
384 // case, even though we may get here as a jump from random parts of the
385 // PNG library called below.
386 return false;
387 }
388
389 PngDecoderState state(bitmap);
390
391 png_set_progressive_read_fn(si.png_ptr_, &state, &DecodeInfoCallback,
392 &DecodeRowCallback, &DecodeEndCallback);
393 png_process_data(si.png_ptr_,
394 si.info_ptr_,
395 const_cast<unsigned char*>(input),
396 input_size);
397
398 if (!state.done) {
399 return false;
400 }
401
402 // Set the bitmap's opaqueness based on what we saw.
403 bitmap->setAlphaType(state.is_opaque ?
404 kOpaque_SkAlphaType : kPremul_SkAlphaType);
405
406 return true;
407 }
408
409 // Encoder --------------------------------------------------------------------
410
411 namespace {
412
AddComments(SkPngEncoder::Options & options,const std::vector<PNGCodec::Comment> & comments)413 static void AddComments(SkPngEncoder::Options& options,
414 const std::vector<PNGCodec::Comment>& comments) {
415 std::vector<const char*> comment_pointers;
416 std::vector<size_t> comment_sizes;
417 for (const auto& comment : comments) {
418 comment_pointers.push_back(comment.key.c_str());
419 comment_pointers.push_back(comment.text.c_str());
420 comment_sizes.push_back(comment.key.length() + 1);
421 comment_sizes.push_back(comment.text.length() + 1);
422 }
423 options.fComments = SkDataTable::MakeCopyArrays(
424 (void const* const*)comment_pointers.data(), comment_sizes.data(),
425 static_cast<int>(comment_pointers.size()));
426 }
427
428 } // namespace
429
EncodeSkPixmap(const SkPixmap & src,const std::vector<PNGCodec::Comment> & comments,std::vector<unsigned char> * output,int zlib_level)430 static bool EncodeSkPixmap(const SkPixmap& src,
431 const std::vector<PNGCodec::Comment>& comments,
432 std::vector<unsigned char>* output,
433 int zlib_level) {
434 output->clear();
435 VectorWStream dst(output);
436
437 SkPngEncoder::Options options;
438 AddComments(options, comments);
439 options.fZLibLevel = zlib_level;
440 return SkPngEncoder::Encode(&dst, src, options);
441 }
442
EncodeSkPixmap(const SkPixmap & src,bool discard_transparency,const std::vector<PNGCodec::Comment> & comments,std::vector<unsigned char> * output,int zlib_level)443 static bool EncodeSkPixmap(const SkPixmap& src,
444 bool discard_transparency,
445 const std::vector<PNGCodec::Comment>& comments,
446 std::vector<unsigned char>* output,
447 int zlib_level) {
448 if (discard_transparency) {
449 SkImageInfo opaque_info = src.info().makeAlphaType(kOpaque_SkAlphaType);
450 SkBitmap copy;
451 if (!copy.tryAllocPixels(opaque_info)) {
452 return false;
453 }
454 SkPixmap opaque_pixmap;
455 bool success = copy.peekPixels(&opaque_pixmap);
456 DCHECK(success);
457 // The following step does the unpremul as we set the dst alpha type to be
458 // kUnpremul_SkAlphaType. Later, because opaque_pixmap has
459 // kOpaque_SkAlphaType, we'll discard the transparency as required.
460 success =
461 src.readPixels(opaque_info.makeAlphaType(kUnpremul_SkAlphaType),
462 opaque_pixmap.writable_addr(), opaque_pixmap.rowBytes());
463 DCHECK(success);
464 return EncodeSkPixmap(opaque_pixmap, comments, output, zlib_level);
465 }
466 return EncodeSkPixmap(src, comments, output, zlib_level);
467 }
468
469 // static
Encode(const unsigned char * input,ColorFormat format,const Size & size,int row_byte_width,bool discard_transparency,const std::vector<Comment> & comments,std::vector<unsigned char> * output)470 bool PNGCodec::Encode(const unsigned char* input,
471 ColorFormat format,
472 const Size& size,
473 int row_byte_width,
474 bool discard_transparency,
475 const std::vector<Comment>& comments,
476 std::vector<unsigned char>* output) {
477 // Initialization required for Windows although the switch covers all cases.
478 SkColorType colorType = kN32_SkColorType;
479 switch (format) {
480 case FORMAT_RGBA:
481 colorType = kRGBA_8888_SkColorType;
482 break;
483 case FORMAT_BGRA:
484 colorType = kBGRA_8888_SkColorType;
485 break;
486 case FORMAT_ARGB:
487 return false;
488 case FORMAT_SkBitmap:
489 colorType = kN32_SkColorType;
490 break;
491 }
492 auto alphaType =
493 format == FORMAT_SkBitmap ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
494 SkImageInfo info =
495 SkImageInfo::Make(size.width(), size.height(), colorType, alphaType);
496 SkPixmap src(info, input, row_byte_width);
497 return EncodeSkPixmap(src, discard_transparency, comments, output,
498 DEFAULT_ZLIB_COMPRESSION);
499 }
500
EncodeSkBitmap(const SkBitmap & input,bool discard_transparency,std::vector<unsigned char> * output,int zlib_level)501 static bool EncodeSkBitmap(const SkBitmap& input,
502 bool discard_transparency,
503 std::vector<unsigned char>* output,
504 int zlib_level) {
505 SkPixmap src;
506 if (!input.peekPixels(&src)) {
507 return false;
508 }
509 return EncodeSkPixmap(src, discard_transparency,
510 std::vector<PNGCodec::Comment>(), output, zlib_level);
511 }
512
513 // static
EncodeBGRASkBitmap(const SkBitmap & input,bool discard_transparency,std::vector<unsigned char> * output)514 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input,
515 bool discard_transparency,
516 std::vector<unsigned char>* output) {
517 return EncodeSkBitmap(input, discard_transparency, output,
518 DEFAULT_ZLIB_COMPRESSION);
519 }
520
521 // static
EncodeA8SkBitmap(const SkBitmap & input,std::vector<unsigned char> * output)522 bool PNGCodec::EncodeA8SkBitmap(const SkBitmap& input,
523 std::vector<unsigned char>* output) {
524 DCHECK_EQ(input.colorType(), kAlpha_8_SkColorType);
525 auto info = input.info()
526 .makeColorType(kGray_8_SkColorType)
527 .makeAlphaType(kOpaque_SkAlphaType);
528 SkPixmap src(info, input.getAddr(0, 0), input.rowBytes());
529 return EncodeSkPixmap(src, std::vector<PNGCodec::Comment>(), output,
530 DEFAULT_ZLIB_COMPRESSION);
531 }
532
533 // static
FastEncodeBGRASkBitmap(const SkBitmap & input,bool discard_transparency,std::vector<unsigned char> * output)534 bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input,
535 bool discard_transparency,
536 std::vector<unsigned char>* output) {
537 return EncodeSkBitmap(input, discard_transparency, output, Z_BEST_SPEED);
538 }
539
Comment(const std::string & k,const std::string & t)540 PNGCodec::Comment::Comment(const std::string& k, const std::string& t)
541 : key(k), text(t) {
542 }
543
~Comment()544 PNGCodec::Comment::~Comment() {
545 }
546
547 } // namespace gfx
548