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 |