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