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