OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/basictypes.h" | |
6 #include "base/gfx/png_encoder.h" | |
7 #include "base/logging.h" | |
8 #include "base/scoped_ptr.h" | |
9 #include "third_party/skia/include/core/SkBitmap.h" | |
10 #include "third_party/skia/include/core/SkUnPreMultiply.h" | |
11 | |
12 extern "C" { | |
13 #if defined(USE_SYSTEM_LIBPNG) | |
14 #include <png.h> | |
15 #else | |
16 #include "third_party/libpng/png.h" | |
17 #endif | |
18 } | |
19 | |
20 namespace { | |
21 | |
22 // Converts BGRA->RGBA and RGBA->BGRA. | |
23 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, | |
24 unsigned char* output) { | |
25 for (int x = 0; x < pixel_width; x++) { | |
26 const unsigned char* pixel_in = &input[x * 4]; | |
27 unsigned char* pixel_out = &output[x * 4]; | |
28 pixel_out[0] = pixel_in[2]; | |
29 pixel_out[1] = pixel_in[1]; | |
30 pixel_out[2] = pixel_in[0]; | |
31 pixel_out[3] = pixel_in[3]; | |
32 } | |
33 } | |
34 | |
35 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, | |
36 unsigned char* rgb) { | |
37 for (int x = 0; x < pixel_width; x++) { | |
38 const unsigned char* pixel_in = &rgba[x * 4]; | |
39 unsigned char* pixel_out = &rgb[x * 3]; | |
40 pixel_out[0] = pixel_in[0]; | |
41 pixel_out[1] = pixel_in[1]; | |
42 pixel_out[2] = pixel_in[2]; | |
43 } | |
44 } | |
45 | |
46 } // namespace | |
47 | |
48 // Encoder -------------------------------------------------------------------- | |
49 // | |
50 // This section of the code is based on nsPNGEncoder.cpp in Mozilla | |
51 // (Copyright 2005 Google Inc.) | |
52 | |
53 namespace { | |
54 | |
55 // Passed around as the io_ptr in the png structs so our callbacks know where | |
56 // to write data. | |
57 struct PngEncoderState { | |
58 PngEncoderState(std::vector<unsigned char>* o) : out(o) {} | |
59 std::vector<unsigned char>* out; | |
60 }; | |
61 | |
62 // Called by libpng to flush its internal buffer to ours. | |
63 void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) { | |
64 PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png)); | |
65 DCHECK(state->out); | |
66 | |
67 size_t old_size = state->out->size(); | |
68 state->out->resize(old_size + size); | |
69 memcpy(&(*state->out)[old_size], data, size); | |
70 } | |
71 | |
72 void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width, | |
73 unsigned char* rgb) { | |
74 for (int x = 0; x < pixel_width; x++) { | |
75 const unsigned char* pixel_in = &bgra[x * 4]; | |
76 unsigned char* pixel_out = &rgb[x * 3]; | |
77 pixel_out[0] = pixel_in[2]; | |
78 pixel_out[1] = pixel_in[1]; | |
79 pixel_out[2] = pixel_in[0]; | |
80 } | |
81 } | |
82 | |
83 // Automatically destroys the given write structs on destruction to make | |
84 // cleanup and error handling code cleaner. | |
85 class PngWriteStructDestroyer { | |
86 public: | |
87 PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { | |
88 } | |
89 ~PngWriteStructDestroyer() { | |
90 png_destroy_write_struct(ps_, pi_); | |
91 } | |
92 private: | |
93 png_struct** ps_; | |
94 png_info** pi_; | |
95 | |
96 DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer); | |
97 }; | |
98 | |
99 } // namespace | |
100 | |
101 // static | |
102 bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format, | |
103 int w, int h, int row_byte_width, | |
104 bool discard_transparency, | |
105 std::vector<unsigned char>* output) { | |
106 // Run to convert an input row into the output row format, NULL means no | |
107 // conversion is necessary. | |
108 void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL; | |
109 | |
110 int input_color_components, output_color_components; | |
111 int png_output_color_type; | |
112 switch (format) { | |
113 case FORMAT_RGB: | |
114 input_color_components = 3; | |
115 output_color_components = 3; | |
116 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
117 discard_transparency = false; | |
118 break; | |
119 | |
120 case FORMAT_RGBA: | |
121 input_color_components = 4; | |
122 if (discard_transparency) { | |
123 output_color_components = 3; | |
124 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
125 converter = ConvertRGBAtoRGB; | |
126 } else { | |
127 output_color_components = 4; | |
128 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
129 converter = NULL; | |
130 } | |
131 break; | |
132 | |
133 case FORMAT_BGRA: | |
134 input_color_components = 4; | |
135 if (discard_transparency) { | |
136 output_color_components = 3; | |
137 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
138 converter = ConvertBGRAtoRGB; | |
139 } else { | |
140 output_color_components = 4; | |
141 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
142 converter = ConvertBetweenBGRAandRGBA; | |
143 } | |
144 break; | |
145 | |
146 default: | |
147 NOTREACHED() << "Unknown pixel format"; | |
148 return false; | |
149 } | |
150 | |
151 // Row stride should be at least as long as the length of the data. | |
152 DCHECK(input_color_components * w <= row_byte_width); | |
153 | |
154 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | |
155 png_voidp_NULL, | |
156 png_error_ptr_NULL, | |
157 png_error_ptr_NULL); | |
158 if (!png_ptr) | |
159 return false; | |
160 png_info* info_ptr = png_create_info_struct(png_ptr); | |
161 if (!info_ptr) { | |
162 png_destroy_write_struct(&png_ptr, NULL); | |
163 return false; | |
164 } | |
165 PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr); | |
166 | |
167 if (setjmp(png_jmpbuf(png_ptr))) { | |
168 // The destroyer will ensure that the structures are cleaned up in this | |
169 // case, even though we may get here as a jump from random parts of the | |
170 // PNG library called below. | |
171 return false; | |
172 } | |
173 | |
174 // Set our callback for libpng to give us the data. | |
175 PngEncoderState state(output); | |
176 png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL); | |
177 | |
178 png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type, | |
179 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |
180 PNG_FILTER_TYPE_DEFAULT); | |
181 png_write_info(png_ptr, info_ptr); | |
182 | |
183 if (!converter) { | |
184 // No conversion needed, give the data directly to libpng. | |
185 for (int y = 0; y < h; y ++) | |
186 png_write_row(png_ptr, | |
187 const_cast<unsigned char*>(&input[y * row_byte_width])); | |
188 } else { | |
189 // Needs conversion using a separate buffer. | |
190 unsigned char* row = new unsigned char[w * output_color_components]; | |
191 for (int y = 0; y < h; y ++) { | |
192 converter(&input[y * row_byte_width], w, row); | |
193 png_write_row(png_ptr, row); | |
194 } | |
195 delete[] row; | |
196 } | |
197 | |
198 png_write_end(png_ptr, info_ptr); | |
199 return true; | |
200 } | |
201 | |
202 // static | |
203 bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input, | |
204 bool discard_transparency, | |
205 std::vector<unsigned char>* output) { | |
206 static const int bbp = 4; | |
207 | |
208 SkAutoLockPixels lock_input(input); | |
209 DCHECK(input.empty() || input.bytesPerPixel() == bbp); | |
210 | |
211 // SkBitmaps are premultiplied, we need to unpremultiply them. | |
212 scoped_array<unsigned char> divided( | |
213 new unsigned char[input.width() * input.height() * bbp]); | |
214 | |
215 int i = 0; | |
216 for (int y = 0; y < input.height(); y++) { | |
217 for (int x = 0; x < input.width(); x++) { | |
218 uint32 pixel = input.getAddr32(0, y)[x]; | |
219 | |
220 int alpha = SkColorGetA(pixel); | |
221 if (alpha != 0 && alpha != 255) { | |
222 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); | |
223 divided[i + 0] = SkColorGetR(unmultiplied); | |
224 divided[i + 1] = SkColorGetG(unmultiplied); | |
225 divided[i + 2] = SkColorGetB(unmultiplied); | |
226 divided[i + 3] = alpha; | |
227 } else { | |
228 divided[i + 0] = SkColorGetR(pixel); | |
229 divided[i + 1] = SkColorGetG(pixel); | |
230 divided[i + 2] = SkColorGetB(pixel); | |
231 divided[i + 3] = alpha; | |
232 } | |
233 i += bbp; | |
234 } | |
235 } | |
236 | |
237 return Encode(divided.get(), | |
238 PNGEncoder::FORMAT_RGBA, input.width(), input.height(), | |
239 input.width() * bbp, discard_transparency, output); | |
240 } | |
OLD | NEW |