• 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/compiler_specific.h"
20 #include "third_party/base/logging.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/libpng16/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   for (int x = 0; x < pixel_width; x++) {
96     const uint8_t* pixel_in = &rgba[x * 4];
97     uint8_t* pixel_out = &rgb[x * 3];
98     pixel_out[0] = pixel_in[0];
99     pixel_out[1] = pixel_in[1];
100     pixel_out[2] = pixel_in[2];
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   for (int x = 0; x < pixel_width; x++) {
152     const uint8_t* pixel_in = &rgb[x * 3];
153     uint8_t* pixel_out = &rgba[x * 4];
154     pixel_out[0] = pixel_in[0];
155     pixel_out[1] = pixel_in[1];
156     pixel_out[2] = pixel_in[2];
157     pixel_out[3] = 0xff;
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();
258         break;
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();
276         break;
277     }
278   } else {
279     NOTREACHED();
280     longjmp(png_jmpbuf(png_ptr), 1);
281   }
282 
283   state->output->resize(state->width * state->output_channels * state->height);
284 }
285 
DecodeRowCallback(png_struct * png_ptr,png_byte * new_row,png_uint_32 row_num,int pass)286 void DecodeRowCallback(png_struct* png_ptr,
287                        png_byte* new_row,
288                        png_uint_32 row_num,
289                        int pass) {
290   PngDecoderState* state =
291       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
292 
293   if (static_cast<int>(row_num) > state->height) {
294     NOTREACHED();
295     return;
296   }
297 
298   uint8_t* base = nullptr;
299   base = &state->output->front();
300 
301   uint8_t* dest = &base[state->width * state->output_channels * row_num];
302   if (state->row_converter)
303     state->row_converter(new_row, state->width, dest, &state->is_opaque);
304   else
305     memcpy(dest, new_row, state->width * state->output_channels);
306 }
307 
DecodeEndCallback(png_struct * png_ptr,png_info * info)308 void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
309   PngDecoderState* state =
310       static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
311 
312   // Mark the image as complete, this will tell the Decode function that we
313   // have successfully found the end of the data.
314   state->done = true;
315 }
316 
317 // Automatically destroys the given read structs on destruction to make
318 // cleanup and error handling code cleaner.
319 class PngReadStructDestroyer {
320  public:
PngReadStructDestroyer(png_struct ** ps,png_info ** pi)321   PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {}
~PngReadStructDestroyer()322   ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_, nullptr); }
323 
324  private:
325   png_struct** ps_;
326   png_info** pi_;
327 };
328 
BuildPNGStruct(pdfium::span<const uint8_t> input,png_struct ** png_ptr,png_info ** info_ptr)329 bool BuildPNGStruct(pdfium::span<const uint8_t> input,
330                     png_struct** png_ptr,
331                     png_info** info_ptr) {
332   if (input.size() < 8)
333     return false;  // Input data too small to be a png
334 
335   // Have libpng check the signature, it likes the first 8 bytes.
336   if (png_sig_cmp(const_cast<uint8_t*>(input.data()), 0, 8) != 0)
337     return false;
338 
339   *png_ptr =
340       png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
341   if (!*png_ptr)
342     return false;
343 
344   *info_ptr = png_create_info_struct(*png_ptr);
345   if (!*info_ptr) {
346     png_destroy_read_struct(png_ptr, nullptr, nullptr);
347     return false;
348   }
349 
350   return true;
351 }
352 
Decode(pdfium::span<const uint8_t> input,ColorFormat format,int * w,int * h)353 std::vector<uint8_t> Decode(pdfium::span<const uint8_t> input,
354                             ColorFormat format,
355                             int* w,
356                             int* h) {
357   std::vector<uint8_t> output;
358   png_struct* png_ptr = nullptr;
359   png_info* info_ptr = nullptr;
360   if (!BuildPNGStruct(input, &png_ptr, &info_ptr))
361     return output;
362 
363   PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
364   if (setjmp(png_jmpbuf(png_ptr))) {
365     // The destroyer will ensure that the structures are cleaned up in this
366     // case, even though we may get here as a jump from random parts of the
367     // PNG library called below.
368     return output;
369   }
370 
371   PngDecoderState state(format, &output);
372 
373   png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
374                               &DecodeRowCallback, &DecodeEndCallback);
375   png_process_data(png_ptr, info_ptr, const_cast<uint8_t*>(input.data()),
376                    input.size());
377 
378   if (!state.done) {
379     // Fed it all the data but the library didn't think we got all the data, so
380     // this file must be truncated.
381     output.clear();
382     return output;
383   }
384 
385   *w = state.width;
386   *h = state.height;
387   return output;
388 }
389 
390 // Encoder
391 //
392 // This section of the code is based on nsPNGEncoder.cpp in Mozilla
393 // (Copyright 2005 Google Inc.)
394 
395 // Passed around as the io_ptr in the png structs so our callbacks know where
396 // to write data.
397 struct PngEncoderState {
PngEncoderStateimage_diff_png::__anonc84d1df30111::PngEncoderState398   explicit PngEncoderState(std::vector<uint8_t>* o) : out(o) {}
399   std::vector<uint8_t>* out;
400 };
401 
402 // Called by libpng to flush its internal buffer to ours.
EncoderWriteCallback(png_structp png,png_bytep data,png_size_t size)403 void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
404   PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
405   size_t old_size = state->out->size();
406   state->out->resize(old_size + size);
407   memcpy(&(*state->out)[old_size], data, size);
408 }
409 
FakeFlushCallback(png_structp png)410 void FakeFlushCallback(png_structp png) {
411   // We don't need to perform any flushing since we aren't doing real IO, but
412   // we're required to provide this function by libpng.
413 }
414 
ConvertBGRAtoRGB(const uint8_t * bgra,int pixel_width,uint8_t * rgb,bool * is_opaque)415 void ConvertBGRAtoRGB(const uint8_t* bgra,
416                       int pixel_width,
417                       uint8_t* rgb,
418                       bool* is_opaque) {
419   for (int x = 0; x < pixel_width; x++) {
420     const uint8_t* pixel_in = &bgra[x * 4];
421     uint8_t* pixel_out = &rgb[x * 3];
422     pixel_out[0] = pixel_in[2];
423     pixel_out[1] = pixel_in[1];
424     pixel_out[2] = pixel_in[0];
425   }
426 }
427 
428 #ifdef PNG_TEXT_SUPPORTED
429 
strdup(const char * str)430 inline char* strdup(const char* str) {
431 #if defined(OS_WIN)
432   return _strdup(str);
433 #else
434   return ::strdup(str);
435 #endif
436 }
437 
438 class CommentWriter {
439  public:
CommentWriter(const std::vector<Comment> & comments)440   explicit CommentWriter(const std::vector<Comment>& comments)
441       : comments_(comments), png_text_(new png_text[comments.size()]) {
442     for (size_t i = 0; i < comments.size(); ++i)
443       AddComment(i, comments[i]);
444   }
445 
~CommentWriter()446   ~CommentWriter() {
447     for (size_t i = 0; i < comments_.size(); ++i) {
448       free(png_text_[i].key);
449       free(png_text_[i].text);
450     }
451     delete[] png_text_;
452   }
453 
HasComments()454   bool HasComments() { return !comments_.empty(); }
455 
get_png_text()456   png_text* get_png_text() { return png_text_; }
457 
size()458   int size() { return static_cast<int>(comments_.size()); }
459 
460  private:
AddComment(size_t pos,const Comment & comment)461   void AddComment(size_t pos, const Comment& comment) {
462     png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
463     // A PNG comment's key can only be 79 characters long.
464     if (comment.key.length() > 79)
465       return;
466     png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str());
467     png_text_[pos].text = strdup(comment.text.c_str());
468     png_text_[pos].text_length = comment.text.length();
469 #ifdef PNG_iTXt_SUPPORTED
470     png_text_[pos].itxt_length = 0;
471     png_text_[pos].lang = 0;
472     png_text_[pos].lang_key = 0;
473 #endif
474   }
475 
476   const std::vector<Comment> comments_;
477   png_text* png_text_;
478 };
479 #endif  // PNG_TEXT_SUPPORTED
480 
481 // The type of functions usable for converting between pixel formats.
482 typedef void (*FormatConverter)(const uint8_t* in,
483                                 int w,
484                                 uint8_t* out,
485                                 bool* is_opaque);
486 
487 // libpng uses a wacky setjmp-based API, which makes the compiler nervous.
488 // We constrain all of the calls we make to libpng where the setjmp() is in
489 // place to this function.
490 // 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)491 bool DoLibpngWrite(png_struct* png_ptr,
492                    png_info* info_ptr,
493                    PngEncoderState* state,
494                    int width,
495                    int height,
496                    int row_byte_width,
497                    pdfium::span<const uint8_t> input,
498                    int compression_level,
499                    int png_output_color_type,
500                    int output_color_components,
501                    FormatConverter converter,
502                    const std::vector<Comment>& comments) {
503 #ifdef PNG_TEXT_SUPPORTED
504   CommentWriter comment_writer(comments);
505 #endif
506   uint8_t* row_buffer = nullptr;
507 
508   // Make sure to not declare any locals here -- locals in the presence
509   // of setjmp() in C++ code makes gcc complain.
510 
511   if (setjmp(png_jmpbuf(png_ptr))) {
512     delete[] row_buffer;
513     return false;
514   }
515 
516   png_set_compression_level(png_ptr, compression_level);
517 
518   // Set our callback for libpng to give us the data.
519   png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
520 
521   png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
522                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
523                PNG_FILTER_TYPE_DEFAULT);
524 
525 #ifdef PNG_TEXT_SUPPORTED
526   if (comment_writer.HasComments()) {
527     png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(),
528                  comment_writer.size());
529   }
530 #endif
531 
532   png_write_info(png_ptr, info_ptr);
533 
534   if (!converter) {
535     // No conversion needed, give the data directly to libpng.
536     for (int y = 0; y < height; y++) {
537       png_write_row(png_ptr, const_cast<uint8_t*>(&input[y * row_byte_width]));
538     }
539   } else {
540     // Needs conversion using a separate buffer.
541     row_buffer = new uint8_t[width * output_color_components];
542     for (int y = 0; y < height; y++) {
543       converter(&input[y * row_byte_width], width, row_buffer, nullptr);
544       png_write_row(png_ptr, row_buffer);
545     }
546     delete[] row_buffer;
547   }
548 
549   png_write_end(png_ptr, info_ptr);
550   return true;
551 }
552 
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)553 std::vector<uint8_t> EncodeWithCompressionLevel(
554     pdfium::span<const uint8_t> input,
555     ColorFormat format,
556     const int width,
557     const int height,
558     int row_byte_width,
559     bool discard_transparency,
560     const std::vector<Comment>& comments,
561     int compression_level) {
562   std::vector<uint8_t> output;
563 
564   // Run to convert an input row into the output row format, nullptr means no
565   // conversion is necessary.
566   FormatConverter converter = nullptr;
567 
568   int input_color_components;
569   int output_color_components;
570   int png_output_color_type;
571   switch (format) {
572     case FORMAT_BGR:
573       converter = ConvertBGRtoRGB;
574       FALLTHROUGH;
575 
576     case FORMAT_RGB:
577       input_color_components = 3;
578       output_color_components = 3;
579       png_output_color_type = PNG_COLOR_TYPE_RGB;
580       break;
581 
582     case FORMAT_RGBA:
583       input_color_components = 4;
584       if (discard_transparency) {
585         output_color_components = 3;
586         png_output_color_type = PNG_COLOR_TYPE_RGB;
587         converter = ConvertRGBAtoRGB;
588       } else {
589         output_color_components = 4;
590         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
591         converter = nullptr;
592       }
593       break;
594 
595     case FORMAT_BGRA:
596       input_color_components = 4;
597       if (discard_transparency) {
598         output_color_components = 3;
599         png_output_color_type = PNG_COLOR_TYPE_RGB;
600         converter = ConvertBGRAtoRGB;
601       } else {
602         output_color_components = 4;
603         png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
604         converter = ConvertBetweenBGRAandRGBA;
605       }
606       break;
607 
608     case FORMAT_GRAY:
609       input_color_components = 1;
610       output_color_components = 1;
611       png_output_color_type = PNG_COLOR_TYPE_GRAY;
612       break;
613 
614     default:
615       NOTREACHED();
616       return output;
617   }
618 
619   // Row stride should be at least as long as the length of the data.
620   if (row_byte_width < input_color_components * width)
621     return output;
622 
623   png_struct* png_ptr =
624       png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
625   if (!png_ptr)
626     return output;
627   png_info* info_ptr = png_create_info_struct(png_ptr);
628   if (!info_ptr) {
629     png_destroy_write_struct(&png_ptr, nullptr);
630     return output;
631   }
632 
633   PngEncoderState state(&output);
634   bool success =
635       DoLibpngWrite(png_ptr, info_ptr, &state, width, height, row_byte_width,
636                     input, compression_level, png_output_color_type,
637                     output_color_components, converter, comments);
638   png_destroy_write_struct(&png_ptr, &info_ptr);
639 
640   if (!success)
641     output.clear();
642   return output;
643 }
644 
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)645 std::vector<uint8_t> Encode(pdfium::span<const uint8_t> input,
646                             ColorFormat format,
647                             const int width,
648                             const int height,
649                             int row_byte_width,
650                             bool discard_transparency,
651                             const std::vector<Comment>& comments) {
652   return EncodeWithCompressionLevel(input, format, width, height,
653                                     row_byte_width, discard_transparency,
654                                     comments, Z_DEFAULT_COMPRESSION);
655 }
656 
657 }  // namespace
658 
DecodePNG(pdfium::span<const uint8_t> input,bool reverse_byte_order,int * width,int * height)659 std::vector<uint8_t> DecodePNG(pdfium::span<const uint8_t> input,
660                                bool reverse_byte_order,
661                                int* width,
662                                int* height) {
663   ColorFormat format = reverse_byte_order ? FORMAT_BGRA : FORMAT_RGBA;
664   return Decode(input, format, width, height);
665 }
666 
EncodeBGRPNG(pdfium::span<const uint8_t> input,int width,int height,int row_byte_width)667 std::vector<uint8_t> EncodeBGRPNG(pdfium::span<const uint8_t> input,
668                                   int width,
669                                   int height,
670                                   int row_byte_width) {
671   return Encode(input, FORMAT_BGR, width, height, row_byte_width, false,
672                 std::vector<Comment>());
673 }
674 
EncodeRGBAPNG(pdfium::span<const uint8_t> input,int width,int height,int row_byte_width)675 std::vector<uint8_t> EncodeRGBAPNG(pdfium::span<const uint8_t> input,
676                                    int width,
677                                    int height,
678                                    int row_byte_width) {
679   return Encode(input, FORMAT_RGBA, width, height, row_byte_width, false,
680                 std::vector<Comment>());
681 }
682 
EncodeBGRAPNG(pdfium::span<const uint8_t> input,int width,int height,int row_byte_width,bool discard_transparency)683 std::vector<uint8_t> EncodeBGRAPNG(pdfium::span<const uint8_t> input,
684                                    int width,
685                                    int height,
686                                    int row_byte_width,
687                                    bool discard_transparency) {
688   return Encode(input, FORMAT_BGRA, width, height, row_byte_width,
689                 discard_transparency, std::vector<Comment>());
690 }
691 
EncodeGrayPNG(pdfium::span<const uint8_t> input,int width,int height,int row_byte_width)692 std::vector<uint8_t> EncodeGrayPNG(pdfium::span<const uint8_t> input,
693                                    int width,
694                                    int height,
695                                    int row_byte_width) {
696   return Encode(input, FORMAT_GRAY, width, height, row_byte_width, false,
697                 std::vector<Comment>());
698 }
699 
700 }  // namespace image_diff_png
701