• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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