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