• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 // This is a duplicate of chromium's src/tools/imagediff/image_diff_png.cc
6 // that has been modified to build in a pdfium environment, which itself
7 // was duplicated as follows:
8 
9 // This is a duplicate of ui/gfx/codec/png_codec.cc, after removing code related
10 // to Skia, that we can use when running layout tests with minimal dependencies.
11 
12 #include "testing/image_diff/image_diff_png.h"
13 
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <string>
18 
19 #include "third_party/base/logging.h"
20 #include "third_party/zlib/zlib.h"
21 
22 #ifdef USE_SYSTEM_LIBPNG
23 #include <png.h>
24 #else
25 #include "third_party/libpng16/png.h"
26 #endif
27 
28 namespace image_diff_png {
29 
30 namespace {
31 
32 enum ColorFormat {
33   // 3 bytes per pixel (packed), in RGB order regardless of endianness.
34   // This is the native JPEG format.
35   FORMAT_RGB,
36 
37   // 3 bytes per pixel, in BGR order regardless of endianness.
38   FORMAT_BGR,
39 
40   // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
41   FORMAT_RGBA,
42 
43   // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
44   // This is the default Windows DIB order.
45   FORMAT_BGRA,
46 
47   // 1 byte per pixel.
48   FORMAT_GRAY,
49 };
50 
51 // Represents a comment in the tEXt ancillary chunk of the png.
52 struct Comment {
53   std::string key;
54   std::string text;
55 };
56 
57 // Converts BGRA->RGBA and RGBA->BGRA.
ConvertBetweenBGRAandRGBA(const unsigned char * input,int pixel_width,unsigned char * output,bool * is_opaque)58 void ConvertBetweenBGRAandRGBA(const unsigned char* input,
59                                int pixel_width,
60                                unsigned char* output,
61                                bool* is_opaque) {
62   for (int x = 0; x < pixel_width; x++) {
63     const unsigned char* pixel_in = &input[x * 4];
64     unsigned char* pixel_out = &output[x * 4];
65     pixel_out[0] = pixel_in[2];
66     pixel_out[1] = pixel_in[1];
67     pixel_out[2] = pixel_in[0];
68     pixel_out[3] = pixel_in[3];
69   }
70 }
71 
ConvertBGRtoRGB(const unsigned char * bgr,int pixel_width,unsigned char * rgb,bool * is_opaque)72 void ConvertBGRtoRGB(const unsigned char* bgr,
73                      int pixel_width,
74                      unsigned char* rgb,
75                      bool* is_opaque) {
76   for (int x = 0; x < pixel_width; x++) {
77     const unsigned char* pixel_in = &bgr[x * 3];
78     unsigned char* pixel_out = &rgb[x * 3];
79     pixel_out[0] = pixel_in[2];
80     pixel_out[1] = pixel_in[1];
81     pixel_out[2] = pixel_in[0];
82   }
83 }
84 
ConvertRGBAtoRGB(const unsigned char * rgba,int pixel_width,unsigned char * rgb,bool * is_opaque)85 void ConvertRGBAtoRGB(const unsigned char* rgba,
86                       int pixel_width,
87                       unsigned char* rgb,
88                       bool* is_opaque) {
89   for (int x = 0; x < pixel_width; x++) {
90     const unsigned char* pixel_in = &rgba[x * 4];
91     unsigned char* pixel_out = &rgb[x * 3];
92     pixel_out[0] = pixel_in[0];
93     pixel_out[1] = pixel_in[1];
94     pixel_out[2] = pixel_in[2];
95   }
96 }
97 
98 }  // namespace
99 
100 // Decoder
101 //
102 // This code is based on WebKit libpng interface (PNGImageDecoder), which is
103 // in turn based on the Mozilla png decoder.
104 
105 namespace {
106 
107 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
108 const double kMaxGamma = 21474.83;  // Maximum gamma accepted by png library.
109 const double kDefaultGamma = 2.2;
110 const double kInverseGamma = 1.0 / kDefaultGamma;
111 
112 class PngDecoderState {
113  public:
114   // Output is a vector<unsigned char>.
PngDecoderState(ColorFormat ofmt,std::vector<unsigned char> * o)115   PngDecoderState(ColorFormat ofmt, std::vector<unsigned char>* o)
116       : output_format(ofmt),
117         output_channels(0),
118         is_opaque(true),
119         output(o),
120         row_converter(nullptr),
121         width(0),
122         height(0),
123         done(false) {}
124 
125   ColorFormat output_format;
126   int output_channels;
127 
128   // Used during the reading of an SkBitmap. Defaults to true until we see a
129   // pixel with anything other than an alpha of 255.
130   bool is_opaque;
131 
132   // An intermediary buffer for decode output.
133   std::vector<unsigned char>* output;
134 
135   // Called to convert a row from the library to the correct output format.
136   // When NULL, no conversion is necessary.
137   void (*row_converter)(const unsigned char* in,
138                         int w,
139                         unsigned char* out,
140                         bool* is_opaque);
141 
142   // Size of the image, set in the info callback.
143   int width;
144   int height;
145 
146   // Set to true when we've found the end of the data.
147   bool done;
148 };
149 
ConvertRGBtoRGBA(const unsigned char * rgb,int pixel_width,unsigned char * rgba,bool * is_opaque)150 void ConvertRGBtoRGBA(const unsigned char* rgb,
151                       int pixel_width,
152                       unsigned char* rgba,
153                       bool* is_opaque) {
154   for (int x = 0; x < pixel_width; x++) {
155     const unsigned char* pixel_in = &rgb[x * 3];
156     unsigned char* pixel_out = &rgba[x * 4];
157     pixel_out[0] = pixel_in[0];
158     pixel_out[1] = pixel_in[1];
159     pixel_out[2] = pixel_in[2];
160     pixel_out[3] = 0xff;
161   }
162 }
163 
ConvertRGBtoBGRA(const unsigned char * rgb,int pixel_width,unsigned char * bgra,bool * is_opaque)164 void ConvertRGBtoBGRA(const unsigned char* rgb,
165                       int pixel_width,
166                       unsigned char* bgra,
167                       bool* is_opaque) {
168   for (int x = 0; x < pixel_width; x++) {
169     const unsigned char* pixel_in = &rgb[x * 3];
170     unsigned char* pixel_out = &bgra[x * 4];
171     pixel_out[0] = pixel_in[2];
172     pixel_out[1] = pixel_in[1];
173     pixel_out[2] = pixel_in[0];
174     pixel_out[3] = 0xff;
175   }
176 }
177 
178 // Called when the png header has been read. This code is based on the WebKit
179 // PNGImageDecoder
DecodeInfoCallback(png_struct * png_ptr,png_info * info_ptr)180 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
181   PngDecoderState* state =
182       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
183 
184   int bit_depth, color_type, interlace_type, compression_type;
185   int filter_type, channels;
186   png_uint_32 w, h;
187   png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
188                &interlace_type, &compression_type, &filter_type);
189 
190   // Bounds check. When the image is unreasonably big, we'll error out and
191   // end up back at the setjmp call when we set up decoding.  "Unreasonably big"
192   // means "big enough that w * h * 32bpp might overflow an int"; we choose this
193   // threshold to match WebKit and because a number of places in code assume
194   // that an image's size (in bytes) fits in a (signed) int.
195   unsigned long long total_size =
196       static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
197   if (total_size > ((1 << 29) - 1))
198     longjmp(png_jmpbuf(png_ptr), 1);
199   state->width = static_cast<int>(w);
200   state->height = static_cast<int>(h);
201 
202   // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
203   if (color_type == PNG_COLOR_TYPE_PALETTE ||
204       (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
205     png_set_expand(png_ptr);
206 
207   // Transparency for paletted images.
208   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
209     png_set_expand(png_ptr);
210 
211   // Convert 16-bit to 8-bit.
212   if (bit_depth == 16)
213     png_set_strip_16(png_ptr);
214 
215   // Expand grayscale to RGB.
216   if (color_type == PNG_COLOR_TYPE_GRAY ||
217       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
218     png_set_gray_to_rgb(png_ptr);
219 
220   // Deal with gamma and keep it under our control.
221   double gamma;
222   if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
223     if (gamma <= 0.0 || gamma > kMaxGamma) {
224       gamma = kInverseGamma;
225       png_set_gAMA(png_ptr, info_ptr, gamma);
226     }
227     png_set_gamma(png_ptr, kDefaultGamma, gamma);
228   } else {
229     png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
230   }
231 
232   // Tell libpng to send us rows for interlaced pngs.
233   if (interlace_type == PNG_INTERLACE_ADAM7)
234     png_set_interlace_handling(png_ptr);
235 
236   // Update our info now
237   png_read_update_info(png_ptr, info_ptr);
238   channels = png_get_channels(png_ptr, info_ptr);
239 
240   // Pick our row format converter necessary for this data.
241   if (channels == 3) {
242     switch (state->output_format) {
243       case FORMAT_RGB:
244         state->row_converter = nullptr;  // no conversion necessary
245         state->output_channels = 3;
246         break;
247       case FORMAT_RGBA:
248         state->row_converter = &ConvertRGBtoRGBA;
249         state->output_channels = 4;
250         break;
251       case FORMAT_BGRA:
252         state->row_converter = &ConvertRGBtoBGRA;
253         state->output_channels = 4;
254         break;
255       case FORMAT_GRAY:
256         state->row_converter = nullptr;
257         state->output_channels = 1;
258         break;
259       default:
260         NOTREACHED();
261         break;
262     }
263   } else if (channels == 4) {
264     switch (state->output_format) {
265       case FORMAT_RGB:
266         state->row_converter = &ConvertRGBAtoRGB;
267         state->output_channels = 3;
268         break;
269       case FORMAT_RGBA:
270         state->row_converter = nullptr;  // no conversion necessary
271         state->output_channels = 4;
272         break;
273       case FORMAT_BGRA:
274         state->row_converter = &ConvertBetweenBGRAandRGBA;
275         state->output_channels = 4;
276         break;
277       default:
278         NOTREACHED();
279         break;
280     }
281   } else {
282     NOTREACHED();
283     longjmp(png_jmpbuf(png_ptr), 1);
284   }
285 
286   state->output->resize(state->width * state->output_channels * state->height);
287 }
288 
DecodeRowCallback(png_struct * png_ptr,png_byte * new_row,png_uint_32 row_num,int pass)289 void DecodeRowCallback(png_struct* png_ptr,
290                        png_byte* new_row,
291                        png_uint_32 row_num,
292                        int pass) {
293   PngDecoderState* state =
294       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
295 
296   if (static_cast<int>(row_num) > state->height) {
297     NOTREACHED();
298     return;
299   }
300 
301   unsigned char* base = NULL;
302   base = &state->output->front();
303 
304   unsigned char* dest = &base[state->width * state->output_channels * row_num];
305   if (state->row_converter)
306     state->row_converter(new_row, state->width, dest, &state->is_opaque);
307   else
308     memcpy(dest, new_row, state->width * state->output_channels);
309 }
310 
DecodeEndCallback(png_struct * png_ptr,png_info * info)311 void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
312   PngDecoderState* state =
313       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
314 
315   // Mark the image as complete, this will tell the Decode function that we
316   // have successfully found the end of the data.
317   state->done = true;
318 }
319 
320 // Automatically destroys the given read structs on destruction to make
321 // cleanup and error handling code cleaner.
322 class PngReadStructDestroyer {
323  public:
PngReadStructDestroyer(png_struct ** ps,png_info ** pi)324   PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {}
~PngReadStructDestroyer()325   ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_, NULL); }
326 
327  private:
328   png_struct** ps_;
329   png_info** pi_;
330 };
331 
BuildPNGStruct(const unsigned char * input,size_t input_size,png_struct ** png_ptr,png_info ** info_ptr)332 bool BuildPNGStruct(const unsigned char* input,
333                     size_t input_size,
334                     png_struct** png_ptr,
335                     png_info** info_ptr) {
336   if (input_size < 8)
337     return false;  // Input data too small to be a png
338 
339   // Have libpng check the signature, it likes the first 8 bytes.
340   if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
341     return false;
342 
343   *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
344   if (!*png_ptr)
345     return false;
346 
347   *info_ptr = png_create_info_struct(*png_ptr);
348   if (!*info_ptr) {
349     png_destroy_read_struct(png_ptr, NULL, NULL);
350     return false;
351   }
352 
353   return true;
354 }
355 
356 }  // namespace
357 
358 // static
Decode(const unsigned char * input,size_t input_size,ColorFormat format,std::vector<unsigned char> * output,int * w,int * h)359 bool Decode(const unsigned char* input,
360             size_t input_size,
361             ColorFormat format,
362             std::vector<unsigned char>* output,
363             int* w,
364             int* h) {
365   png_struct* png_ptr = NULL;
366   png_info* info_ptr = NULL;
367   if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
368     return false;
369 
370   PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
371   if (setjmp(png_jmpbuf(png_ptr))) {
372     // The destroyer will ensure that the structures are cleaned up in this
373     // case, even though we may get here as a jump from random parts of the
374     // PNG library called below.
375     return false;
376   }
377 
378   PngDecoderState state(format, output);
379 
380   png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
381                               &DecodeRowCallback, &DecodeEndCallback);
382   png_process_data(png_ptr, info_ptr, const_cast<unsigned char*>(input),
383                    input_size);
384 
385   if (!state.done) {
386     // Fed it all the data but the library didn't think we got all the data, so
387     // this file must be truncated.
388     output->clear();
389     return false;
390   }
391 
392   *w = state.width;
393   *h = state.height;
394   return true;
395 }
396 
397 // Encoder
398 //
399 // This section of the code is based on nsPNGEncoder.cpp in Mozilla
400 // (Copyright 2005 Google Inc.)
401 
402 namespace {
403 
404 // Passed around as the io_ptr in the png structs so our callbacks know where
405 // to write data.
406 struct PngEncoderState {
PngEncoderStateimage_diff_png::__anon39a3d9950311::PngEncoderState407   explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
408   std::vector<unsigned char>* out;
409 };
410 
411 // Called by libpng to flush its internal buffer to ours.
EncoderWriteCallback(png_structp png,png_bytep data,png_size_t size)412 void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
413   PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
414   size_t old_size = state->out->size();
415   state->out->resize(old_size + size);
416   memcpy(&(*state->out)[old_size], data, size);
417 }
418 
FakeFlushCallback(png_structp png)419 void FakeFlushCallback(png_structp png) {
420   // We don't need to perform any flushing since we aren't doing real IO, but
421   // we're required to provide this function by libpng.
422 }
423 
ConvertBGRAtoRGB(const unsigned char * bgra,int pixel_width,unsigned char * rgb,bool * is_opaque)424 void ConvertBGRAtoRGB(const unsigned char* bgra,
425                       int pixel_width,
426                       unsigned char* rgb,
427                       bool* is_opaque) {
428   for (int x = 0; x < pixel_width; x++) {
429     const unsigned char* pixel_in = &bgra[x * 4];
430     unsigned char* pixel_out = &rgb[x * 3];
431     pixel_out[0] = pixel_in[2];
432     pixel_out[1] = pixel_in[1];
433     pixel_out[2] = pixel_in[0];
434   }
435 }
436 
437 #ifdef PNG_TEXT_SUPPORTED
438 
strdup(const char * str)439 inline char* strdup(const char* str) {
440 #if defined(OS_WIN)
441   return _strdup(str);
442 #else
443   return ::strdup(str);
444 #endif
445 }
446 
447 class CommentWriter {
448  public:
CommentWriter(const std::vector<Comment> & comments)449   explicit CommentWriter(const std::vector<Comment>& comments)
450       : comments_(comments), png_text_(new png_text[comments.size()]) {
451     for (size_t i = 0; i < comments.size(); ++i)
452       AddComment(i, comments[i]);
453   }
454 
~CommentWriter()455   ~CommentWriter() {
456     for (size_t i = 0; i < comments_.size(); ++i) {
457       free(png_text_[i].key);
458       free(png_text_[i].text);
459     }
460     delete[] png_text_;
461   }
462 
HasComments()463   bool HasComments() { return !comments_.empty(); }
464 
get_png_text()465   png_text* get_png_text() { return png_text_; }
466 
size()467   int size() { return static_cast<int>(comments_.size()); }
468 
469  private:
AddComment(size_t pos,const Comment & comment)470   void AddComment(size_t pos, const Comment& comment) {
471     png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
472     // A PNG comment's key can only be 79 characters long.
473     if (comment.key.length() > 79)
474       return;
475     png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str());
476     png_text_[pos].text = strdup(comment.text.c_str());
477     png_text_[pos].text_length = comment.text.length();
478 #ifdef PNG_iTXt_SUPPORTED
479     png_text_[pos].itxt_length = 0;
480     png_text_[pos].lang = 0;
481     png_text_[pos].lang_key = 0;
482 #endif
483   }
484 
485   const std::vector<Comment> comments_;
486   png_text* png_text_;
487 };
488 #endif  // PNG_TEXT_SUPPORTED
489 
490 // The type of functions usable for converting between pixel formats.
491 typedef void (*FormatConverter)(const unsigned char* in,
492                                 int w,
493                                 unsigned char* out,
494                                 bool* is_opaque);
495 
496 // libpng uses a wacky setjmp-based API, which makes the compiler nervous.
497 // We constrain all of the calls we make to libpng where the setjmp() is in
498 // place to this function.
499 // 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<Comment> & comments)500 bool DoLibpngWrite(png_struct* png_ptr,
501                    png_info* info_ptr,
502                    PngEncoderState* state,
503                    int width,
504                    int height,
505                    int row_byte_width,
506                    const unsigned char* input,
507                    int compression_level,
508                    int png_output_color_type,
509                    int output_color_components,
510                    FormatConverter converter,
511                    const std::vector<Comment>& comments) {
512 #ifdef PNG_TEXT_SUPPORTED
513   CommentWriter comment_writer(comments);
514 #endif
515   unsigned char* row_buffer = NULL;
516 
517   // Make sure to not declare any locals here -- locals in the presence
518   // of setjmp() in C++ code makes gcc complain.
519 
520   if (setjmp(png_jmpbuf(png_ptr))) {
521     delete[] row_buffer;
522     return false;
523   }
524 
525   png_set_compression_level(png_ptr, compression_level);
526 
527   // Set our callback for libpng to give us the data.
528   png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
529 
530   png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
531                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
532                PNG_FILTER_TYPE_DEFAULT);
533 
534 #ifdef PNG_TEXT_SUPPORTED
535   if (comment_writer.HasComments()) {
536     png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(),
537                  comment_writer.size());
538   }
539 #endif
540 
541   png_write_info(png_ptr, info_ptr);
542 
543   if (!converter) {
544     // No conversion needed, give the data directly to libpng.
545     for (int y = 0; y < height; y++) {
546       png_write_row(png_ptr,
547                     const_cast<unsigned char*>(&input[y * row_byte_width]));
548     }
549   } else {
550     // Needs conversion using a separate buffer.
551     row_buffer = new unsigned char[width * output_color_components];
552     for (int y = 0; y < height; y++) {
553       converter(&input[y * row_byte_width], width, row_buffer, NULL);
554       png_write_row(png_ptr, row_buffer);
555     }
556     delete[] row_buffer;
557   }
558 
559   png_write_end(png_ptr, info_ptr);
560   return true;
561 }
562 
563 }  // namespace
564 
565 // static
EncodeWithCompressionLevel(const unsigned char * input,ColorFormat format,const int width,const int height,int row_byte_width,bool discard_transparency,const std::vector<Comment> & comments,int compression_level,std::vector<unsigned char> * output)566 bool EncodeWithCompressionLevel(const unsigned char* input,
567                                 ColorFormat format,
568                                 const int width,
569                                 const int height,
570                                 int row_byte_width,
571                                 bool discard_transparency,
572                                 const std::vector<Comment>& comments,
573                                 int compression_level,
574                                 std::vector<unsigned char>* output) {
575   // Run to convert an input row into the output row format, NULL means no
576   // conversion is necessary.
577   FormatConverter converter = nullptr;
578 
579   int input_color_components, output_color_components;
580   int png_output_color_type;
581   switch (format) {
582     case FORMAT_BGR:
583       converter = ConvertBGRtoRGB;
584 
585     case FORMAT_RGB:
586       input_color_components = 3;
587       output_color_components = 3;
588       png_output_color_type = PNG_COLOR_TYPE_RGB;
589       discard_transparency = false;
590       break;
591 
592     case FORMAT_RGBA:
593       input_color_components = 4;
594       if (discard_transparency) {
595         output_color_components = 3;
596         png_output_color_type = PNG_COLOR_TYPE_RGB;
597         converter = ConvertRGBAtoRGB;
598       } else {
599         output_color_components = 4;
600         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
601         converter = nullptr;
602       }
603       break;
604 
605     case FORMAT_BGRA:
606       input_color_components = 4;
607       if (discard_transparency) {
608         output_color_components = 3;
609         png_output_color_type = PNG_COLOR_TYPE_RGB;
610         converter = ConvertBGRAtoRGB;
611       } else {
612         output_color_components = 4;
613         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
614         converter = ConvertBetweenBGRAandRGBA;
615       }
616       break;
617 
618     case FORMAT_GRAY:
619       input_color_components = 1;
620       output_color_components = 1;
621       png_output_color_type = PNG_COLOR_TYPE_GRAY;
622       discard_transparency = false;
623       break;
624 
625     default:
626       NOTREACHED();
627       return false;
628   }
629 
630   // Row stride should be at least as long as the length of the data.
631   if (row_byte_width < input_color_components * width)
632     return false;
633 
634   png_struct* png_ptr =
635       png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
636   if (!png_ptr)
637     return false;
638   png_info* info_ptr = png_create_info_struct(png_ptr);
639   if (!info_ptr) {
640     png_destroy_write_struct(&png_ptr, NULL);
641     return false;
642   }
643 
644   PngEncoderState state(output);
645   bool success =
646       DoLibpngWrite(png_ptr, info_ptr, &state, width, height, row_byte_width,
647                     input, compression_level, png_output_color_type,
648                     output_color_components, converter, comments);
649   png_destroy_write_struct(&png_ptr, &info_ptr);
650 
651   return success;
652 }
653 
654 // static
Encode(const unsigned char * input,ColorFormat format,const int width,const int height,int row_byte_width,bool discard_transparency,const std::vector<Comment> & comments,std::vector<unsigned char> * output)655 bool Encode(const unsigned char* input,
656             ColorFormat format,
657             const int width,
658             const int height,
659             int row_byte_width,
660             bool discard_transparency,
661             const std::vector<Comment>& comments,
662             std::vector<unsigned char>* output) {
663   return EncodeWithCompressionLevel(input, format, width, height,
664                                     row_byte_width, discard_transparency,
665                                     comments, Z_DEFAULT_COMPRESSION, output);
666 }
667 
668 // Decode a PNG into an RGBA pixel array.
DecodePNG(const unsigned char * input,size_t input_size,std::vector<unsigned char> * output,int * width,int * height)669 bool DecodePNG(const unsigned char* input,
670                size_t input_size,
671                std::vector<unsigned char>* output,
672                int* width,
673                int* height) {
674   return Decode(input, input_size, FORMAT_RGBA, output, width, height);
675 }
676 
677 // Encode a BGR pixel array into a PNG.
EncodeBGRPNG(const unsigned char * input,int width,int height,int row_byte_width,std::vector<unsigned char> * output)678 bool EncodeBGRPNG(const unsigned char* input,
679                   int width,
680                   int height,
681                   int row_byte_width,
682                   std::vector<unsigned char>* output) {
683   return Encode(input, FORMAT_BGR, width, height, row_byte_width, false,
684                 std::vector<Comment>(), output);
685 }
686 
687 // Encode an RGBA pixel array into a PNG.
EncodeRGBAPNG(const unsigned char * input,int width,int height,int row_byte_width,std::vector<unsigned char> * output)688 bool EncodeRGBAPNG(const unsigned char* input,
689                    int width,
690                    int height,
691                    int row_byte_width,
692                    std::vector<unsigned char>* output) {
693   return Encode(input, FORMAT_RGBA, width, height, row_byte_width, false,
694                 std::vector<Comment>(), output);
695 }
696 
697 // Encode an BGRA pixel array into a PNG.
EncodeBGRAPNG(const unsigned char * input,int width,int height,int row_byte_width,bool discard_transparency,std::vector<unsigned char> * output)698 bool EncodeBGRAPNG(const unsigned char* input,
699                    int width,
700                    int height,
701                    int row_byte_width,
702                    bool discard_transparency,
703                    std::vector<unsigned char>* output) {
704   return Encode(input, FORMAT_BGRA, width, height, row_byte_width,
705                 discard_transparency, std::vector<Comment>(), output);
706 }
707 
708 // Encode a grayscale pixel array into a PNG.
EncodeGrayPNG(const unsigned char * input,int width,int height,int row_byte_width,std::vector<unsigned char> * output)709 bool EncodeGrayPNG(const unsigned char* input,
710                    int width,
711                    int height,
712                    int row_byte_width,
713                    std::vector<unsigned char>* output) {
714   return Encode(input, FORMAT_GRAY, width, height, row_byte_width, false,
715                 std::vector<Comment>(), output);
716 }
717 
718 }  // namespace image_diff_png
719