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 "SkImageEncoderPriv.h"
9
10 #ifdef SK_HAS_PNG_LIBRARY
11
12 #include "SkColor.h"
13 #include "SkColorPriv.h"
14 #include "SkDither.h"
15 #include "SkImageEncoderFns.h"
16 #include "SkMath.h"
17 #include "SkStream.h"
18 #include "SkString.h"
19 #include "SkTemplates.h"
20 #include "SkUnPreMultiply.h"
21 #include "SkUtils.h"
22
23 #include "png.h"
24
25 // Suppress most PNG warnings when calling image decode functions.
26 static const bool c_suppressPNGImageDecoderWarnings = true;
27
sk_error_fn(png_structp png_ptr,png_const_charp msg)28 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
29 if (!c_suppressPNGImageDecoderWarnings) {
30 SkDEBUGF(("------ png error %s\n", msg));
31 }
32 longjmp(png_jmpbuf(png_ptr), 1);
33 }
34
sk_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)35 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
36 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
37 if (!sk_stream->write(data, len)) {
38 png_error(png_ptr, "sk_write_fn Error!");
39 }
40 }
41
set_icc(png_structp png_ptr,png_infop info_ptr,sk_sp<SkData> icc)42 static void set_icc(png_structp png_ptr, png_infop info_ptr, sk_sp<SkData> icc) {
43 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
44 const char* name = "Skia";
45 png_const_bytep iccPtr = icc->bytes();
46 #else
47 SkString str("Skia");
48 char* name = str.writable_str();
49 png_charp iccPtr = (png_charp) icc->writable_data();
50 #endif
51 png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
52 }
53
choose_proc(const SkImageInfo & info,SkTransferFunctionBehavior unpremulBehavior)54 static transform_scanline_proc choose_proc(const SkImageInfo& info,
55 SkTransferFunctionBehavior unpremulBehavior) {
56 const bool isSRGBTransferFn =
57 (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
58 switch (info.colorType()) {
59 case kRGBA_8888_SkColorType:
60 switch (info.alphaType()) {
61 case kOpaque_SkAlphaType:
62 return transform_scanline_RGBX;
63 case kUnpremul_SkAlphaType:
64 return transform_scanline_memcpy;
65 case kPremul_SkAlphaType:
66 return isSRGBTransferFn ? transform_scanline_srgbA :
67 transform_scanline_rgbA;
68 default:
69 SkASSERT(false);
70 return nullptr;
71 }
72 case kBGRA_8888_SkColorType:
73 switch (info.alphaType()) {
74 case kOpaque_SkAlphaType:
75 return transform_scanline_BGRX;
76 case kUnpremul_SkAlphaType:
77 return transform_scanline_BGRA;
78 case kPremul_SkAlphaType:
79 return isSRGBTransferFn ? transform_scanline_sbgrA :
80 transform_scanline_bgrA;
81 default:
82 SkASSERT(false);
83 return nullptr;
84 }
85 case kRGB_565_SkColorType:
86 return transform_scanline_565;
87 case kARGB_4444_SkColorType:
88 switch (info.alphaType()) {
89 case kOpaque_SkAlphaType:
90 return transform_scanline_444;
91 case kPremul_SkAlphaType:
92 // 4444 is assumed to be legacy premul.
93 return transform_scanline_4444;
94 default:
95 SkASSERT(false);
96 return nullptr;
97 }
98 case kIndex_8_SkColorType:
99 case kGray_8_SkColorType:
100 return transform_scanline_memcpy;
101 case kRGBA_F16_SkColorType:
102 switch (info.alphaType()) {
103 case kOpaque_SkAlphaType:
104 case kUnpremul_SkAlphaType:
105 return transform_scanline_F16;
106 case kPremul_SkAlphaType:
107 return transform_scanline_F16_premul;
108 default:
109 SkASSERT(false);
110 return nullptr;
111 }
112 default:
113 SkASSERT(false);
114 return nullptr;
115 }
116 }
117
118 /* Pack palette[] with the corresponding colors, and if the image has alpha, also
119 pack trans[] and return the number of alphas[] entries written. If the image is
120 opaque, the return value will always be 0.
121 */
pack_palette(SkColorTable * ctable,png_color * SK_RESTRICT palette,png_byte * SK_RESTRICT alphas,const SkImageInfo & info,SkTransferFunctionBehavior unpremulBehavior)122 static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette,
123 png_byte* SK_RESTRICT alphas, const SkImageInfo& info,
124 SkTransferFunctionBehavior unpremulBehavior) {
125 const SkPMColor* colors = ctable->readColors();
126 const int count = ctable->count();
127 SkPMColor storage[256];
128 if (kPremul_SkAlphaType == info.alphaType()) {
129 // Unpremultiply the colors.
130 const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType);
131 transform_scanline_proc proc = choose_proc(rgbaInfo, unpremulBehavior);
132 proc((char*) storage, (const char*) colors, ctable->count(), 4, nullptr);
133 colors = storage;
134 }
135
136 int numWithAlpha = 0;
137 if (kOpaque_SkAlphaType != info.alphaType()) {
138 // PNG requires that all non-opaque colors come first in the palette. Write these first.
139 for (int i = 0; i < count; i++) {
140 uint8_t alpha = SkGetPackedA32(colors[i]);
141 if (0xFF != alpha) {
142 alphas[numWithAlpha] = alpha;
143 palette[numWithAlpha].red = SkGetPackedR32(colors[i]);
144 palette[numWithAlpha].green = SkGetPackedG32(colors[i]);
145 palette[numWithAlpha].blue = SkGetPackedB32(colors[i]);
146 numWithAlpha++;
147 }
148 }
149
150 }
151
152 if (0 == numWithAlpha) {
153 // All of the entries are opaque.
154 for (int i = 0; i < count; i++) {
155 SkPMColor c = *colors++;
156 palette[i].red = SkGetPackedR32(c);
157 palette[i].green = SkGetPackedG32(c);
158 palette[i].blue = SkGetPackedB32(c);
159 }
160 } else {
161 // We have already written the non-opaque colors. Now just write the opaque colors.
162 int currIndex = numWithAlpha;
163 int i = 0;
164 while (currIndex != count) {
165 uint8_t alpha = SkGetPackedA32(colors[i]);
166 if (0xFF == alpha) {
167 palette[currIndex].red = SkGetPackedR32(colors[i]);
168 palette[currIndex].green = SkGetPackedG32(colors[i]);
169 palette[currIndex].blue = SkGetPackedB32(colors[i]);
170 currIndex++;
171 }
172
173 i++;
174 }
175 }
176
177 return numWithAlpha;
178 }
179
180 static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&,
181 SkTransferFunctionBehavior unpremulBehavior);
182
SkEncodeImageAsPNG(SkWStream * stream,const SkPixmap & pixmap,const SkEncodeOptions & opts)183 bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap, const SkEncodeOptions& opts) {
184 if (SkTransferFunctionBehavior::kRespect == opts.fUnpremulBehavior) {
185 if (!pixmap.colorSpace() || (!pixmap.colorSpace()->gammaCloseToSRGB() &&
186 !pixmap.colorSpace()->gammaIsLinear())) {
187 return false;
188 }
189 }
190
191 if (!pixmap.addr() || pixmap.info().isEmpty()) {
192 return false;
193 }
194
195 const SkColorType colorType = pixmap.colorType();
196 const SkAlphaType alphaType = pixmap.alphaType();
197 switch (alphaType) {
198 case kUnpremul_SkAlphaType:
199 if (kARGB_4444_SkColorType == colorType) {
200 return false;
201 }
202
203 break;
204 case kOpaque_SkAlphaType:
205 case kPremul_SkAlphaType:
206 break;
207 default:
208 return false;
209 }
210
211 const bool isOpaque = (kOpaque_SkAlphaType == alphaType);
212 int bitDepth = 8;
213 png_color_8 sig_bit;
214 sk_bzero(&sig_bit, sizeof(png_color_8));
215 int pngColorType;
216 switch (colorType) {
217 case kRGBA_F16_SkColorType:
218 if (!pixmap.colorSpace() || !pixmap.colorSpace()->gammaIsLinear()) {
219 return false;
220 }
221
222 sig_bit.red = 16;
223 sig_bit.green = 16;
224 sig_bit.blue = 16;
225 sig_bit.alpha = 16;
226 bitDepth = 16;
227 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
228 break;
229 case kIndex_8_SkColorType:
230 sig_bit.red = 8;
231 sig_bit.green = 8;
232 sig_bit.blue = 8;
233 sig_bit.alpha = 8;
234 pngColorType = PNG_COLOR_TYPE_PALETTE;
235 break;
236 case kGray_8_SkColorType:
237 sig_bit.gray = 8;
238 pngColorType = PNG_COLOR_TYPE_GRAY;
239 SkASSERT(isOpaque);
240 break;
241 case kRGBA_8888_SkColorType:
242 case kBGRA_8888_SkColorType:
243 sig_bit.red = 8;
244 sig_bit.green = 8;
245 sig_bit.blue = 8;
246 sig_bit.alpha = 8;
247 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
248 break;
249 case kARGB_4444_SkColorType:
250 sig_bit.red = 4;
251 sig_bit.green = 4;
252 sig_bit.blue = 4;
253 sig_bit.alpha = 4;
254 pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
255 break;
256 case kRGB_565_SkColorType:
257 sig_bit.red = 5;
258 sig_bit.green = 6;
259 sig_bit.blue = 5;
260 pngColorType = PNG_COLOR_TYPE_RGB;
261 SkASSERT(isOpaque);
262 break;
263 default:
264 return false;
265 }
266
267 if (kIndex_8_SkColorType == colorType) {
268 SkColorTable* ctable = pixmap.ctable();
269 if (!ctable || ctable->count() == 0) {
270 return false;
271 }
272
273 // Currently, we always use 8-bit indices for paletted pngs.
274 // When ctable->count() <= 16, we could potentially use 1, 2,
275 // or 4 bit indices.
276 }
277
278 return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit, opts.fUnpremulBehavior);
279 }
280
num_components(int pngColorType)281 static int num_components(int pngColorType) {
282 switch (pngColorType) {
283 case PNG_COLOR_TYPE_PALETTE:
284 case PNG_COLOR_TYPE_GRAY:
285 return 1;
286 case PNG_COLOR_TYPE_RGB:
287 return 3;
288 case PNG_COLOR_TYPE_RGBA:
289 return 4;
290 default:
291 SkASSERT(false);
292 return 0;
293 }
294 }
295
do_encode(SkWStream * stream,const SkPixmap & pixmap,int pngColorType,int bitDepth,png_color_8 & sig_bit,SkTransferFunctionBehavior unpremulBehavior)296 static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, int pngColorType, int bitDepth,
297 png_color_8& sig_bit, SkTransferFunctionBehavior unpremulBehavior) {
298 png_structp png_ptr;
299 png_infop info_ptr;
300
301 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
302 if (nullptr == png_ptr) {
303 return false;
304 }
305
306 info_ptr = png_create_info_struct(png_ptr);
307 if (nullptr == info_ptr) {
308 png_destroy_write_struct(&png_ptr, nullptr);
309 return false;
310 }
311
312 /* Set error handling. REQUIRED if you aren't supplying your own
313 * error handling functions in the png_create_write_struct() call.
314 */
315 if (setjmp(png_jmpbuf(png_ptr))) {
316 png_destroy_write_struct(&png_ptr, &info_ptr);
317 return false;
318 }
319
320 png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, nullptr);
321
322 /* Set the image information here. Width and height are up to 2^31,
323 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
324 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
325 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
326 * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
327 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
328 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
329 */
330
331 png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(),
332 bitDepth, pngColorType,
333 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
334 PNG_FILTER_TYPE_BASE);
335
336 // set our colortable/trans arrays if needed
337 png_color paletteColors[256];
338 png_byte trans[256];
339 if (kIndex_8_SkColorType == pixmap.colorType()) {
340 SkColorTable* colorTable = pixmap.ctable();
341 SkASSERT(colorTable);
342 int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info(),
343 unpremulBehavior);
344 png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count());
345 if (numTrans > 0) {
346 png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr);
347 }
348 }
349
350 if (pixmap.colorSpace() && pixmap.colorSpace()->isSRGB()) {
351 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
352 } else {
353 sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
354 if (icc) {
355 set_icc(png_ptr, info_ptr, std::move(icc));
356 }
357 }
358
359 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
360 png_write_info(png_ptr, info_ptr);
361 int pngBytesPerPixel = num_components(pngColorType) * (bitDepth / 8);
362 if (kRGBA_F16_SkColorType == pixmap.colorType() && kOpaque_SkAlphaType == pixmap.alphaType()) {
363 // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
364 // to skip the alpha channel.
365 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
366 pngBytesPerPixel = 8;
367 }
368
369 SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() * pngBytesPerPixel);
370 char* storage = rowStorage.get();
371 const char* srcImage = (const char*)pixmap.addr();
372 transform_scanline_proc proc = choose_proc(pixmap.info(), unpremulBehavior);
373 for (int y = 0; y < pixmap.height(); y++) {
374 png_bytep row_ptr = (png_bytep)storage;
375 proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType()),
376 nullptr);
377 png_write_rows(png_ptr, &row_ptr, 1);
378 srcImage += pixmap.rowBytes();
379 }
380
381 png_write_end(png_ptr, info_ptr);
382
383 /* clean up after the write, and free any memory allocated */
384 png_destroy_write_struct(&png_ptr, &info_ptr);
385 return true;
386 }
387
388 #endif
389