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