OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2006 The Android Open Source Project | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "SkImageEncoder.h" | |
9 #include "SkColor.h" | |
10 #include "SkColorPriv.h" | |
11 #include "SkDither.h" | |
12 #include "SkMath.h" | |
13 #include "SkRTConf.h" | |
14 #include "SkStream.h" | |
15 #include "SkTemplates.h" | |
16 #include "SkUtils.h" | |
17 #include "transform_scanline.h" | |
18 | |
19 #include "png.h" | |
20 | |
21 /* These were dropped in libpng >= 1.4 */ | |
22 #ifndef png_infopp_NULL | |
23 #define png_infopp_NULL nullptr | |
24 #endif | |
25 | |
26 #ifndef png_bytepp_NULL | |
27 #define png_bytepp_NULL nullptr | |
28 #endif | |
29 | |
30 #ifndef int_p_NULL | |
31 #define int_p_NULL nullptr | |
32 #endif | |
33 | |
34 #ifndef png_flush_ptr_NULL | |
35 #define png_flush_ptr_NULL nullptr | |
36 #endif | |
37 | |
38 #define DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS true | |
39 SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings, | |
40 "images.png.suppressDecoderWarnings", | |
41 DEFAULT_FOR_SUPPRESS_PNG_IMAGE_DECODER_WARNINGS, | |
42 "Suppress most PNG warnings when calling image decode " | |
43 "functions."); | |
44 | |
45 /////////////////////////////////////////////////////////////////////////////// | |
46 | |
47 #include "SkColorPriv.h" | |
48 #include "SkUnPreMultiply.h" | |
49 | |
50 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { | |
51 if (!c_suppressPNGImageDecoderWarnings) { | |
52 SkDEBUGF(("------ png error %s\n", msg)); | |
53 } | |
54 longjmp(png_jmpbuf(png_ptr), 1); | |
55 } | |
56 | |
57 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { | |
58 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); | |
59 if (!sk_stream->write(data, len)) { | |
60 png_error(png_ptr, "sk_write_fn Error!"); | |
61 } | |
62 } | |
63 | |
64 static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) { | |
65 // we don't care about search on alpha if we're kIndex8, since only the | |
66 // colortable packing cares about that distinction, not the pixels | |
67 if (kIndex_8_SkColorType == ct) { | |
68 hasAlpha = false; // we store false in the table entries for kIndex8 | |
69 } | |
70 | |
71 static const struct { | |
72 SkColorType fColorType; | |
73 bool fHasAlpha; | |
74 transform_scanline_proc fProc; | |
75 } gMap[] = { | |
76 { kRGB_565_SkColorType, false, transform_scanline_565 }, | |
77 { kN32_SkColorType, false, transform_scanline_888 }, | |
78 { kN32_SkColorType, true, transform_scanline_8888 }, | |
79 { kARGB_4444_SkColorType, false, transform_scanline_444 }, | |
80 { kARGB_4444_SkColorType, true, transform_scanline_4444 }, | |
81 { kIndex_8_SkColorType, false, transform_scanline_memcpy }, | |
82 }; | |
83 | |
84 for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) { | |
85 if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) { | |
86 return gMap[i].fProc; | |
87 } | |
88 } | |
89 sk_throw(); | |
90 return nullptr; | |
91 } | |
92 | |
93 // return the minimum legal bitdepth (by png standards) for this many colortable | |
94 // entries. SkBitmap always stores in 8bits per pixel, but for colorcount <= 16, | |
95 // we can use fewer bits per in png | |
96 static int computeBitDepth(int colorCount) { | |
97 #if 0 | |
98 int bits = SkNextLog2(colorCount); | |
99 SkASSERT(bits >= 1 && bits <= 8); | |
100 // now we need bits itself to be a power of 2 (e.g. 1, 2, 4, 8) | |
101 return SkNextPow2(bits); | |
102 #else | |
103 // for the moment, we don't know how to pack bitdepth < 8 | |
104 return 8; | |
105 #endif | |
106 } | |
107 | |
108 /* Pack palette[] with the corresponding colors, and if hasAlpha is true, also | |
109 pack trans[] and return the number of trans[] entries written. If hasAlpha | |
110 is false, the return value will always be 0. | |
111 | |
112 Note: this routine takes care of unpremultiplying the RGB values when we | |
113 have alpha in the colortable, since png doesn't support premul colors | |
114 */ | |
115 static inline int pack_palette(SkColorTable* ctable, | |
116 png_color* SK_RESTRICT palette, | |
117 png_byte* SK_RESTRICT trans, bool hasAlpha) { | |
118 const SkPMColor* SK_RESTRICT colors = ctable ? ctable->readColors() : nullpt
r; | |
119 const int ctCount = ctable->count(); | |
120 int i, num_trans = 0; | |
121 | |
122 if (hasAlpha) { | |
123 /* first see if we have some number of fully opaque at the end of the | |
124 ctable. PNG allows num_trans < num_palette, but all of the trans | |
125 entries must come first in the palette. If I was smarter, I'd | |
126 reorder the indices and ctable so that all non-opaque colors came | |
127 first in the palette. But, since that would slow down the encode, | |
128 I'm leaving the indices and ctable order as is, and just looking | |
129 at the tail of the ctable for opaqueness. | |
130 */ | |
131 num_trans = ctCount; | |
132 for (i = ctCount - 1; i >= 0; --i) { | |
133 if (SkGetPackedA32(colors[i]) != 0xFF) { | |
134 break; | |
135 } | |
136 num_trans -= 1; | |
137 } | |
138 | |
139 const SkUnPreMultiply::Scale* SK_RESTRICT table = | |
140 SkUnPreMultiply::GetScaleTable(); | |
141 | |
142 for (i = 0; i < num_trans; i++) { | |
143 const SkPMColor c = *colors++; | |
144 const unsigned a = SkGetPackedA32(c); | |
145 const SkUnPreMultiply::Scale s = table[a]; | |
146 trans[i] = a; | |
147 palette[i].red = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); | |
148 palette[i].green = SkUnPreMultiply::ApplyScale(s,SkGetPackedG32(c)); | |
149 palette[i].blue = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); | |
150 } | |
151 // now fall out of this if-block to use common code for the trailing | |
152 // opaque entries | |
153 } | |
154 | |
155 // these (remaining) entries are opaque | |
156 for (i = num_trans; i < ctCount; i++) { | |
157 SkPMColor c = *colors++; | |
158 palette[i].red = SkGetPackedR32(c); | |
159 palette[i].green = SkGetPackedG32(c); | |
160 palette[i].blue = SkGetPackedB32(c); | |
161 } | |
162 return num_trans; | |
163 } | |
164 | |
165 class SkPNGImageEncoder : public SkImageEncoder { | |
166 protected: | |
167 bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override; | |
168 private: | |
169 bool doEncode(SkWStream* stream, const SkBitmap& bm, | |
170 const bool& hasAlpha, int colorType, | |
171 int bitDepth, SkColorType ct, | |
172 png_color_8& sig_bit); | |
173 | |
174 typedef SkImageEncoder INHERITED; | |
175 }; | |
176 | |
177 bool SkPNGImageEncoder::onEncode(SkWStream* stream, | |
178 const SkBitmap& originalBitmap, | |
179 int /*quality*/) { | |
180 SkBitmap copy; | |
181 const SkBitmap* bitmap = &originalBitmap; | |
182 switch (originalBitmap.colorType()) { | |
183 case kIndex_8_SkColorType: | |
184 case kN32_SkColorType: | |
185 case kARGB_4444_SkColorType: | |
186 case kRGB_565_SkColorType: | |
187 break; | |
188 default: | |
189 // TODO(scroggo): support 8888-but-not-N32 natively. | |
190 // TODO(scroggo): support kGray_8 directly. | |
191 // TODO(scroggo): support Alpha_8 as Grayscale(black)+Alpha | |
192 if (originalBitmap.copyTo(©, kN32_SkColorType)) { | |
193 bitmap = © | |
194 } | |
195 } | |
196 SkColorType ct = bitmap->colorType(); | |
197 | |
198 const bool hasAlpha = !bitmap->isOpaque(); | |
199 int colorType = PNG_COLOR_MASK_COLOR; | |
200 int bitDepth = 8; // default for color | |
201 png_color_8 sig_bit; | |
202 | |
203 switch (ct) { | |
204 case kIndex_8_SkColorType: | |
205 colorType |= PNG_COLOR_MASK_PALETTE; | |
206 // fall through to the ARGB_8888 case | |
207 case kN32_SkColorType: | |
208 sig_bit.red = 8; | |
209 sig_bit.green = 8; | |
210 sig_bit.blue = 8; | |
211 sig_bit.alpha = 8; | |
212 break; | |
213 case kARGB_4444_SkColorType: | |
214 sig_bit.red = 4; | |
215 sig_bit.green = 4; | |
216 sig_bit.blue = 4; | |
217 sig_bit.alpha = 4; | |
218 break; | |
219 case kRGB_565_SkColorType: | |
220 sig_bit.red = 5; | |
221 sig_bit.green = 6; | |
222 sig_bit.blue = 5; | |
223 sig_bit.alpha = 0; | |
224 break; | |
225 default: | |
226 return false; | |
227 } | |
228 | |
229 if (hasAlpha) { | |
230 // don't specify alpha if we're a palette, even if our ctable has alpha | |
231 if (!(colorType & PNG_COLOR_MASK_PALETTE)) { | |
232 colorType |= PNG_COLOR_MASK_ALPHA; | |
233 } | |
234 } else { | |
235 sig_bit.alpha = 0; | |
236 } | |
237 | |
238 SkAutoLockPixels alp(*bitmap); | |
239 // readyToDraw checks for pixels (and colortable if that is required) | |
240 if (!bitmap->readyToDraw()) { | |
241 return false; | |
242 } | |
243 | |
244 // we must do this after we have locked the pixels | |
245 SkColorTable* ctable = bitmap->getColorTable(); | |
246 if (ctable) { | |
247 if (ctable->count() == 0) { | |
248 return false; | |
249 } | |
250 // check if we can store in fewer than 8 bits | |
251 bitDepth = computeBitDepth(ctable->count()); | |
252 } | |
253 | |
254 return doEncode(stream, *bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit)
; | |
255 } | |
256 | |
257 bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, | |
258 const bool& hasAlpha, int colorType, | |
259 int bitDepth, SkColorType ct, | |
260 png_color_8& sig_bit) { | |
261 | |
262 png_structp png_ptr; | |
263 png_infop info_ptr; | |
264 | |
265 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_f
n, | |
266 nullptr); | |
267 if (nullptr == png_ptr) { | |
268 return false; | |
269 } | |
270 | |
271 info_ptr = png_create_info_struct(png_ptr); | |
272 if (nullptr == info_ptr) { | |
273 png_destroy_write_struct(&png_ptr, png_infopp_NULL); | |
274 return false; | |
275 } | |
276 | |
277 /* Set error handling. REQUIRED if you aren't supplying your own | |
278 * error handling functions in the png_create_write_struct() call. | |
279 */ | |
280 if (setjmp(png_jmpbuf(png_ptr))) { | |
281 png_destroy_write_struct(&png_ptr, &info_ptr); | |
282 return false; | |
283 } | |
284 | |
285 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL); | |
286 | |
287 /* Set the image information here. Width and height are up to 2^31, | |
288 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on | |
289 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, | |
290 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, | |
291 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or | |
292 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST | |
293 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED | |
294 */ | |
295 | |
296 png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(), | |
297 bitDepth, colorType, | |
298 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, | |
299 PNG_FILTER_TYPE_BASE); | |
300 | |
301 // set our colortable/trans arrays if needed | |
302 png_color paletteColors[256]; | |
303 png_byte trans[256]; | |
304 if (kIndex_8_SkColorType == ct) { | |
305 SkColorTable* ct = bitmap.getColorTable(); | |
306 int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha); | |
307 png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count()); | |
308 if (numTrans > 0) { | |
309 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr); | |
310 } | |
311 } | |
312 #ifdef PNG_sBIT_SUPPORTED | |
313 png_set_sBIT(png_ptr, info_ptr, &sig_bit); | |
314 #endif | |
315 png_write_info(png_ptr, info_ptr); | |
316 | |
317 const char* srcImage = (const char*)bitmap.getPixels(); | |
318 SkAutoSTMalloc<1024, char> rowStorage(bitmap.width() << 2); | |
319 char* storage = rowStorage.get(); | |
320 transform_scanline_proc proc = choose_proc(ct, hasAlpha); | |
321 | |
322 for (int y = 0; y < bitmap.height(); y++) { | |
323 png_bytep row_ptr = (png_bytep)storage; | |
324 proc(srcImage, bitmap.width(), storage); | |
325 png_write_rows(png_ptr, &row_ptr, 1); | |
326 srcImage += bitmap.rowBytes(); | |
327 } | |
328 | |
329 png_write_end(png_ptr, info_ptr); | |
330 | |
331 /* clean up after the write, and free any memory allocated */ | |
332 png_destroy_write_struct(&png_ptr, &info_ptr); | |
333 return true; | |
334 } | |
335 | |
336 /////////////////////////////////////////////////////////////////////////////// | |
337 DEFINE_ENCODER_CREATOR(PNGImageEncoder); | |
338 /////////////////////////////////////////////////////////////////////////////// | |
339 | |
340 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) { | |
341 return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr; | |
342 } | |
343 | |
344 static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory); | |
OLD | NEW |