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