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