| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/codec/png_codec.h" | 5 #include "sky/tools/imagediff/image_diff_png.h" |
| 6 |
| 7 #include <stdlib.h> |
| 8 #include <string.h> |
| 6 | 9 |
| 7 #include "base/logging.h" | 10 #include "base/logging.h" |
| 8 #include "base/strings/string_util.h" | 11 #include "build/build_config.h" |
| 9 #include "third_party/libpng/png.h" | 12 #include "third_party/libpng/png.h" |
| 10 #include "third_party/skia/include/core/SkBitmap.h" | |
| 11 #include "third_party/skia/include/core/SkColorPriv.h" | |
| 12 #include "third_party/skia/include/core/SkUnPreMultiply.h" | |
| 13 #include "third_party/zlib/zlib.h" | 13 #include "third_party/zlib/zlib.h" |
| 14 #include "ui/gfx/size.h" | |
| 15 #include "ui/gfx/skia_util.h" | |
| 16 | 14 |
| 17 namespace gfx { | 15 namespace image_diff_png { |
| 18 | 16 |
| 17 // This is a duplicate of ui/gfx/codec/png_codec.cc, after removing code related |
| 18 // to Skia, that we can use when running layout tests with minimal dependencies. |
| 19 namespace { | 19 namespace { |
| 20 | 20 |
| 21 enum ColorFormat { |
| 22 // 3 bytes per pixel (packed), in RGB order regardless of endianness. |
| 23 // This is the native JPEG format. |
| 24 FORMAT_RGB, |
| 25 |
| 26 // 4 bytes per pixel, in RGBA order in memory regardless of endianness. |
| 27 FORMAT_RGBA, |
| 28 |
| 29 // 4 bytes per pixel, in BGRA order in memory regardless of endianness. |
| 30 // This is the default Windows DIB order. |
| 31 FORMAT_BGRA, |
| 32 |
| 33 // 4 bytes per pixel, in pre-multiplied kARGB_8888_Config format. For use |
| 34 // with directly writing to a skia bitmap. |
| 35 FORMAT_SkBitmap |
| 36 }; |
| 37 |
| 38 // Represents a comment in the tEXt ancillary chunk of the png. |
| 39 struct Comment { |
| 40 std::string key; |
| 41 std::string text; |
| 42 }; |
| 43 |
| 21 // Converts BGRA->RGBA and RGBA->BGRA. | 44 // Converts BGRA->RGBA and RGBA->BGRA. |
| 22 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, | 45 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, |
| 23 unsigned char* output, bool* is_opaque) { | 46 unsigned char* output, bool* is_opaque) { |
| 24 for (int x = 0; x < pixel_width; x++) { | 47 for (int x = 0; x < pixel_width; x++) { |
| 25 const unsigned char* pixel_in = &input[x * 4]; | 48 const unsigned char* pixel_in = &input[x * 4]; |
| 26 unsigned char* pixel_out = &output[x * 4]; | 49 unsigned char* pixel_out = &output[x * 4]; |
| 27 pixel_out[0] = pixel_in[2]; | 50 pixel_out[0] = pixel_in[2]; |
| 28 pixel_out[1] = pixel_in[1]; | 51 pixel_out[1] = pixel_in[1]; |
| 29 pixel_out[2] = pixel_in[0]; | 52 pixel_out[2] = pixel_in[0]; |
| 30 pixel_out[3] = pixel_in[3]; | 53 pixel_out[3] = pixel_in[3]; |
| 31 } | 54 } |
| 32 } | 55 } |
| 33 | 56 |
| 34 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, | 57 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, |
| 35 unsigned char* rgb, bool* is_opaque) { | 58 unsigned char* rgb, bool* is_opaque) { |
| 36 for (int x = 0; x < pixel_width; x++) | |
| 37 memcpy(&rgb[x * 3], &rgba[x * 4], 3); | |
| 38 } | |
| 39 | |
| 40 void ConvertSkiaToRGB(const unsigned char* skia, int pixel_width, | |
| 41 unsigned char* rgb, bool* is_opaque) { | |
| 42 for (int x = 0; x < pixel_width; x++) { | 59 for (int x = 0; x < pixel_width; x++) { |
| 43 const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]); | 60 const unsigned char* pixel_in = &rgba[x * 4]; |
| 44 unsigned char* pixel_out = &rgb[x * 3]; | 61 unsigned char* pixel_out = &rgb[x * 3]; |
| 45 | 62 pixel_out[0] = pixel_in[0]; |
| 46 int alpha = SkGetPackedA32(pixel_in); | 63 pixel_out[1] = pixel_in[1]; |
| 47 if (alpha != 0 && alpha != 255) { | 64 pixel_out[2] = pixel_in[2]; |
| 48 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in); | |
| 49 pixel_out[0] = SkColorGetR(unmultiplied); | |
| 50 pixel_out[1] = SkColorGetG(unmultiplied); | |
| 51 pixel_out[2] = SkColorGetB(unmultiplied); | |
| 52 } else { | |
| 53 pixel_out[0] = SkGetPackedR32(pixel_in); | |
| 54 pixel_out[1] = SkGetPackedG32(pixel_in); | |
| 55 pixel_out[2] = SkGetPackedB32(pixel_in); | |
| 56 } | |
| 57 } | 65 } |
| 58 } | 66 } |
| 59 | 67 |
| 60 void ConvertSkiaToRGBA(const unsigned char* skia, int pixel_width, | |
| 61 unsigned char* rgba, bool* is_opaque) { | |
| 62 gfx::ConvertSkiaToRGBA(skia, pixel_width, rgba); | |
| 63 } | |
| 64 | |
| 65 } // namespace | 68 } // namespace |
| 66 | 69 |
| 67 // Decoder -------------------------------------------------------------------- | 70 // Decoder -------------------------------------------------------------------- |
| 68 // | 71 // |
| 69 // This code is based on WebKit libpng interface (PNGImageDecoder), which is | 72 // This code is based on WebKit libpng interface (PNGImageDecoder), which is |
| 70 // in turn based on the Mozilla png decoder. | 73 // in turn based on the Mozilla png decoder. |
| 71 | 74 |
| 72 namespace { | 75 namespace { |
| 73 | 76 |
| 74 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2. | 77 // Gamma constants: We assume we're on Windows which uses a gamma of 2.2. |
| 75 const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library. | 78 const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library. |
| 76 const double kDefaultGamma = 2.2; | 79 const double kDefaultGamma = 2.2; |
| 77 const double kInverseGamma = 1.0 / kDefaultGamma; | 80 const double kInverseGamma = 1.0 / kDefaultGamma; |
| 78 | 81 |
| 79 class PngDecoderState { | 82 class PngDecoderState { |
| 80 public: | 83 public: |
| 81 // Output is a vector<unsigned char>. | 84 // Output is a vector<unsigned char>. |
| 82 PngDecoderState(PNGCodec::ColorFormat ofmt, std::vector<unsigned char>* o) | 85 PngDecoderState(ColorFormat ofmt, std::vector<unsigned char>* o) |
| 83 : output_format(ofmt), | 86 : output_format(ofmt), |
| 84 output_channels(0), | 87 output_channels(0), |
| 85 bitmap(NULL), | |
| 86 is_opaque(true), | 88 is_opaque(true), |
| 87 output(o), | 89 output(o), |
| 90 row_converter(NULL), |
| 88 width(0), | 91 width(0), |
| 89 height(0), | 92 height(0), |
| 90 done(false) { | 93 done(false) { |
| 91 } | 94 } |
| 92 | 95 |
| 93 // Output is an SkBitmap. | 96 ColorFormat output_format; |
| 94 explicit PngDecoderState(SkBitmap* skbitmap) | |
| 95 : output_format(PNGCodec::FORMAT_SkBitmap), | |
| 96 output_channels(0), | |
| 97 bitmap(skbitmap), | |
| 98 is_opaque(true), | |
| 99 output(NULL), | |
| 100 width(0), | |
| 101 height(0), | |
| 102 done(false) { | |
| 103 } | |
| 104 | |
| 105 PNGCodec::ColorFormat output_format; | |
| 106 int output_channels; | 97 int output_channels; |
| 107 | 98 |
| 108 // An incoming SkBitmap to write to. If NULL, we write to output instead. | |
| 109 SkBitmap* bitmap; | |
| 110 | |
| 111 // Used during the reading of an SkBitmap. Defaults to true until we see a | 99 // Used during the reading of an SkBitmap. Defaults to true until we see a |
| 112 // pixel with anything other than an alpha of 255. | 100 // pixel with anything other than an alpha of 255. |
| 113 bool is_opaque; | 101 bool is_opaque; |
| 114 | 102 |
| 115 // The other way to decode output, where we write into an intermediary buffer | 103 // An intermediary buffer for decode output. |
| 116 // instead of directly to an SkBitmap. | |
| 117 std::vector<unsigned char>* output; | 104 std::vector<unsigned char>* output; |
| 118 | 105 |
| 106 // Called to convert a row from the library to the correct output format. |
| 107 // When NULL, no conversion is necessary. |
| 108 void (*row_converter)(const unsigned char* in, int w, unsigned char* out, |
| 109 bool* is_opaque); |
| 110 |
| 119 // Size of the image, set in the info callback. | 111 // Size of the image, set in the info callback. |
| 120 int width; | 112 int width; |
| 121 int height; | 113 int height; |
| 122 | 114 |
| 123 // Set to true when we've found the end of the data. | 115 // Set to true when we've found the end of the data. |
| 124 bool done; | 116 bool done; |
| 125 | |
| 126 private: | |
| 127 DISALLOW_COPY_AND_ASSIGN(PngDecoderState); | |
| 128 }; | 117 }; |
| 129 | 118 |
| 130 // User transform (passed to libpng) which converts a row decoded by libpng to | 119 void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, |
| 131 // Skia format. Expects the row to have 4 channels, otherwise there won't be | 120 unsigned char* rgba, bool* is_opaque) { |
| 132 // enough room in |data|. | 121 for (int x = 0; x < pixel_width; x++) { |
| 133 void ConvertRGBARowToSkia(png_structp png_ptr, | 122 const unsigned char* pixel_in = &rgb[x * 3]; |
| 134 png_row_infop row_info, | 123 unsigned char* pixel_out = &rgba[x * 4]; |
| 135 png_bytep data) { | 124 pixel_out[0] = pixel_in[0]; |
| 136 const int channels = row_info->channels; | 125 pixel_out[1] = pixel_in[1]; |
| 137 DCHECK_EQ(channels, 4); | 126 pixel_out[2] = pixel_in[2]; |
| 138 | 127 pixel_out[3] = 0xff; |
| 139 PngDecoderState* state = | |
| 140 static_cast<PngDecoderState*>(png_get_user_transform_ptr(png_ptr)); | |
| 141 DCHECK(state) << "LibPNG user transform pointer is NULL"; | |
| 142 | |
| 143 unsigned char* const end = data + row_info->rowbytes; | |
| 144 for (unsigned char* p = data; p < end; p += channels) { | |
| 145 uint32_t* sk_pixel = reinterpret_cast<uint32_t*>(p); | |
| 146 const unsigned char alpha = p[channels - 1]; | |
| 147 if (alpha != 255) { | |
| 148 state->is_opaque = false; | |
| 149 *sk_pixel = SkPreMultiplyARGB(alpha, p[0], p[1], p[2]); | |
| 150 } else { | |
| 151 *sk_pixel = SkPackARGB32(alpha, p[0], p[1], p[2]); | |
| 152 } | |
| 153 } | 128 } |
| 154 } | 129 } |
| 155 | 130 |
| 131 void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width, |
| 132 unsigned char* bgra, bool* is_opaque) { |
| 133 for (int x = 0; x < pixel_width; x++) { |
| 134 const unsigned char* pixel_in = &rgb[x * 3]; |
| 135 unsigned char* pixel_out = &bgra[x * 4]; |
| 136 pixel_out[0] = pixel_in[2]; |
| 137 pixel_out[1] = pixel_in[1]; |
| 138 pixel_out[2] = pixel_in[0]; |
| 139 pixel_out[3] = 0xff; |
| 140 } |
| 141 } |
| 142 |
| 156 // Called when the png header has been read. This code is based on the WebKit | 143 // Called when the png header has been read. This code is based on the WebKit |
| 157 // PNGImageDecoder | 144 // PNGImageDecoder |
| 158 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { | 145 void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { |
| 159 PngDecoderState* state = static_cast<PngDecoderState*>( | 146 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 160 png_get_progressive_ptr(png_ptr)); | 147 png_get_progressive_ptr(png_ptr)); |
| 161 | 148 |
| 162 int bit_depth, color_type, interlace_type, compression_type; | 149 int bit_depth, color_type, interlace_type, compression_type; |
| 163 int filter_type; | 150 int filter_type, channels; |
| 164 png_uint_32 w, h; | 151 png_uint_32 w, h; |
| 165 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, | 152 png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, |
| 166 &interlace_type, &compression_type, &filter_type); | 153 &interlace_type, &compression_type, &filter_type); |
| 167 | 154 |
| 168 // Bounds check. When the image is unreasonably big, we'll error out and | 155 // Bounds check. When the image is unreasonably big, we'll error out and |
| 169 // end up back at the setjmp call when we set up decoding. "Unreasonably big" | 156 // end up back at the setjmp call when we set up decoding. "Unreasonably big" |
| 170 // means "big enough that w * h * 32bpp might overflow an int"; we choose this | 157 // means "big enough that w * h * 32bpp might overflow an int"; we choose this |
| 171 // threshold to match WebKit and because a number of places in code assume | 158 // threshold to match WebKit and because a number of places in code assume |
| 172 // that an image's size (in bytes) fits in a (signed) int. | 159 // that an image's size (in bytes) fits in a (signed) int. |
| 173 unsigned long long total_size = | 160 unsigned long long total_size = |
| 174 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h); | 161 static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h); |
| 175 if (total_size > ((1 << 29) - 1)) | 162 if (total_size > ((1 << 29) - 1)) |
| 176 longjmp(png_jmpbuf(png_ptr), 1); | 163 longjmp(png_jmpbuf(png_ptr), 1); |
| 177 state->width = static_cast<int>(w); | 164 state->width = static_cast<int>(w); |
| 178 state->height = static_cast<int>(h); | 165 state->height = static_cast<int>(h); |
| 179 | 166 |
| 180 // The following png_set_* calls have to be done in the order dictated by | |
| 181 // the libpng docs. Please take care if you have to move any of them. This | |
| 182 // is also why certain things are done outside of the switch, even though | |
| 183 // they look like they belong there. | |
| 184 | |
| 185 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. | 167 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. |
| 186 if (color_type == PNG_COLOR_TYPE_PALETTE || | 168 if (color_type == PNG_COLOR_TYPE_PALETTE || |
| 187 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) | 169 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) |
| 188 png_set_expand(png_ptr); | 170 png_set_expand(png_ptr); |
| 189 | 171 |
| 190 // The '!= 0' is for silencing a Windows compiler warning. | |
| 191 bool input_has_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0); | |
| 192 | |
| 193 // Transparency for paletted images. | 172 // Transparency for paletted images. |
| 194 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | 173 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) |
| 195 png_set_expand(png_ptr); | 174 png_set_expand(png_ptr); |
| 196 input_has_alpha = true; | |
| 197 } | |
| 198 | 175 |
| 199 // Convert 16-bit to 8-bit. | 176 // Convert 16-bit to 8-bit. |
| 200 if (bit_depth == 16) | 177 if (bit_depth == 16) |
| 201 png_set_strip_16(png_ptr); | 178 png_set_strip_16(png_ptr); |
| 202 | 179 |
| 203 // Pick our row format converter necessary for this data. | |
| 204 if (!input_has_alpha) { | |
| 205 switch (state->output_format) { | |
| 206 case PNGCodec::FORMAT_RGB: | |
| 207 state->output_channels = 3; | |
| 208 break; | |
| 209 case PNGCodec::FORMAT_RGBA: | |
| 210 state->output_channels = 4; | |
| 211 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); | |
| 212 break; | |
| 213 case PNGCodec::FORMAT_BGRA: | |
| 214 state->output_channels = 4; | |
| 215 png_set_bgr(png_ptr); | |
| 216 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); | |
| 217 break; | |
| 218 case PNGCodec::FORMAT_SkBitmap: | |
| 219 state->output_channels = 4; | |
| 220 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); | |
| 221 break; | |
| 222 } | |
| 223 } else { | |
| 224 switch (state->output_format) { | |
| 225 case PNGCodec::FORMAT_RGB: | |
| 226 state->output_channels = 3; | |
| 227 png_set_strip_alpha(png_ptr); | |
| 228 break; | |
| 229 case PNGCodec::FORMAT_RGBA: | |
| 230 state->output_channels = 4; | |
| 231 break; | |
| 232 case PNGCodec::FORMAT_BGRA: | |
| 233 state->output_channels = 4; | |
| 234 png_set_bgr(png_ptr); | |
| 235 break; | |
| 236 case PNGCodec::FORMAT_SkBitmap: | |
| 237 state->output_channels = 4; | |
| 238 break; | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 // Expand grayscale to RGB. | 180 // Expand grayscale to RGB. |
| 243 if (color_type == PNG_COLOR_TYPE_GRAY || | 181 if (color_type == PNG_COLOR_TYPE_GRAY || |
| 244 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) | 182 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) |
| 245 png_set_gray_to_rgb(png_ptr); | 183 png_set_gray_to_rgb(png_ptr); |
| 246 | 184 |
| 247 // Deal with gamma and keep it under our control. | 185 // Deal with gamma and keep it under our control. |
| 248 double gamma; | 186 double gamma; |
| 249 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { | 187 if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { |
| 250 if (gamma <= 0.0 || gamma > kMaxGamma) { | 188 if (gamma <= 0.0 || gamma > kMaxGamma) { |
| 251 gamma = kInverseGamma; | 189 gamma = kInverseGamma; |
| 252 png_set_gAMA(png_ptr, info_ptr, gamma); | 190 png_set_gAMA(png_ptr, info_ptr, gamma); |
| 253 } | 191 } |
| 254 png_set_gamma(png_ptr, kDefaultGamma, gamma); | 192 png_set_gamma(png_ptr, kDefaultGamma, gamma); |
| 255 } else { | 193 } else { |
| 256 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); | 194 png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); |
| 257 } | 195 } |
| 258 | 196 |
| 259 // Setting the user transforms here (as opposed to inside the switch above) | |
| 260 // because all png_set_* calls need to be done in the specific order | |
| 261 // mandated by libpng. | |
| 262 if (state->output_format == PNGCodec::FORMAT_SkBitmap) { | |
| 263 png_set_read_user_transform_fn(png_ptr, ConvertRGBARowToSkia); | |
| 264 png_set_user_transform_info(png_ptr, state, 0, 0); | |
| 265 } | |
| 266 | |
| 267 // Tell libpng to send us rows for interlaced pngs. | 197 // Tell libpng to send us rows for interlaced pngs. |
| 268 if (interlace_type == PNG_INTERLACE_ADAM7) | 198 if (interlace_type == PNG_INTERLACE_ADAM7) |
| 269 png_set_interlace_handling(png_ptr); | 199 png_set_interlace_handling(png_ptr); |
| 270 | 200 |
| 201 // Update our info now |
| 271 png_read_update_info(png_ptr, info_ptr); | 202 png_read_update_info(png_ptr, info_ptr); |
| 203 channels = png_get_channels(png_ptr, info_ptr); |
| 272 | 204 |
| 273 if (state->bitmap) { | 205 // Pick our row format converter necessary for this data. |
| 274 state->bitmap->allocN32Pixels(state->width, state->height); | 206 if (channels == 3) { |
| 275 } else if (state->output) { | 207 switch (state->output_format) { |
| 276 state->output->resize( | 208 case FORMAT_RGB: |
| 277 state->width * state->output_channels * state->height); | 209 state->row_converter = NULL; // no conversion necessary |
| 210 state->output_channels = 3; |
| 211 break; |
| 212 case FORMAT_RGBA: |
| 213 state->row_converter = &ConvertRGBtoRGBA; |
| 214 state->output_channels = 4; |
| 215 break; |
| 216 case FORMAT_BGRA: |
| 217 state->row_converter = &ConvertRGBtoBGRA; |
| 218 state->output_channels = 4; |
| 219 break; |
| 220 default: |
| 221 NOTREACHED() << "Unknown output format"; |
| 222 break; |
| 223 } |
| 224 } else if (channels == 4) { |
| 225 switch (state->output_format) { |
| 226 case FORMAT_RGB: |
| 227 state->row_converter = &ConvertRGBAtoRGB; |
| 228 state->output_channels = 3; |
| 229 break; |
| 230 case FORMAT_RGBA: |
| 231 state->row_converter = NULL; // no conversion necessary |
| 232 state->output_channels = 4; |
| 233 break; |
| 234 case FORMAT_BGRA: |
| 235 state->row_converter = &ConvertBetweenBGRAandRGBA; |
| 236 state->output_channels = 4; |
| 237 break; |
| 238 default: |
| 239 NOTREACHED() << "Unknown output format"; |
| 240 break; |
| 241 } |
| 242 } else { |
| 243 NOTREACHED() << "Unknown input channels"; |
| 244 longjmp(png_jmpbuf(png_ptr), 1); |
| 278 } | 245 } |
| 246 |
| 247 state->output->resize( |
| 248 state->width * state->output_channels * state->height); |
| 279 } | 249 } |
| 280 | 250 |
| 281 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, | 251 void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, |
| 282 png_uint_32 row_num, int pass) { | 252 png_uint_32 row_num, int pass) { |
| 283 if (!new_row) | |
| 284 return; // Interlaced image; row didn't change this pass. | |
| 285 | |
| 286 PngDecoderState* state = static_cast<PngDecoderState*>( | 253 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 287 png_get_progressive_ptr(png_ptr)); | 254 png_get_progressive_ptr(png_ptr)); |
| 288 | 255 |
| 256 DCHECK(pass == 0); |
| 289 if (static_cast<int>(row_num) > state->height) { | 257 if (static_cast<int>(row_num) > state->height) { |
| 290 NOTREACHED() << "Invalid row"; | 258 NOTREACHED() << "Invalid row"; |
| 291 return; | 259 return; |
| 292 } | 260 } |
| 293 | 261 |
| 294 unsigned char* base = NULL; | 262 unsigned char* base = NULL; |
| 295 if (state->bitmap) | 263 base = &state->output->front(); |
| 296 base = reinterpret_cast<unsigned char*>(state->bitmap->getAddr32(0, 0)); | |
| 297 else if (state->output) | |
| 298 base = &state->output->front(); | |
| 299 | 264 |
| 300 unsigned char* dest = &base[state->width * state->output_channels * row_num]; | 265 unsigned char* dest = &base[state->width * state->output_channels * row_num]; |
| 301 png_progressive_combine_row(png_ptr, dest, new_row); | 266 if (state->row_converter) |
| 267 state->row_converter(new_row, state->width, dest, &state->is_opaque); |
| 268 else |
| 269 memcpy(dest, new_row, state->width * state->output_channels); |
| 302 } | 270 } |
| 303 | 271 |
| 304 void DecodeEndCallback(png_struct* png_ptr, png_info* info) { | 272 void DecodeEndCallback(png_struct* png_ptr, png_info* info) { |
| 305 PngDecoderState* state = static_cast<PngDecoderState*>( | 273 PngDecoderState* state = static_cast<PngDecoderState*>( |
| 306 png_get_progressive_ptr(png_ptr)); | 274 png_get_progressive_ptr(png_ptr)); |
| 307 | 275 |
| 308 // Mark the image as complete, this will tell the Decode function that we | 276 // Mark the image as complete, this will tell the Decode function that we |
| 309 // have successfully found the end of the data. | 277 // have successfully found the end of the data. |
| 310 state->done = true; | 278 state->done = true; |
| 311 } | 279 } |
| 312 | 280 |
| 313 // Automatically destroys the given read structs on destruction to make | 281 // Automatically destroys the given read structs on destruction to make |
| 314 // cleanup and error handling code cleaner. | 282 // cleanup and error handling code cleaner. |
| 315 class PngReadStructDestroyer { | 283 class PngReadStructDestroyer { |
| 316 public: | 284 public: |
| 317 PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { | 285 PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { |
| 318 } | 286 } |
| 319 ~PngReadStructDestroyer() { | 287 ~PngReadStructDestroyer() { |
| 320 png_destroy_read_struct(ps_, pi_, NULL); | 288 png_destroy_read_struct(ps_, pi_, NULL); |
| 321 } | 289 } |
| 322 private: | 290 private: |
| 323 png_struct** ps_; | 291 png_struct** ps_; |
| 324 png_info** pi_; | 292 png_info** pi_; |
| 325 DISALLOW_COPY_AND_ASSIGN(PngReadStructDestroyer); | |
| 326 }; | |
| 327 | |
| 328 // Automatically destroys the given write structs on destruction to make | |
| 329 // cleanup and error handling code cleaner. | |
| 330 class PngWriteStructDestroyer { | |
| 331 public: | |
| 332 explicit PngWriteStructDestroyer(png_struct** ps) : ps_(ps), pi_(0) { | |
| 333 } | |
| 334 ~PngWriteStructDestroyer() { | |
| 335 png_destroy_write_struct(ps_, pi_); | |
| 336 } | |
| 337 void SetInfoStruct(png_info** pi) { | |
| 338 pi_ = pi; | |
| 339 } | |
| 340 private: | |
| 341 png_struct** ps_; | |
| 342 png_info** pi_; | |
| 343 DISALLOW_COPY_AND_ASSIGN(PngWriteStructDestroyer); | |
| 344 }; | 293 }; |
| 345 | 294 |
| 346 bool BuildPNGStruct(const unsigned char* input, size_t input_size, | 295 bool BuildPNGStruct(const unsigned char* input, size_t input_size, |
| 347 png_struct** png_ptr, png_info** info_ptr) { | 296 png_struct** png_ptr, png_info** info_ptr) { |
| 348 if (input_size < 8) | 297 if (input_size < 8) |
| 349 return false; // Input data too small to be a png | 298 return false; // Input data too small to be a png |
| 350 | 299 |
| 351 // Have libpng check the signature, it likes the first 8 bytes. | 300 // Have libpng check the signature, it likes the first 8 bytes. |
| 352 if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0) | 301 if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0) |
| 353 return false; | 302 return false; |
| 354 | 303 |
| 355 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | 304 *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); |
| 356 if (!*png_ptr) | 305 if (!*png_ptr) |
| 357 return false; | 306 return false; |
| 358 | 307 |
| 359 *info_ptr = png_create_info_struct(*png_ptr); | 308 *info_ptr = png_create_info_struct(*png_ptr); |
| 360 if (!*info_ptr) { | 309 if (!*info_ptr) { |
| 361 png_destroy_read_struct(png_ptr, NULL, NULL); | 310 png_destroy_read_struct(png_ptr, NULL, NULL); |
| 362 return false; | 311 return false; |
| 363 } | 312 } |
| 364 | 313 |
| 365 return true; | 314 return true; |
| 366 } | 315 } |
| 367 | 316 |
| 368 // Libpng user error and warning functions which allows us to print libpng | |
| 369 // errors and warnings using Chrome's logging facilities instead of stderr. | |
| 370 | |
| 371 void LogLibPNGDecodeError(png_structp png_ptr, png_const_charp error_msg) { | |
| 372 DLOG(ERROR) << "libpng decode error: " << error_msg; | |
| 373 longjmp(png_jmpbuf(png_ptr), 1); | |
| 374 } | |
| 375 | |
| 376 void LogLibPNGDecodeWarning(png_structp png_ptr, png_const_charp warning_msg) { | |
| 377 DLOG(ERROR) << "libpng decode warning: " << warning_msg; | |
| 378 } | |
| 379 | |
| 380 void LogLibPNGEncodeError(png_structp png_ptr, png_const_charp error_msg) { | |
| 381 DLOG(ERROR) << "libpng encode error: " << error_msg; | |
| 382 longjmp(png_jmpbuf(png_ptr), 1); | |
| 383 } | |
| 384 | |
| 385 void LogLibPNGEncodeWarning(png_structp png_ptr, png_const_charp warning_msg) { | |
| 386 DLOG(ERROR) << "libpng encode warning: " << warning_msg; | |
| 387 } | |
| 388 | |
| 389 } // namespace | 317 } // namespace |
| 390 | 318 |
| 391 // static | 319 // static |
| 392 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, | 320 bool Decode(const unsigned char* input, size_t input_size, |
| 393 ColorFormat format, std::vector<unsigned char>* output, | 321 ColorFormat format, std::vector<unsigned char>* output, |
| 394 int* w, int* h) { | 322 int* w, int* h) { |
| 395 png_struct* png_ptr = NULL; | 323 png_struct* png_ptr = NULL; |
| 396 png_info* info_ptr = NULL; | 324 png_info* info_ptr = NULL; |
| 397 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) | 325 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) |
| 398 return false; | 326 return false; |
| 399 | 327 |
| 400 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); | 328 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); |
| 401 if (setjmp(png_jmpbuf(png_ptr))) { | 329 if (setjmp(png_jmpbuf(png_ptr))) { |
| 402 // The destroyer will ensure that the structures are cleaned up in this | 330 // The destroyer will ensure that the structures are cleaned up in this |
| 403 // case, even though we may get here as a jump from random parts of the | 331 // case, even though we may get here as a jump from random parts of the |
| 404 // PNG library called below. | 332 // PNG library called below. |
| 405 return false; | 333 return false; |
| 406 } | 334 } |
| 407 | 335 |
| 408 PngDecoderState state(format, output); | 336 PngDecoderState state(format, output); |
| 409 | 337 |
| 410 png_set_error_fn(png_ptr, NULL, LogLibPNGDecodeError, LogLibPNGDecodeWarning); | |
| 411 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, | 338 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, |
| 412 &DecodeRowCallback, &DecodeEndCallback); | 339 &DecodeRowCallback, &DecodeEndCallback); |
| 413 png_process_data(png_ptr, | 340 png_process_data(png_ptr, |
| 414 info_ptr, | 341 info_ptr, |
| 415 const_cast<unsigned char*>(input), | 342 const_cast<unsigned char*>(input), |
| 416 input_size); | 343 input_size); |
| 417 | 344 |
| 418 if (!state.done) { | 345 if (!state.done) { |
| 419 // Fed it all the data but the library didn't think we got all the data, so | 346 // Fed it all the data but the library didn't think we got all the data, so |
| 420 // this file must be truncated. | 347 // this file must be truncated. |
| 421 output->clear(); | 348 output->clear(); |
| 422 return false; | 349 return false; |
| 423 } | 350 } |
| 424 | 351 |
| 425 *w = state.width; | 352 *w = state.width; |
| 426 *h = state.height; | 353 *h = state.height; |
| 427 return true; | 354 return true; |
| 428 } | 355 } |
| 429 | 356 |
| 430 // static | |
| 431 bool PNGCodec::Decode(const unsigned char* input, size_t input_size, | |
| 432 SkBitmap* bitmap) { | |
| 433 DCHECK(bitmap); | |
| 434 png_struct* png_ptr = NULL; | |
| 435 png_info* info_ptr = NULL; | |
| 436 if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr)) | |
| 437 return false; | |
| 438 | |
| 439 PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); | |
| 440 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 441 // The destroyer will ensure that the structures are cleaned up in this | |
| 442 // case, even though we may get here as a jump from random parts of the | |
| 443 // PNG library called below. | |
| 444 return false; | |
| 445 } | |
| 446 | |
| 447 PngDecoderState state(bitmap); | |
| 448 | |
| 449 png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, | |
| 450 &DecodeRowCallback, &DecodeEndCallback); | |
| 451 png_process_data(png_ptr, | |
| 452 info_ptr, | |
| 453 const_cast<unsigned char*>(input), | |
| 454 input_size); | |
| 455 | |
| 456 if (!state.done) { | |
| 457 return false; | |
| 458 } | |
| 459 | |
| 460 // Set the bitmap's opaqueness based on what we saw. | |
| 461 bitmap->setAlphaType(state.is_opaque ? | |
| 462 kOpaque_SkAlphaType : kPremul_SkAlphaType); | |
| 463 | |
| 464 return true; | |
| 465 } | |
| 466 | |
| 467 // Encoder -------------------------------------------------------------------- | 357 // Encoder -------------------------------------------------------------------- |
| 468 // | 358 // |
| 469 // This section of the code is based on nsPNGEncoder.cpp in Mozilla | 359 // This section of the code is based on nsPNGEncoder.cpp in Mozilla |
| 470 // (Copyright 2005 Google Inc.) | 360 // (Copyright 2005 Google Inc.) |
| 471 | 361 |
| 472 namespace { | 362 namespace { |
| 473 | 363 |
| 474 // Passed around as the io_ptr in the png structs so our callbacks know where | 364 // Passed around as the io_ptr in the png structs so our callbacks know where |
| 475 // to write data. | 365 // to write data. |
| 476 struct PngEncoderState { | 366 struct PngEncoderState { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 498 for (int x = 0; x < pixel_width; x++) { | 388 for (int x = 0; x < pixel_width; x++) { |
| 499 const unsigned char* pixel_in = &bgra[x * 4]; | 389 const unsigned char* pixel_in = &bgra[x * 4]; |
| 500 unsigned char* pixel_out = &rgb[x * 3]; | 390 unsigned char* pixel_out = &rgb[x * 3]; |
| 501 pixel_out[0] = pixel_in[2]; | 391 pixel_out[0] = pixel_in[2]; |
| 502 pixel_out[1] = pixel_in[1]; | 392 pixel_out[1] = pixel_in[1]; |
| 503 pixel_out[2] = pixel_in[0]; | 393 pixel_out[2] = pixel_in[0]; |
| 504 } | 394 } |
| 505 } | 395 } |
| 506 | 396 |
| 507 #ifdef PNG_TEXT_SUPPORTED | 397 #ifdef PNG_TEXT_SUPPORTED |
| 398 |
| 399 inline char* strdup(const char* str) { |
| 400 #if defined(OS_WIN) |
| 401 return _strdup(str); |
| 402 #else |
| 403 return ::strdup(str); |
| 404 #endif |
| 405 } |
| 406 |
| 508 class CommentWriter { | 407 class CommentWriter { |
| 509 public: | 408 public: |
| 510 explicit CommentWriter(const std::vector<PNGCodec::Comment>& comments) | 409 explicit CommentWriter(const std::vector<Comment>& comments) |
| 511 : comments_(comments), | 410 : comments_(comments), |
| 512 png_text_(new png_text[comments.size()]) { | 411 png_text_(new png_text[comments.size()]) { |
| 513 for (size_t i = 0; i < comments.size(); ++i) | 412 for (size_t i = 0; i < comments.size(); ++i) |
| 514 AddComment(i, comments[i]); | 413 AddComment(i, comments[i]); |
| 515 } | 414 } |
| 516 | 415 |
| 517 ~CommentWriter() { | 416 ~CommentWriter() { |
| 518 for (size_t i = 0; i < comments_.size(); ++i) { | 417 for (size_t i = 0; i < comments_.size(); ++i) { |
| 519 free(png_text_[i].key); | 418 free(png_text_[i].key); |
| 520 free(png_text_[i].text); | 419 free(png_text_[i].text); |
| 521 } | 420 } |
| 522 delete [] png_text_; | 421 delete [] png_text_; |
| 523 } | 422 } |
| 524 | 423 |
| 525 bool HasComments() { | 424 bool HasComments() { |
| 526 return !comments_.empty(); | 425 return !comments_.empty(); |
| 527 } | 426 } |
| 528 | 427 |
| 529 png_text* get_png_text() { | 428 png_text* get_png_text() { |
| 530 return png_text_; | 429 return png_text_; |
| 531 } | 430 } |
| 532 | 431 |
| 533 int size() { | 432 int size() { |
| 534 return static_cast<int>(comments_.size()); | 433 return static_cast<int>(comments_.size()); |
| 535 } | 434 } |
| 536 | 435 |
| 537 private: | 436 private: |
| 538 void AddComment(size_t pos, const PNGCodec::Comment& comment) { | 437 void AddComment(size_t pos, const Comment& comment) { |
| 539 png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE; | 438 png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE; |
| 540 // A PNG comment's key can only be 79 characters long. | 439 // A PNG comment's key can only be 79 characters long. |
| 541 DCHECK(comment.key.length() < 79); | 440 DCHECK(comment.key.length() < 79); |
| 542 png_text_[pos].key = base::strdup(comment.key.substr(0, 78).c_str()); | 441 png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str()); |
| 543 png_text_[pos].text = base::strdup(comment.text.c_str()); | 442 png_text_[pos].text = strdup(comment.text.c_str()); |
| 544 png_text_[pos].text_length = comment.text.length(); | 443 png_text_[pos].text_length = comment.text.length(); |
| 545 #ifdef PNG_iTXt_SUPPORTED | 444 #ifdef PNG_iTXt_SUPPORTED |
| 546 png_text_[pos].itxt_length = 0; | 445 png_text_[pos].itxt_length = 0; |
| 547 png_text_[pos].lang = 0; | 446 png_text_[pos].lang = 0; |
| 548 png_text_[pos].lang_key = 0; | 447 png_text_[pos].lang_key = 0; |
| 549 #endif | 448 #endif |
| 550 } | 449 } |
| 551 | 450 |
| 552 DISALLOW_COPY_AND_ASSIGN(CommentWriter); | 451 const std::vector<Comment> comments_; |
| 553 | |
| 554 const std::vector<PNGCodec::Comment> comments_; | |
| 555 png_text* png_text_; | 452 png_text* png_text_; |
| 556 }; | 453 }; |
| 557 #endif // PNG_TEXT_SUPPORTED | 454 #endif // PNG_TEXT_SUPPORTED |
| 558 | 455 |
| 559 // The type of functions usable for converting between pixel formats. | 456 // The type of functions usable for converting between pixel formats. |
| 560 typedef void (*FormatConverter)(const unsigned char* in, int w, | 457 typedef void (*FormatConverter)(const unsigned char* in, int w, |
| 561 unsigned char* out, bool* is_opaque); | 458 unsigned char* out, bool* is_opaque); |
| 562 | 459 |
| 563 // libpng uses a wacky setjmp-based API, which makes the compiler nervous. | 460 // libpng uses a wacky setjmp-based API, which makes the compiler nervous. |
| 564 // We constrain all of the calls we make to libpng where the setjmp() is in | 461 // We constrain all of the calls we make to libpng where the setjmp() is in |
| 565 // place to this function. | 462 // place to this function. |
| 566 // Returns true on success. | 463 // Returns true on success. |
| 567 bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr, | 464 bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr, |
| 568 PngEncoderState* state, | 465 PngEncoderState* state, |
| 569 int width, int height, int row_byte_width, | 466 int width, int height, int row_byte_width, |
| 570 const unsigned char* input, int compression_level, | 467 const unsigned char* input, int compression_level, |
| 571 int png_output_color_type, int output_color_components, | 468 int png_output_color_type, int output_color_components, |
| 572 FormatConverter converter, | 469 FormatConverter converter, |
| 573 const std::vector<PNGCodec::Comment>& comments) { | 470 const std::vector<Comment>& comments) { |
| 574 #ifdef PNG_TEXT_SUPPORTED | 471 #ifdef PNG_TEXT_SUPPORTED |
| 575 CommentWriter comment_writer(comments); | 472 CommentWriter comment_writer(comments); |
| 576 #endif | 473 #endif |
| 577 unsigned char* row_buffer = NULL; | 474 unsigned char* row_buffer = NULL; |
| 578 | 475 |
| 579 // Make sure to not declare any locals here -- locals in the presence | 476 // Make sure to not declare any locals here -- locals in the presence |
| 580 // of setjmp() in C++ code makes gcc complain. | 477 // of setjmp() in C++ code makes gcc complain. |
| 581 | 478 |
| 582 if (setjmp(png_jmpbuf(png_ptr))) { | 479 if (setjmp(png_jmpbuf(png_ptr))) { |
| 583 delete[] row_buffer; | 480 delete[] row_buffer; |
| 584 return false; | 481 return false; |
| 585 } | 482 } |
| 586 | 483 |
| 587 png_set_compression_level(png_ptr, compression_level); | 484 png_set_compression_level(png_ptr, compression_level); |
| 588 | 485 |
| 589 // Set our callback for libpng to give us the data. | 486 // Set our callback for libpng to give us the data. |
| 590 png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback); | 487 png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback); |
| 591 png_set_error_fn(png_ptr, NULL, LogLibPNGEncodeError, LogLibPNGEncodeWarning); | |
| 592 | 488 |
| 593 png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type, | 489 png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type, |
| 594 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | 490 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, |
| 595 PNG_FILTER_TYPE_DEFAULT); | 491 PNG_FILTER_TYPE_DEFAULT); |
| 596 | 492 |
| 597 #ifdef PNG_TEXT_SUPPORTED | 493 #ifdef PNG_TEXT_SUPPORTED |
| 598 if (comment_writer.HasComments()) { | 494 if (comment_writer.HasComments()) { |
| 599 png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(), | 495 png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(), |
| 600 comment_writer.size()); | 496 comment_writer.size()); |
| 601 } | 497 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 616 converter(&input[y * row_byte_width], width, row_buffer, NULL); | 512 converter(&input[y * row_byte_width], width, row_buffer, NULL); |
| 617 png_write_row(png_ptr, row_buffer); | 513 png_write_row(png_ptr, row_buffer); |
| 618 } | 514 } |
| 619 delete[] row_buffer; | 515 delete[] row_buffer; |
| 620 } | 516 } |
| 621 | 517 |
| 622 png_write_end(png_ptr, info_ptr); | 518 png_write_end(png_ptr, info_ptr); |
| 623 return true; | 519 return true; |
| 624 } | 520 } |
| 625 | 521 |
| 626 bool EncodeWithCompressionLevel(const unsigned char* input, | 522 } // namespace |
| 627 PNGCodec::ColorFormat format, | 523 |
| 628 const Size& size, | 524 // static |
| 525 bool EncodeWithCompressionLevel(const unsigned char* input, ColorFormat format, |
| 526 const int width, const int height, |
| 629 int row_byte_width, | 527 int row_byte_width, |
| 630 bool discard_transparency, | 528 bool discard_transparency, |
| 631 const std::vector<PNGCodec::Comment>& comments, | 529 const std::vector<Comment>& comments, |
| 632 int compression_level, | 530 int compression_level, |
| 633 std::vector<unsigned char>* output) { | 531 std::vector<unsigned char>* output) { |
| 634 // Run to convert an input row into the output row format, NULL means no | 532 // Run to convert an input row into the output row format, NULL means no |
| 635 // conversion is necessary. | 533 // conversion is necessary. |
| 636 FormatConverter converter = NULL; | 534 FormatConverter converter = NULL; |
| 637 | 535 |
| 638 int input_color_components, output_color_components; | 536 int input_color_components, output_color_components; |
| 639 int png_output_color_type; | 537 int png_output_color_type; |
| 640 switch (format) { | 538 switch (format) { |
| 641 case PNGCodec::FORMAT_RGB: | 539 case FORMAT_RGB: |
| 642 input_color_components = 3; | 540 input_color_components = 3; |
| 643 output_color_components = 3; | 541 output_color_components = 3; |
| 644 png_output_color_type = PNG_COLOR_TYPE_RGB; | 542 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 543 discard_transparency = false; |
| 645 break; | 544 break; |
| 646 | 545 |
| 647 case PNGCodec::FORMAT_RGBA: | 546 case FORMAT_RGBA: |
| 648 input_color_components = 4; | 547 input_color_components = 4; |
| 649 if (discard_transparency) { | 548 if (discard_transparency) { |
| 650 output_color_components = 3; | 549 output_color_components = 3; |
| 651 png_output_color_type = PNG_COLOR_TYPE_RGB; | 550 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 652 converter = ConvertRGBAtoRGB; | 551 converter = ConvertRGBAtoRGB; |
| 653 } else { | 552 } else { |
| 654 output_color_components = 4; | 553 output_color_components = 4; |
| 655 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | 554 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
| 656 converter = NULL; | 555 converter = NULL; |
| 657 } | 556 } |
| 658 break; | 557 break; |
| 659 | 558 |
| 660 case PNGCodec::FORMAT_BGRA: | 559 case FORMAT_BGRA: |
| 661 input_color_components = 4; | 560 input_color_components = 4; |
| 662 if (discard_transparency) { | 561 if (discard_transparency) { |
| 663 output_color_components = 3; | 562 output_color_components = 3; |
| 664 png_output_color_type = PNG_COLOR_TYPE_RGB; | 563 png_output_color_type = PNG_COLOR_TYPE_RGB; |
| 665 converter = ConvertBGRAtoRGB; | 564 converter = ConvertBGRAtoRGB; |
| 666 } else { | 565 } else { |
| 667 output_color_components = 4; | 566 output_color_components = 4; |
| 668 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | 567 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; |
| 669 converter = ConvertBetweenBGRAandRGBA; | 568 converter = ConvertBetweenBGRAandRGBA; |
| 670 } | 569 } |
| 671 break; | 570 break; |
| 672 | 571 |
| 673 case PNGCodec::FORMAT_SkBitmap: | |
| 674 // Compare row_byte_width and size.width() to detect the format of | |
| 675 // SkBitmap. kA8_Config (1bpp) and kARGB_8888_Config (4bpp) are the two | |
| 676 // supported formats. | |
| 677 if (row_byte_width < 4 * size.width()) { | |
| 678 // Not 4bpp, so must be 1bpp. | |
| 679 // Ignore discard_transparency - it doesn't make sense in this context, | |
| 680 // since alpha is the only thing we have and it needs to be used for | |
| 681 // color intensity. | |
| 682 input_color_components = 1; | |
| 683 output_color_components = 1; | |
| 684 png_output_color_type = PNG_COLOR_TYPE_GRAY; | |
| 685 // |converter| is left as null | |
| 686 } else { | |
| 687 input_color_components = 4; | |
| 688 if (discard_transparency) { | |
| 689 output_color_components = 3; | |
| 690 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
| 691 converter = ConvertSkiaToRGB; | |
| 692 } else { | |
| 693 output_color_components = 4; | |
| 694 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
| 695 converter = ConvertSkiaToRGBA; | |
| 696 } | |
| 697 } | |
| 698 break; | |
| 699 | |
| 700 default: | 572 default: |
| 701 NOTREACHED() << "Unknown pixel format"; | 573 NOTREACHED() << "Unknown pixel format"; |
| 702 return false; | 574 return false; |
| 703 } | 575 } |
| 704 | 576 |
| 705 // Row stride should be at least as long as the length of the data. | 577 // Row stride should be at least as long as the length of the data. |
| 706 DCHECK(input_color_components * size.width() <= row_byte_width); | 578 DCHECK(input_color_components * width <= row_byte_width); |
| 707 | 579 |
| 708 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | 580 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, |
| 709 NULL, NULL, NULL); | 581 NULL, NULL, NULL); |
| 710 if (!png_ptr) | 582 if (!png_ptr) |
| 711 return false; | 583 return false; |
| 712 PngWriteStructDestroyer destroyer(&png_ptr); | |
| 713 png_info* info_ptr = png_create_info_struct(png_ptr); | 584 png_info* info_ptr = png_create_info_struct(png_ptr); |
| 714 if (!info_ptr) | 585 if (!info_ptr) { |
| 586 png_destroy_write_struct(&png_ptr, NULL); |
| 715 return false; | 587 return false; |
| 716 destroyer.SetInfoStruct(&info_ptr); | 588 } |
| 717 | |
| 718 output->clear(); | |
| 719 | 589 |
| 720 PngEncoderState state(output); | 590 PngEncoderState state(output); |
| 721 bool success = DoLibpngWrite(png_ptr, info_ptr, &state, | 591 bool success = DoLibpngWrite(png_ptr, info_ptr, &state, |
| 722 size.width(), size.height(), row_byte_width, | 592 width, height, row_byte_width, |
| 723 input, compression_level, png_output_color_type, | 593 input, compression_level, png_output_color_type, |
| 724 output_color_components, converter, comments); | 594 output_color_components, converter, comments); |
| 595 png_destroy_write_struct(&png_ptr, &info_ptr); |
| 725 | 596 |
| 726 return success; | 597 return success; |
| 727 } | 598 } |
| 728 | 599 |
| 729 bool InternalEncodeSkBitmap(const SkBitmap& input, | |
| 730 bool discard_transparency, | |
| 731 int compression_level, | |
| 732 std::vector<unsigned char>* output) { | |
| 733 if (input.empty() || input.isNull()) | |
| 734 return false; | |
| 735 int bpp = input.bytesPerPixel(); | |
| 736 DCHECK(bpp == 1 || bpp == 4); // We support kA8_Config and kARGB_8888_Config. | |
| 737 | |
| 738 SkAutoLockPixels lock_input(input); | |
| 739 unsigned char* inputAddr = bpp == 1 ? | |
| 740 reinterpret_cast<unsigned char*>(input.getAddr8(0, 0)) : | |
| 741 reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)); // bpp = 4 | |
| 742 return EncodeWithCompressionLevel( | |
| 743 inputAddr, | |
| 744 PNGCodec::FORMAT_SkBitmap, | |
| 745 Size(input.width(), input.height()), | |
| 746 static_cast<int>(input.rowBytes()), | |
| 747 discard_transparency, | |
| 748 std::vector<PNGCodec::Comment>(), | |
| 749 compression_level, | |
| 750 output); | |
| 751 } | |
| 752 | |
| 753 | |
| 754 } // namespace | |
| 755 | |
| 756 // static | 600 // static |
| 757 bool PNGCodec::Encode(const unsigned char* input, | 601 bool Encode(const unsigned char* input, ColorFormat format, |
| 758 ColorFormat format, | 602 const int width, const int height, int row_byte_width, |
| 759 const Size& size, | 603 bool discard_transparency, |
| 760 int row_byte_width, | 604 const std::vector<Comment>& comments, |
| 761 bool discard_transparency, | 605 std::vector<unsigned char>* output) { |
| 762 const std::vector<Comment>& comments, | 606 return EncodeWithCompressionLevel(input, format, width, height, |
| 763 std::vector<unsigned char>* output) { | |
| 764 return EncodeWithCompressionLevel(input, | |
| 765 format, | |
| 766 size, | |
| 767 row_byte_width, | 607 row_byte_width, |
| 768 discard_transparency, | 608 discard_transparency, |
| 769 comments, | 609 comments, Z_DEFAULT_COMPRESSION, |
| 770 Z_DEFAULT_COMPRESSION, | |
| 771 output); | 610 output); |
| 772 } | 611 } |
| 773 | 612 |
| 774 // static | 613 // Decode a PNG into an RGBA pixel array. |
| 775 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input, | 614 bool DecodePNG(const unsigned char* input, size_t input_size, |
| 776 bool discard_transparency, | 615 std::vector<unsigned char>* output, |
| 777 std::vector<unsigned char>* output) { | 616 int* width, int* height) { |
| 778 return InternalEncodeSkBitmap(input, | 617 return Decode(input, input_size, FORMAT_RGBA, output, width, height); |
| 779 discard_transparency, | |
| 780 Z_DEFAULT_COMPRESSION, | |
| 781 output); | |
| 782 } | 618 } |
| 783 | 619 |
| 784 // static | 620 // Encode an RGBA pixel array into a PNG. |
| 785 bool PNGCodec::EncodeA8SkBitmap(const SkBitmap& input, | 621 bool EncodeRGBAPNG(const unsigned char* input, |
| 786 std::vector<unsigned char>* output) { | 622 int width, |
| 787 return InternalEncodeSkBitmap(input, | 623 int height, |
| 788 false, | 624 int row_byte_width, |
| 789 Z_DEFAULT_COMPRESSION, | 625 std::vector<unsigned char>* output) { |
| 790 output); | 626 return Encode(input, FORMAT_RGBA, |
| 627 width, height, row_byte_width, false, |
| 628 std::vector<Comment>(), output); |
| 791 } | 629 } |
| 792 | 630 |
| 793 // static | 631 // Encode an BGRA pixel array into a PNG. |
| 794 bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input, | 632 bool EncodeBGRAPNG(const unsigned char* input, |
| 795 bool discard_transparency, | 633 int width, |
| 796 std::vector<unsigned char>* output) { | 634 int height, |
| 797 return InternalEncodeSkBitmap(input, | 635 int row_byte_width, |
| 798 discard_transparency, | 636 bool discard_transparency, |
| 799 Z_BEST_SPEED, | 637 std::vector<unsigned char>* output) { |
| 800 output); | 638 return Encode(input, FORMAT_BGRA, |
| 639 width, height, row_byte_width, discard_transparency, |
| 640 std::vector<Comment>(), output); |
| 801 } | 641 } |
| 802 | 642 |
| 803 PNGCodec::Comment::Comment(const std::string& k, const std::string& t) | 643 } // image_diff_png |
| 804 : key(k), text(t) { | |
| 805 } | |
| 806 | |
| 807 PNGCodec::Comment::~Comment() { | |
| 808 } | |
| 809 | |
| 810 } // namespace gfx | |
| OLD | NEW |