12#include "testing/image_diff/image_diff_png.h"
19#include "third_party/base/check_op.h"
20#include "third_party/base/notreached.h"
25#include "third_party/zlib/zlib.h"
28#ifdef USE_SYSTEM_LIBPNG
31#include "third_party/libpng/png.h"
64void ConvertBetweenBGRAandRGBA(
const uint8_t* input,
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];
78void ConvertBGRtoRGB(
const uint8_t* bgr,
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];
91void ConvertRGBAtoRGB(
const uint8_t* rgba,
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);
110constexpr double kDefaultGamma = 2.2;
113constexpr double kMaxGamma = 21474.83;
115constexpr double kInverseGamma = 1.0 / kDefaultGamma;
117class PngDecoderState {
119 PngDecoderState(ColorFormat ofmt, std::vector<uint8_t>* out)
137 bool* is_opaque) =
nullptr;
147void ConvertRGBtoRGBA(
const uint8_t* rgb,
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);
161void ConvertRGBtoBGRA(
const uint8_t* rgb,
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];
177void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
178 PngDecoderState* state =
179 static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
181 int bit_depth, color_type, interlace_type, compression_type;
182 int filter_type, channels;
184 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
185 &interlace_type, &compression_type, &filter_type);
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);
200 if (color_type == PNG_COLOR_TYPE_PALETTE ||
201 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
202 png_set_expand(png_ptr);
205 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
206 png_set_expand(png_ptr);
210 png_set_strip_16(png_ptr);
213 if (color_type == PNG_COLOR_TYPE_GRAY ||
214 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
215 png_set_gray_to_rgb(png_ptr);
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);
224 png_set_gamma(png_ptr, kDefaultGamma, gamma);
226 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
230 if (interlace_type == PNG_INTERLACE_ADAM7)
231 png_set_interlace_handling(png_ptr);
234 png_read_update_info(png_ptr, info_ptr);
235 channels = png_get_channels(png_ptr, info_ptr);
257 NOTREACHED_NORETURN();
259 }
else if (channels == 4) {
274 NOTREACHED_NORETURN();
277 NOTREACHED_NORETURN();
283void DecodeRowCallback(png_struct* png_ptr,
287 PngDecoderState* state =
288 static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
289 CHECK_LE(
static_cast<
int>(row_num), state
->height);
291 uint8_t* base =
nullptr;
292 base = &state->output->front();
301void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
302 PngDecoderState* state =
303 static_cast<PngDecoderState*>(png_get_progressive_ptr(png_ptr));
312class PngReadStructDestroyer {
314 PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {}
315 ~PngReadStructDestroyer() { png_destroy_read_struct(ps_, pi_,
nullptr); }
322bool BuildPNGStruct(pdfium::span<
const uint8_t> input,
323 png_struct** png_ptr,
324 png_info** info_ptr) {
325 if (input.size() < 8)
329 if (png_sig_cmp(
const_cast<uint8_t*>(input.data()), 0, 8) != 0)
333 png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
337 *info_ptr = png_create_info_struct(*png_ptr);
339 png_destroy_read_struct(png_ptr,
nullptr,
nullptr);
346std::vector<uint8_t> Decode(pdfium::span<
const uint8_t> input,
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))
356 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
357 if (setjmp(png_jmpbuf(png_ptr))) {
364 PngDecoderState state(format, &output);
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()),
390struct PngEncoderState {
391 explicit PngEncoderState(std::vector<uint8_t>* o) : out(o) {}
396void 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);
403void FakeFlushCallback(png_structp png) {
408void ConvertBGRAtoRGB(
const uint8_t* bgra,
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];
421#ifdef PNG_TEXT_SUPPORTED
423inline char* strdup(
const char* str) {
427 return ::strdup(str);
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]);
440 for (size_t i = 0; i < comments_.size(); ++i) {
441 free(png_text_[i].key);
442 free(png_text_[i].text);
447 bool HasComments() {
return !comments_.empty(); }
449 png_text* get_png_text() {
return png_text_; }
451 int size() {
return static_cast<
int>(comments_.size()); }
454 void AddComment(size_t pos,
const Comment& comment) {
455 png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
457 if (comment.key.size() > 79)
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;
469 const std::vector<Comment> comments_;
475typedef void (*FormatConverter)(
const uint8_t* in,
484bool DoLibpngWrite(png_struct* png_ptr,
486 PngEncoderState* state,
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);
499 uint8_t* row_buffer =
nullptr;
504 if (setjmp(png_jmpbuf(png_ptr))) {
509 png_set_compression_level(png_ptr, compression_level);
512 png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
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);
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());
525 png_write_info(png_ptr, info_ptr);
529 for (
int y = 0; y < height; y++) {
530 png_write_row(png_ptr,
const_cast<uint8_t*>(&input[y * row_byte_width]));
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);
542 png_write_end(png_ptr, info_ptr);
546std::vector<uint8_t> EncodeWithCompressionLevel(
547 pdfium::span<
const uint8_t> input,
552 bool discard_transparency,
553 const std::vector<Comment>& comments,
554 int compression_level) {
555 std::vector<uint8_t> output;
559 FormatConverter converter =
nullptr;
561 int input_color_components;
562 int output_color_components;
563 int png_output_color_type;
566 converter = ConvertBGRtoRGB;
570 input_color_components = 3;
571 output_color_components = 3;
572 png_output_color_type = PNG_COLOR_TYPE_RGB;
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;
582 output_color_components = 4;
583 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
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;
595 output_color_components = 4;
596 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
597 converter = ConvertBetweenBGRAandRGBA;
602 input_color_components = 1;
603 output_color_components = 1;
604 png_output_color_type = PNG_COLOR_TYPE_GRAY;
609 if (row_byte_width < input_color_components * width)
612 png_struct* png_ptr =
613 png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
616 png_info* info_ptr = png_create_info_struct(png_ptr);
618 png_destroy_write_struct(&png_ptr,
nullptr);
622 PngEncoderState state(&output);
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);
634std::vector<uint8_t> Encode(pdfium::span<
const uint8_t> input,
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);
649 bool reverse_byte_order,
652 ColorFormat format = reverse_byte_order ? FORMAT_BGRA : FORMAT_RGBA;
653 return Decode(input, format, width, height);
659 int row_byte_width) {
660 return Encode(input, FORMAT_BGR, width, height, row_byte_width,
false,
661 std::vector<Comment>());
667 int row_byte_width) {
668 return Encode(input, FORMAT_RGBA, width, height, row_byte_width,
false,
669 std::vector<Comment>());
676 bool discard_transparency) {
677 return Encode(input, FORMAT_BGRA, width, height, row_byte_width,
678 discard_transparency, std::vector<Comment>());
684 int row_byte_width) {
685 return Encode(input, FORMAT_GRAY, width, height, row_byte_width,
false,
686 std::vector<Comment>());
void(* row_converter)(const uint8_t *in, int w, uint8_t *out, bool *is_opaque)
std::vector< uint8_t > * out
std::vector< uint8_t > *const output
const ColorFormat output_format
std::vector< uint8_t > EncodeGrayPNG(pdfium::span< const uint8_t > input, int width, int height, int row_byte_width)
std::vector< uint8_t > DecodePNG(pdfium::span< const uint8_t > input, bool reverse_byte_order, int *width, int *height)
std::vector< uint8_t > EncodeBGRPNG(pdfium::span< const uint8_t > input, int width, int height, int row_byte_width)
std::vector< uint8_t > EncodeRGBAPNG(pdfium::span< const uint8_t > input, int width, int height, int row_byte_width)
std::vector< uint8_t > EncodeBGRAPNG(pdfium::span< const uint8_t > input, int width, int height, int row_byte_width, bool discard_transparency)