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