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