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