• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "third_party/libpng/png.h"
10 #include "third_party/skia/include/core/SkBitmap.h"
11 #include "third_party/skia/include/core/SkColorPriv.h"
12 #include "third_party/skia/include/core/SkUnPreMultiply.h"
13 #include "third_party/zlib/zlib.h"
14 #include "ui/gfx/size.h"
15 #include "ui/gfx/skia_util.h"
16 
17 namespace gfx {
18 
19 namespace {
20 
21 // Converts BGRA->RGBA and RGBA->BGRA.
ConvertBetweenBGRAandRGBA(const unsigned char * input,int pixel_width,unsigned char * output,bool * is_opaque)22 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
23                                unsigned char* output, bool* is_opaque) {
24   for (int x = 0; x < pixel_width; x++) {
25     const unsigned char* pixel_in = &input[x * 4];
26     unsigned char* pixel_out = &output[x * 4];
27     pixel_out[0] = pixel_in[2];
28     pixel_out[1] = pixel_in[1];
29     pixel_out[2] = pixel_in[0];
30     pixel_out[3] = pixel_in[3];
31   }
32 }
33 
ConvertRGBAtoRGB(const unsigned char * rgba,int pixel_width,unsigned char * rgb,bool * is_opaque)34 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
35                       unsigned char* rgb, bool* is_opaque) {
36   for (int x = 0; x < pixel_width; x++)
37     memcpy(&rgb[x * 3], &rgba[x * 4], 3);
38 }
39 
ConvertSkiaToRGB(const unsigned char * skia,int pixel_width,unsigned char * rgb,bool * is_opaque)40 void ConvertSkiaToRGB(const unsigned char* skia, int pixel_width,
41                       unsigned char* rgb, bool* is_opaque) {
42   for (int x = 0; x < pixel_width; x++) {
43     const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]);
44     unsigned char* pixel_out = &rgb[x * 3];
45 
46     int alpha = SkGetPackedA32(pixel_in);
47     if (alpha != 0 && alpha != 255) {
48       SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in);
49       pixel_out[0] = SkColorGetR(unmultiplied);
50       pixel_out[1] = SkColorGetG(unmultiplied);
51       pixel_out[2] = SkColorGetB(unmultiplied);
52     } else {
53       pixel_out[0] = SkGetPackedR32(pixel_in);
54       pixel_out[1] = SkGetPackedG32(pixel_in);
55       pixel_out[2] = SkGetPackedB32(pixel_in);
56     }
57   }
58 }
59 
ConvertSkiaToRGBA(const unsigned char * skia,int pixel_width,unsigned char * rgba,bool * is_opaque)60 void ConvertSkiaToRGBA(const unsigned char* skia, int pixel_width,
61                        unsigned char* rgba, bool* is_opaque) {
62   gfx::ConvertSkiaToRGBA(skia, pixel_width, rgba);
63 }
64 
65 }  // namespace
66 
67 // Decoder --------------------------------------------------------------------
68 //
69 // This code is based on WebKit libpng interface (PNGImageDecoder), which is
70 // in turn based on the Mozilla png decoder.
71 
72 namespace {
73 
74 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
75 const double kMaxGamma = 21474.83;  // Maximum gamma accepted by png library.
76 const double kDefaultGamma = 2.2;
77 const double kInverseGamma = 1.0 / kDefaultGamma;
78 
79 class PngDecoderState {
80  public:
81   // Output is a vector<unsigned char>.
PngDecoderState(PNGCodec::ColorFormat ofmt,std::vector<unsigned char> * o)82   PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o)
83       : output_format(ofmt),
84         output_channels(0),
85         bitmap(NULL),
86         is_opaque(true),
87         output(o),
88         width(0),
89         height(0),
90         done(false) {
91   }
92 
93   // Output is an SkBitmap.
PngDecoderState(SkBitmap * skbitmap)94   explicit PngDecoderState(SkBitmap* skbitmap)
95       : output_format(PNGCodec::FORMAT_SkBitmap),
96         output_channels(0),
97         bitmap(skbitmap),
98         is_opaque(true),
99         output(NULL),
100         width(0),
101         height(0),
102         done(false) {
103   }
104 
105   PNGCodec::ColorFormat output_format;
106   int output_channels;
107 
108   // An incoming SkBitmap to write to. If NULL, we write to output instead.
109   SkBitmap* bitmap;
110 
111   // Used during the reading of an SkBitmap. Defaults to true until we see a
112   // pixel with anything other than an alpha of 255.
113   bool is_opaque;
114 
115   // The other way to decode output, where we write into an intermediary buffer
116   // instead of directly to an SkBitmap.
117   std::vector<unsigned char>* output;
118 
119   // Size of the image, set in the info callback.
120   int width;
121   int height;
122 
123   // Set to true when we've found the end of the data.
124   bool done;
125 
126  private:
127   DISALLOW_COPY_AND_ASSIGN(PngDecoderState);
128 };
129 
130 // User transform (passed to libpng) which converts a row decoded by libpng to
131 // Skia format. Expects the row to have 4 channels, otherwise there won't be
132 // enough room in |data|.
ConvertRGBARowToSkia(png_structp png_ptr,png_row_infop row_info,png_bytep data)133 void ConvertRGBARowToSkia(png_structp png_ptr,
134                           png_row_infop row_info,
135                           png_bytep data) {
136   const int channels = row_info->channels;
137   DCHECK_EQ(channels, 4);
138 
139   PngDecoderState* state =
140       static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr));
141   DCHECK(state) << "LibPNG user transform pointer is NULL";
142 
143   unsigned char* const end = data + row_info->rowbytes;
144   for (unsigned char* p = data; p < end; p += channels) {
145     uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p);
146     const unsigned char alpha = p[channels - 1];
147     if (alpha != 255) {
148       state->is_opaque = false;
149       *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]);
150     } else {
151       *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]);
152     }
153   }
154 }
155 
156 // Called when the png header has been read. This code is based on the WebKit
157 // PNGImageDecoder
DecodeInfoCallback(png_struct * png_ptr,png_info * info_ptr)158 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
159   PngDecoderState* state = static_cast<PngDecoderState*>(
160       png_get_progressive_ptr(png_ptr));
161 
162   int bit_depth, color_type, interlace_type, compression_type;
163   int filter_type;
164   png_uint_32 w, h;
165   png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
166                &interlace_type, &compression_type, &filter_type);
167 
168   // Bounds check. When the image is unreasonably big, we'll error out and
169   // end up back at the setjmp call when we set up decoding.  "Unreasonably big"
170   // means "big enough that w * h * 32bpp might overflow an int"; we choose this
171   // threshold to match WebKit and because a number of places in code assume
172   // that an image's size (in bytes) fits in a (signed) int.
173   unsigned long long total_size =
174       static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
175   if (total_size > ((1 << 29) - 1))
176     longjmp(png_jmpbuf(png_ptr), 1);
177   state->width = static_cast<int>(w);
178   state->height = static_cast<int>(h);
179 
180   // The following png_set_* calls have to be done in the order dictated by
181   // the libpng docs. Please take care if you have to move any of them. This
182   // is also why certain things are done outside of the switch, even though
183   // they look like they belong there.
184 
185   // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
186   if (color_type == PNG_COLOR_TYPE_PALETTE ||
187       (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
188     png_set_expand(png_ptr);
189 
190   // The '!= 0' is for silencing a Windows compiler warning.
191   bool input_has_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
192 
193   // Transparency for paletted images.
194   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
195     png_set_expand(png_ptr);
196     input_has_alpha = true;
197   }
198 
199   // Convert 16-bit to 8-bit.
200   if (bit_depth == 16)
201     png_set_strip_16(png_ptr);
202 
203   // Pick our row format converter necessary for this data.
204   if (!input_has_alpha) {
205     switch (state->output_format) {
206       case PNGCodec::FORMAT_RGB:
207         state->output_channels = 3;
208         break;
209       case PNGCodec::FORMAT_RGBA:
210         state->output_channels = 4;
211         png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
212         break;
213       case PNGCodec::FORMAT_BGRA:
214         state->output_channels = 4;
215         png_set_bgr(png_ptr);
216         png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
217         break;
218       case PNGCodec::FORMAT_SkBitmap:
219         state->output_channels = 4;
220         png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
221         break;
222     }
223   } else {
224     switch (state->output_format) {
225       case PNGCodec::FORMAT_RGB:
226         state->output_channels = 3;
227         png_set_strip_alpha(png_ptr);
228         break;
229       case PNGCodec::FORMAT_RGBA:
230         state->output_channels = 4;
231         break;
232       case PNGCodec::FORMAT_BGRA:
233         state->output_channels = 4;
234         png_set_bgr(png_ptr);
235         break;
236       case PNGCodec::FORMAT_SkBitmap:
237         state->output_channels = 4;
238         break;
239     }
240   }
241 
242   // Expand grayscale to RGB.
243   if (color_type == PNG_COLOR_TYPE_GRAY ||
244       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
245     png_set_gray_to_rgb(png_ptr);
246 
247   // Deal with gamma and keep it under our control.
248   double gamma;
249   if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
250     if (gamma <= 0.0 || gamma > kMaxGamma) {
251       gamma = kInverseGamma;
252       png_set_gAMA(png_ptr, info_ptr, gamma);
253     }
254     png_set_gamma(png_ptr, kDefaultGamma, gamma);
255   } else {
256     png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
257   }
258 
259   // Setting the user transforms here (as opposed to inside the switch above)
260   // because all png_set_* calls need to be done in the specific order
261   // mandated by libpng.
262   if (state->output_format == PNGCodec::FORMAT_SkBitmap) {
263     png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia);
264     png_set_user_transform_info(png_ptr, state, 0, 0);
265   }
266 
267   // Tell libpng to send us rows for interlaced pngs.
268   if (interlace_type == PNG_INTERLACE_ADAM7)
269     png_set_interlace_handling(png_ptr);
270 
271   png_read_update_info(png_ptr, info_ptr);
272 
273   if (state->bitmap) {
274     state->bitmap->allocN32Pixels(state->width, state->height);
275   } else if (state->output) {
276     state->output->resize(
277         state->width * state->output_channels * state->height);
278   }
279 }
280 
DecodeRowCallback(png_struct * png_ptr,png_byte * new_row,png_uint_32 row_num,int pass)281 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
282                        png_uint_32 row_num, int pass) {
283   if (!new_row)
284     return;  // Interlaced image; row didn't change this pass.
285 
286   PngDecoderState* state = static_cast<PngDecoderState*>(
287       png_get_progressive_ptr(png_ptr));
288 
289   if (static_cast<int>(row_num) > state->height) {
290     NOTREACHED() << "Invalid row";
291     return;
292   }
293 
294   unsigned char* base = NULL;
295   if (state->bitmap)
296     base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0));
297   else if (state->output)
298     base = &state->output->front();
299 
300   unsigned char* dest = &base[state->width * state->output_channels * row_num];
301   png_progressive_combine_row(png_ptr, dest, new_row);
302 }
303 
DecodeEndCallback(png_struct * png_ptr,png_info * info)304 void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
305   PngDecoderState* state = static_cast<PngDecoderState*>(
306       png_get_progressive_ptr(png_ptr));
307 
308   // Mark the image as complete, this will tell the Decode function that we
309   // have successfully found the end of the data.
310   state->done = true;
311 }
312 
313 // Automatically destroys the given read structs on destruction to make
314 // cleanup and error handling code cleaner.
315 class PngReadStructDestroyer {
316  public:
PngReadStructDestroyer(png_struct ** ps,png_info ** pi)317   PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
318   }
~PngReadStructDestroyer()319   ~PngReadStructDestroyer() {
320     png_destroy_read_struct(ps_, pi_, NULL);
321   }
322  private:
323   png_struct** ps_;
324   png_info** pi_;
325   DISALLOW_COPY_AND_ASSIGN(PngReadStructDestroyer);
326 };
327 
328 // Automatically destroys the given write structs on destruction to make
329 // cleanup and error handling code cleaner.
330 class PngWriteStructDestroyer {
331  public:
PngWriteStructDestroyer(png_struct ** ps)332   explicit PngWriteStructDestroyer(png_struct** ps) : ps_(ps), pi_(0) {
333   }
~PngWriteStructDestroyer()334   ~PngWriteStructDestroyer() {
335     png_destroy_write_struct(ps_, pi_);
336   }
SetInfoStruct(png_info ** pi)337   void SetInfoStruct(png_info** pi) {
338     pi_ = pi;
339   }
340  private:
341   png_struct** ps_;
342   png_info** pi_;
343   DISALLOW_COPY_AND_ASSIGN(PngWriteStructDestroyer);
344 };
345 
BuildPNGStruct(const unsigned char * input,size_t input_size,png_struct ** png_ptr,png_info ** info_ptr)346 bool BuildPNGStruct(const unsigned char* input, size_t input_size,
347                     png_struct** png_ptr, png_info** info_ptr) {
348   if (input_size < 8)
349     return false;  // Input data too small to be a png
350 
351   // Have libpng check the signature, it likes the first 8 bytes.
352   if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
353     return false;
354 
355   *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
356   if (!*png_ptr)
357     return false;
358 
359   *info_ptr = png_create_info_struct(*png_ptr);
360   if (!*info_ptr) {
361     png_destroy_read_struct(png_ptr, NULL, NULL);
362     return false;
363   }
364 
365   return true;
366 }
367 
368 // Libpng user error and warning functions which allows us to print libpng
369 // errors and warnings using Chrome's logging facilities instead of stderr.
370 
LogLibPNGDecodeError(png_structp png_ptr,png_const_charp error_msg)371 void LogLibPNGDecodeError(png_structp png_ptr, png_const_charp error_msg) {
372   DLOG(ERROR) << "libpng decode error: " << error_msg;
373   longjmp(png_jmpbuf(png_ptr), 1);
374 }
375 
LogLibPNGDecodeWarning(png_structp png_ptr,png_const_charp warning_msg)376 void LogLibPNGDecodeWarning(png_structp png_ptr, png_const_charp warning_msg) {
377   DLOG(ERROR) << "libpng decode warning: " << warning_msg;
378 }
379 
LogLibPNGEncodeError(png_structp png_ptr,png_const_charp error_msg)380 void LogLibPNGEncodeError(png_structp png_ptr, png_const_charp error_msg) {
381   DLOG(ERROR) << "libpng encode error: " << error_msg;
382   longjmp(png_jmpbuf(png_ptr), 1);
383 }
384 
LogLibPNGEncodeWarning(png_structp png_ptr,png_const_charp warning_msg)385 void LogLibPNGEncodeWarning(png_structp png_ptr, png_const_charp warning_msg) {
386   DLOG(ERROR) << "libpng encode warning: " << warning_msg;
387 }
388 
389 }  // namespace
390 
391 // static
Decode(const unsigned char * input,size_t input_size,ColorFormat format,std::vector<unsigned char> * output,int * w,int * h)392 bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
393                       ColorFormat format, std::vector<unsigned char>* output,
394                       int* w, int* h) {
395   png_struct* png_ptr = NULL;
396   png_info* info_ptr = NULL;
397   if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
398     return false;
399 
400   PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
401   if (setjmp(png_jmpbuf(png_ptr))) {
402     // The destroyer will ensure that the structures are cleaned up in this
403     // case, even though we may get here as a jump from random parts of the
404     // PNG library called below.
405     return false;
406   }
407 
408   PngDecoderState state(format, output);
409 
410   png_set_error_fn(png_ptr, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning);
411   png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
412                               &DecodeRowCallback, &DecodeEndCallback);
413   png_process_data(png_ptr,
414                    info_ptr,
415                    const_cast<unsigned char*>(input),
416                    input_size);
417 
418   if (!state.done) {
419     // Fed it all the data but the library didn't think we got all the data, so
420     // this file must be truncated.
421     output->clear();
422     return false;
423   }
424 
425   *w = state.width;
426   *h = state.height;
427   return true;
428 }
429 
430 // static
Decode(const unsigned char * input,size_t input_size,SkBitmap * bitmap)431 bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
432                       SkBitmap* bitmap) {
433   DCHECK(bitmap);
434   png_struct* png_ptr = NULL;
435   png_info* info_ptr = NULL;
436   if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
437     return false;
438 
439   PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
440   if (setjmp(png_jmpbuf(png_ptr))) {
441     // The destroyer will ensure that the structures are cleaned up in this
442     // case, even though we may get here as a jump from random parts of the
443     // PNG library called below.
444     return false;
445   }
446 
447   PngDecoderState state(bitmap);
448 
449   png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
450                               &DecodeRowCallback, &DecodeEndCallback);
451   png_process_data(png_ptr,
452                    info_ptr,
453                    const_cast<unsigned char*>(input),
454                    input_size);
455 
456   if (!state.done) {
457     return false;
458   }
459 
460   // Set the bitmap's opaqueness based on what we saw.
461   bitmap->setAlphaType(state.is_opaque ?
462                        kOpaque_SkAlphaType : kPremul_SkAlphaType);
463 
464   return true;
465 }
466 
467 // Encoder --------------------------------------------------------------------
468 //
469 // This section of the code is based on nsPNGEncoder.cpp in Mozilla
470 // (Copyright 2005 Google Inc.)
471 
472 namespace {
473 
474 // Passed around as the io_ptr in the png structs so our callbacks know where
475 // to write data.
476 struct PngEncoderState {
PngEncoderStategfx::__anon815ae2e60311::PngEncoderState477   explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
478   std::vector<unsigned char>* out;
479 };
480 
481 // Called by libpng to flush its internal buffer to ours.
EncoderWriteCallback(png_structp png,png_bytep data,png_size_t size)482 void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
483   PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
484   DCHECK(state->out);
485 
486   size_t old_size = state->out->size();
487   state->out->resize(old_size + size);
488   memcpy(&(*state->out)[old_size], data, size);
489 }
490 
FakeFlushCallback(png_structp png)491 void FakeFlushCallback(png_structp png) {
492   // We don't need to perform any flushing since we aren't doing real IO, but
493   // we're required to provide this function by libpng.
494 }
495 
ConvertBGRAtoRGB(const unsigned char * bgra,int pixel_width,unsigned char * rgb,bool * is_opaque)496 void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
497                       unsigned char* rgb, bool* is_opaque) {
498   for (int x = 0; x < pixel_width; x++) {
499     const unsigned char* pixel_in = &bgra[x * 4];
500     unsigned char* pixel_out = &rgb[x * 3];
501     pixel_out[0] = pixel_in[2];
502     pixel_out[1] = pixel_in[1];
503     pixel_out[2] = pixel_in[0];
504   }
505 }
506 
507 #ifdef PNG_TEXT_SUPPORTED
508 class CommentWriter {
509  public:
CommentWriter(const std::vector<PNGCodec::Comment> & comments)510   explicit CommentWriter(const std::vector<PNGCodec::Comment>& comments)
511       : comments_(comments),
512         png_text_(new png_text[comments.size()]) {
513     for (size_t i = 0; i < comments.size(); ++i)
514       AddComment(i, comments[i]);
515   }
516 
~CommentWriter()517   ~CommentWriter() {
518     for (size_t i = 0; i < comments_.size(); ++i) {
519       free(png_text_[i].key);
520       free(png_text_[i].text);
521     }
522     delete [] png_text_;
523   }
524 
HasComments()525   bool HasComments() {
526     return !comments_.empty();
527   }
528 
get_png_text()529   png_text* get_png_text() {
530     return png_text_;
531   }
532 
size()533   int size() {
534     return static_cast<int>(comments_.size());
535   }
536 
537  private:
AddComment(size_t pos,const PNGCodec::Comment & comment)538   void AddComment(size_t pos, const PNGCodec::Comment& comment) {
539     png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
540     // A PNG comment's key can only be 79 characters long.
541     DCHECK(comment.key.length() < 79);
542     png_text_[pos].key = base::strdup(comment.key.substr(0, 78).c_str());
543     png_text_[pos].text = base::strdup(comment.text.c_str());
544     png_text_[pos].text_length = comment.text.length();
545 #ifdef PNG_iTXt_SUPPORTED
546     png_text_[pos].itxt_length = 0;
547     png_text_[pos].lang = 0;
548     png_text_[pos].lang_key = 0;
549 #endif
550   }
551 
552   DISALLOW_COPY_AND_ASSIGN(CommentWriter);
553 
554   const std::vector<PNGCodec::Comment> comments_;
555   png_text* png_text_;
556 };
557 #endif  // PNG_TEXT_SUPPORTED
558 
559 // The type of functions usable for converting between pixel formats.
560 typedef void (*FormatConverter)(const unsigned char* in, int w,
561                                 unsigned char* out, bool* is_opaque);
562 
563 // libpng uses a wacky setjmp-based API, which makes the compiler nervous.
564 // We constrain all of the calls we make to libpng where the setjmp() is in
565 // place to this function.
566 // Returns true on success.
DoLibpngWrite(png_struct * png_ptr,png_info * info_ptr,PngEncoderState * state,int width,int height,int row_byte_width,const unsigned char * input,int compression_level,int png_output_color_type,int output_color_components,FormatConverter converter,const std::vector<PNGCodec::Comment> & comments)567 bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
568                    PngEncoderState* state,
569                    int width, int height, int row_byte_width,
570                    const unsigned char* input, int compression_level,
571                    int png_output_color_type, int output_color_components,
572                    FormatConverter converter,
573                    const std::vector<PNGCodec::Comment>& comments) {
574 #ifdef PNG_TEXT_SUPPORTED
575   CommentWriter comment_writer(comments);
576 #endif
577   unsigned char* row_buffer = NULL;
578 
579   // Make sure to not declare any locals here -- locals in the presence
580   // of setjmp() in C++ code makes gcc complain.
581 
582   if (setjmp(png_jmpbuf(png_ptr))) {
583     delete[] row_buffer;
584     return false;
585   }
586 
587   png_set_compression_level(png_ptr, compression_level);
588 
589   // Set our callback for libpng to give us the data.
590   png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
591   png_set_error_fn(png_ptr, NULL, LogLibPNGEncodeError, LogLibPNGEncodeWarning);
592 
593   png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
594                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
595                PNG_FILTER_TYPE_DEFAULT);
596 
597 #ifdef PNG_TEXT_SUPPORTED
598   if (comment_writer.HasComments()) {
599     png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(),
600                  comment_writer.size());
601   }
602 #endif
603 
604   png_write_info(png_ptr, info_ptr);
605 
606   if (!converter) {
607     // No conversion needed, give the data directly to libpng.
608     for (int y = 0; y < height; y ++) {
609       png_write_row(png_ptr,
610                     const_cast<unsigned char*>(&input[y * row_byte_width]));
611     }
612   } else {
613     // Needs conversion using a separate buffer.
614     row_buffer = new unsigned char[width * output_color_components];
615     for (int y = 0; y < height; y ++) {
616       converter(&input[y * row_byte_width], width, row_buffer, NULL);
617       png_write_row(png_ptr, row_buffer);
618     }
619     delete[] row_buffer;
620   }
621 
622   png_write_end(png_ptr, info_ptr);
623   return true;
624 }
625 
EncodeWithCompressionLevel(const unsigned char * input,PNGCodec::ColorFormat format,const Size & size,int row_byte_width,bool discard_transparency,const std::vector<PNGCodec::Comment> & comments,int compression_level,std::vector<unsigned char> * output)626 bool EncodeWithCompressionLevel(const unsigned char* input,
627                                 PNGCodec::ColorFormat format,
628                                 const Size& size,
629                                 int row_byte_width,
630                                 bool discard_transparency,
631                                 const std::vector<PNGCodec::Comment>& comments,
632                                 int compression_level,
633                                 std::vector<unsigned char>* output) {
634   // Run to convert an input row into the output row format, NULL means no
635   // conversion is necessary.
636   FormatConverter converter = NULL;
637 
638   int input_color_components, output_color_components;
639   int png_output_color_type;
640   switch (format) {
641     case PNGCodec::FORMAT_RGB:
642       input_color_components = 3;
643       output_color_components = 3;
644       png_output_color_type = PNG_COLOR_TYPE_RGB;
645       break;
646 
647     case PNGCodec::FORMAT_RGBA:
648       input_color_components = 4;
649       if (discard_transparency) {
650         output_color_components = 3;
651         png_output_color_type = PNG_COLOR_TYPE_RGB;
652         converter = ConvertRGBAtoRGB;
653       } else {
654         output_color_components = 4;
655         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
656         converter = NULL;
657       }
658       break;
659 
660     case PNGCodec::FORMAT_BGRA:
661       input_color_components = 4;
662       if (discard_transparency) {
663         output_color_components = 3;
664         png_output_color_type = PNG_COLOR_TYPE_RGB;
665         converter = ConvertBGRAtoRGB;
666       } else {
667         output_color_components = 4;
668         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
669         converter = ConvertBetweenBGRAandRGBA;
670       }
671       break;
672 
673     case PNGCodec::FORMAT_SkBitmap:
674       // Compare row_byte_width and size.width() to detect the format of
675       // SkBitmap. kA8_Config (1bpp) and kARGB_8888_Config (4bpp) are the two
676       // supported formats.
677       if (row_byte_width < 4 * size.width()) {
678         // Not 4bpp, so must be 1bpp.
679         // Ignore discard_transparency - it doesn't make sense in this context,
680         // since alpha is the only thing we have and it needs to be used for
681         // color intensity.
682         input_color_components = 1;
683         output_color_components = 1;
684         png_output_color_type = PNG_COLOR_TYPE_GRAY;
685         // |converter| is left as null
686       } else {
687         input_color_components = 4;
688         if (discard_transparency) {
689           output_color_components = 3;
690           png_output_color_type = PNG_COLOR_TYPE_RGB;
691           converter = ConvertSkiaToRGB;
692         } else {
693           output_color_components = 4;
694           png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
695           converter = ConvertSkiaToRGBA;
696         }
697       }
698       break;
699 
700     default:
701       NOTREACHED() << "Unknown pixel format";
702       return false;
703   }
704 
705   // Row stride should be at least as long as the length of the data.
706   DCHECK(input_color_components * size.width() <= row_byte_width);
707 
708   png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
709                                                 NULL, NULL, NULL);
710   if (!png_ptr)
711     return false;
712   PngWriteStructDestroyer destroyer(&png_ptr);
713   png_info* info_ptr = png_create_info_struct(png_ptr);
714   if (!info_ptr)
715     return false;
716   destroyer.SetInfoStruct(&info_ptr);
717 
718   output->clear();
719 
720   PngEncoderState state(output);
721   bool success = DoLibpngWrite(png_ptr, info_ptr, &state,
722                                size.width(), size.height(), row_byte_width,
723                                input, compression_level, png_output_color_type,
724                                output_color_components, converter, comments);
725 
726   return success;
727 }
728 
InternalEncodeSkBitmap(const SkBitmap & input,bool discard_transparency,int compression_level,std::vector<unsigned char> * output)729 bool InternalEncodeSkBitmap(const SkBitmap& input,
730                             bool discard_transparency,
731                             int compression_level,
732                             std::vector<unsigned char>* output) {
733   if (input.empty() || input.isNull())
734     return false;
735   int bpp = input.bytesPerPixel();
736   DCHECK(bpp == 1 || bpp == 4);  // We support kA8_Config and kARGB_8888_Config.
737 
738   SkAutoLockPixels lock_input(input);
739   unsigned char* inputAddr = bpp == 1 ?
740       reinterpret_cast<unsigned char*>(input.getAddr8(0, 0)) :
741       reinterpret_cast<unsigned char*>(input.getAddr32(0, 0));    // bpp = 4
742   return EncodeWithCompressionLevel(
743       inputAddr,
744       PNGCodec::FORMAT_SkBitmap,
745       Size(input.width(), input.height()),
746       static_cast<int>(input.rowBytes()),
747       discard_transparency,
748       std::vector<PNGCodec::Comment>(),
749       compression_level,
750       output);
751 }
752 
753 
754 }  // namespace
755 
756 // 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)757 bool PNGCodec::Encode(const unsigned char* input,
758                       ColorFormat format,
759                       const Size& size,
760                       int row_byte_width,
761                       bool discard_transparency,
762                       const std::vector<Comment>& comments,
763                       std::vector<unsigned char>* output) {
764   return EncodeWithCompressionLevel(input,
765                                     format,
766                                     size,
767                                     row_byte_width,
768                                     discard_transparency,
769                                     comments,
770                                     Z_DEFAULT_COMPRESSION,
771                                     output);
772 }
773 
774 // static
EncodeBGRASkBitmap(const SkBitmap & input,bool discard_transparency,std::vector<unsigned char> * output)775 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input,
776                                   bool discard_transparency,
777                                   std::vector<unsigned char>* output) {
778   return InternalEncodeSkBitmap(input,
779                                 discard_transparency,
780                                 Z_DEFAULT_COMPRESSION,
781                                 output);
782 }
783 
784 // static
EncodeA8SkBitmap(const SkBitmap & input,std::vector<unsigned char> * output)785 bool PNGCodec::EncodeA8SkBitmap(const SkBitmap& input,
786                                 std::vector<unsigned char>* output) {
787   return InternalEncodeSkBitmap(input,
788                                 false,
789                                 Z_DEFAULT_COMPRESSION,
790                                 output);
791 }
792 
793 // static
FastEncodeBGRASkBitmap(const SkBitmap & input,bool discard_transparency,std::vector<unsigned char> * output)794 bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input,
795                                       bool discard_transparency,
796                                       std::vector<unsigned char>* output) {
797   return InternalEncodeSkBitmap(input,
798                                 discard_transparency,
799                                 Z_BEST_SPEED,
800                                 output);
801 }
802 
Comment(const std::string & k,const std::string & t)803 PNGCodec::Comment::Comment(const std::string& k, const std::string& t)
804     : key(k), text(t) {
805 }
806 
~Comment()807 PNGCodec::Comment::~Comment() {
808 }
809 
810 }  // namespace gfx
811