• 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 "SkColorTable.h"
13 #include "SkImageEncoderFns.h"
14 #include "SkImageInfoPriv.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkPngEncoder.h"
18 #include "SkPngPriv.h"
19 
20 #include "png.h"
21 
22 static_assert(PNG_FILTER_NONE  == (int)SkPngEncoder::FilterFlag::kNone,  "Skia libpng filter err.");
23 static_assert(PNG_FILTER_SUB   == (int)SkPngEncoder::FilterFlag::kSub,   "Skia libpng filter err.");
24 static_assert(PNG_FILTER_UP    == (int)SkPngEncoder::FilterFlag::kUp,    "Skia libpng filter err.");
25 static_assert(PNG_FILTER_AVG   == (int)SkPngEncoder::FilterFlag::kAvg,   "Skia libpng filter err.");
26 static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err.");
27 static_assert(PNG_ALL_FILTERS  == (int)SkPngEncoder::FilterFlag::kAll,   "Skia libpng filter err.");
28 
29 static constexpr bool kSuppressPngEncodeWarnings = true;
30 
sk_error_fn(png_structp png_ptr,png_const_charp msg)31 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
32     if (!kSuppressPngEncodeWarnings) {
33         SkDebugf("libpng encode error: %s\n", msg);
34     }
35 
36     longjmp(png_jmpbuf(png_ptr), 1);
37 }
38 
sk_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)39 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
40     SkWStream* stream = (SkWStream*)png_get_io_ptr(png_ptr);
41     if (!stream->write(data, len)) {
42         png_error(png_ptr, "sk_write_fn cannot write to stream");
43     }
44 }
45 
46 class SkPngEncoderMgr final : SkNoncopyable {
47 public:
48 
49     /*
50      * Create the decode manager
51      * Does not take ownership of stream
52      */
53     static std::unique_ptr<SkPngEncoderMgr> Make(SkWStream* stream);
54 
55     bool setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options);
56     bool setColorSpace(const SkImageInfo& info);
57     bool writeInfo(const SkImageInfo& srcInfo);
58     void chooseProc(const SkImageInfo& srcInfo, SkTransferFunctionBehavior unpremulBehavior);
59 
pngPtr()60     png_structp pngPtr() { return fPngPtr; }
infoPtr()61     png_infop infoPtr() { return fInfoPtr; }
pngBytesPerPixel() const62     int pngBytesPerPixel() const { return fPngBytesPerPixel; }
proc() const63     transform_scanline_proc proc() const { return fProc; }
64 
~SkPngEncoderMgr()65     ~SkPngEncoderMgr() {
66         png_destroy_write_struct(&fPngPtr, &fInfoPtr);
67     }
68 
69 private:
70 
SkPngEncoderMgr(png_structp pngPtr,png_infop infoPtr)71     SkPngEncoderMgr(png_structp pngPtr, png_infop infoPtr)
72         : fPngPtr(pngPtr)
73         , fInfoPtr(infoPtr)
74     {}
75 
76     png_structp             fPngPtr;
77     png_infop               fInfoPtr;
78     int                     fPngBytesPerPixel;
79     transform_scanline_proc fProc;
80 };
81 
Make(SkWStream * stream)82 std::unique_ptr<SkPngEncoderMgr> SkPngEncoderMgr::Make(SkWStream* stream) {
83     png_structp pngPtr =
84             png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
85     if (!pngPtr) {
86         return nullptr;
87     }
88 
89     png_infop infoPtr = png_create_info_struct(pngPtr);
90     if (!infoPtr) {
91         png_destroy_write_struct(&pngPtr, nullptr);
92         return nullptr;
93     }
94 
95     png_set_write_fn(pngPtr, (void*)stream, sk_write_fn, nullptr);
96     return std::unique_ptr<SkPngEncoderMgr>(new SkPngEncoderMgr(pngPtr, infoPtr));
97 }
98 
setHeader(const SkImageInfo & srcInfo,const SkPngEncoder::Options & options)99 bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) {
100     if (setjmp(png_jmpbuf(fPngPtr))) {
101         return false;
102     }
103 
104     int pngColorType;
105     png_color_8 sigBit;
106     int bitDepth = 8;
107     switch (srcInfo.colorType()) {
108         case kRGBA_F16_SkColorType:
109             SkASSERT(srcInfo.colorSpace() && srcInfo.colorSpace()->gammaIsLinear());
110             sigBit.red = 16;
111             sigBit.green = 16;
112             sigBit.blue = 16;
113             sigBit.alpha = 16;
114             bitDepth = 16;
115             pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
116             fPngBytesPerPixel = 8;
117             break;
118         case kGray_8_SkColorType:
119             sigBit.gray = 8;
120             pngColorType = PNG_COLOR_TYPE_GRAY;
121             fPngBytesPerPixel = 1;
122             SkASSERT(srcInfo.isOpaque());
123             break;
124         case kRGBA_8888_SkColorType:
125         case kBGRA_8888_SkColorType:
126             sigBit.red = 8;
127             sigBit.green = 8;
128             sigBit.blue = 8;
129             sigBit.alpha = 8;
130             pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
131             fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
132             break;
133         case kRGB_888x_SkColorType:
134             sigBit.red   = 8;
135             sigBit.green = 8;
136             sigBit.blue  = 8;
137             pngColorType = PNG_COLOR_TYPE_RGB;
138             fPngBytesPerPixel = 3;
139             SkASSERT(srcInfo.isOpaque());
140             break;
141         case kARGB_4444_SkColorType:
142             if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
143                 return false;
144             }
145 
146             sigBit.red = 4;
147             sigBit.green = 4;
148             sigBit.blue = 4;
149             sigBit.alpha = 4;
150             pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
151             fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
152             break;
153         case kRGB_565_SkColorType:
154             sigBit.red = 5;
155             sigBit.green = 6;
156             sigBit.blue = 5;
157             pngColorType = PNG_COLOR_TYPE_RGB;
158             fPngBytesPerPixel = 3;
159             SkASSERT(srcInfo.isOpaque());
160             break;
161         case kAlpha_8_SkColorType:  // store as gray+alpha, but ignore gray
162             sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha;
163             sigBit.alpha = 8;
164             pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
165             fPngBytesPerPixel = 2;
166             break;
167         case kRGBA_1010102_SkColorType:
168             bitDepth     = 16;
169             sigBit.red   = 10;
170             sigBit.green = 10;
171             sigBit.blue  = 10;
172             sigBit.alpha = 2;
173             pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
174             fPngBytesPerPixel = 8;
175             break;
176         case kRGB_101010x_SkColorType:
177             bitDepth     = 16;
178             sigBit.red   = 10;
179             sigBit.green = 10;
180             sigBit.blue  = 10;
181             pngColorType = PNG_COLOR_TYPE_RGB;
182             fPngBytesPerPixel = 6;
183             break;
184         default:
185             return false;
186     }
187 
188     png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(),
189                  bitDepth, pngColorType,
190                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
191                  PNG_FILTER_TYPE_BASE);
192     png_set_sBIT(fPngPtr, fInfoPtr, &sigBit);
193 
194     int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll;
195     SkASSERT(filters == (int)options.fFilterFlags);
196     png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters);
197 
198     int zlibLevel = SkTMin(SkTMax(0, options.fZLibLevel), 9);
199     SkASSERT(zlibLevel == options.fZLibLevel);
200     png_set_compression_level(fPngPtr, zlibLevel);
201 
202     // Set comments in tEXt chunk
203     const sk_sp<SkDataTable>& comments = options.fComments;
204     if (comments != nullptr) {
205         std::vector<png_text> png_texts(comments->count());
206         std::vector<SkString> clippedKeys;
207         for (int i = 0; i < comments->count() / 2; ++i) {
208             const char* keyword;
209             const char* originalKeyword = comments->atStr(2 * i);
210             const char* text = comments->atStr(2 * i + 1);
211             if (strlen(originalKeyword) <= PNG_KEYWORD_MAX_LENGTH) {
212                 keyword = originalKeyword;
213             } else {
214                 SkDEBUGFAILF("PNG tEXt keyword should be no longer than %d.",
215                         PNG_KEYWORD_MAX_LENGTH);
216                 clippedKeys.emplace_back(originalKeyword, PNG_KEYWORD_MAX_LENGTH);
217                 keyword = clippedKeys.back().c_str();
218             }
219             // It seems safe to convert png_const_charp to png_charp for key/text,
220             // and we don't have to provide text_length and other fields as we're providing
221             // 0-terminated c_str with PNG_TEXT_COMPRESSION_NONE (no compression, no itxt).
222             png_texts[i].compression = PNG_TEXT_COMPRESSION_NONE;
223             png_texts[i].key = (png_charp)keyword;
224             png_texts[i].text = (png_charp)text;
225         }
226         png_set_text(fPngPtr, fInfoPtr, png_texts.data(), png_texts.size());
227     }
228 
229     return true;
230 }
231 
choose_proc(const SkImageInfo & info,SkTransferFunctionBehavior unpremulBehavior)232 static transform_scanline_proc choose_proc(const SkImageInfo& info,
233                                            SkTransferFunctionBehavior unpremulBehavior) {
234     const bool isSRGBTransferFn =
235             (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
236     switch (info.colorType()) {
237         case kRGBA_8888_SkColorType:
238             switch (info.alphaType()) {
239                 case kOpaque_SkAlphaType:
240                     return transform_scanline_RGBX;
241                 case kUnpremul_SkAlphaType:
242                     return transform_scanline_memcpy;
243                 case kPremul_SkAlphaType:
244                     return isSRGBTransferFn ? transform_scanline_srgbA :
245                                               transform_scanline_rgbA;
246                 default:
247                     SkASSERT(false);
248                     return nullptr;
249             }
250         case kBGRA_8888_SkColorType:
251             switch (info.alphaType()) {
252                 case kOpaque_SkAlphaType:
253                     return transform_scanline_BGRX;
254                 case kUnpremul_SkAlphaType:
255                     return transform_scanline_BGRA;
256                 case kPremul_SkAlphaType:
257                     return isSRGBTransferFn ? transform_scanline_sbgrA :
258                                               transform_scanline_bgrA;
259                 default:
260                     SkASSERT(false);
261                     return nullptr;
262             }
263         case kRGB_565_SkColorType:
264             return transform_scanline_565;
265         case kRGB_888x_SkColorType:
266             return transform_scanline_888x;
267         case kARGB_4444_SkColorType:
268             switch (info.alphaType()) {
269                 case kOpaque_SkAlphaType:
270                     return transform_scanline_444;
271                 case kPremul_SkAlphaType:
272                     // 4444 is assumed to be legacy premul.
273                     return transform_scanline_4444;
274                 default:
275                     SkASSERT(false);
276                     return nullptr;
277             }
278         case kGray_8_SkColorType:
279             return transform_scanline_memcpy;
280         case kRGBA_F16_SkColorType:
281             switch (info.alphaType()) {
282                 case kOpaque_SkAlphaType:
283                 case kUnpremul_SkAlphaType:
284                     return transform_scanline_F16;
285                 case kPremul_SkAlphaType:
286                     return transform_scanline_F16_premul;
287                 default:
288                     SkASSERT(false);
289                     return nullptr;
290             }
291         case kRGBA_1010102_SkColorType:
292             switch (info.alphaType()) {
293                 case kOpaque_SkAlphaType:
294                 case kUnpremul_SkAlphaType:
295                     return transform_scanline_1010102;
296                 case kPremul_SkAlphaType:
297                     return transform_scanline_1010102_premul;
298                 default:
299                     SkASSERT(false);
300                     return nullptr;
301             }
302         case kRGB_101010x_SkColorType:
303             return transform_scanline_101010x;
304         case kAlpha_8_SkColorType:
305             return transform_scanline_A8_to_GrayAlpha;
306         default:
307             SkASSERT(false);
308             return nullptr;
309     }
310 }
311 
set_icc(png_structp png_ptr,png_infop info_ptr,const SkImageInfo & info)312 static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) {
313     sk_sp<SkData> icc = icc_from_color_space(info);
314     if (!icc) {
315         return;
316     }
317 
318 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
319     const char* name = "Skia";
320     png_const_bytep iccPtr = icc->bytes();
321 #else
322     SkString str("Skia");
323     char* name = str.writable_str();
324     png_charp iccPtr = (png_charp) icc->writable_data();
325 #endif
326     png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
327 }
328 
setColorSpace(const SkImageInfo & info)329 bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) {
330     if (setjmp(png_jmpbuf(fPngPtr))) {
331         return false;
332     }
333 
334     if (info.colorSpace() && info.colorSpace()->isSRGB()) {
335         png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
336     } else {
337         set_icc(fPngPtr, fInfoPtr, info);
338     }
339 
340     return true;
341 }
342 
writeInfo(const SkImageInfo & srcInfo)343 bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) {
344     if (setjmp(png_jmpbuf(fPngPtr))) {
345         return false;
346     }
347 
348     png_write_info(fPngPtr, fInfoPtr);
349     if (kRGBA_F16_SkColorType == srcInfo.colorType() &&
350         kOpaque_SkAlphaType == srcInfo.alphaType())
351     {
352         // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
353         // to skip the alpha channel.
354         png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER);
355     }
356 
357     return true;
358 }
359 
chooseProc(const SkImageInfo & srcInfo,SkTransferFunctionBehavior unpremulBehavior)360 void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo,
361                                  SkTransferFunctionBehavior unpremulBehavior) {
362     fProc = choose_proc(srcInfo, unpremulBehavior);
363 }
364 
Make(SkWStream * dst,const SkPixmap & src,const Options & options)365 std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src,
366                                               const Options& options) {
367     if (!SkPixmapIsValid(src, options.fUnpremulBehavior)) {
368         return nullptr;
369     }
370 
371     std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst);
372     if (!encoderMgr) {
373         return nullptr;
374     }
375 
376     if (!encoderMgr->setHeader(src.info(), options)) {
377         return nullptr;
378     }
379 
380     if (!encoderMgr->setColorSpace(src.info())) {
381         return nullptr;
382     }
383 
384     if (!encoderMgr->writeInfo(src.info())) {
385         return nullptr;
386     }
387 
388     encoderMgr->chooseProc(src.info(), options.fUnpremulBehavior);
389 
390     return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src));
391 }
392 
SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr,const SkPixmap & src)393 SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
394     : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
395     , fEncoderMgr(std::move(encoderMgr))
396 {}
397 
~SkPngEncoder()398 SkPngEncoder::~SkPngEncoder() {}
399 
onEncodeRows(int numRows)400 bool SkPngEncoder::onEncodeRows(int numRows) {
401     if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) {
402         return false;
403     }
404 
405     const void* srcRow = fSrc.addr(0, fCurrRow);
406     for (int y = 0; y < numRows; y++) {
407         fEncoderMgr->proc()((char*) fStorage.get(), (const char*) srcRow, fSrc.width(),
408                             SkColorTypeBytesPerPixel(fSrc.colorType()), nullptr);
409 
410         png_bytep rowPtr = (png_bytep) fStorage.get();
411         png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1);
412         srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
413     }
414 
415     fCurrRow += numRows;
416     if (fCurrRow == fSrc.height()) {
417         png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr());
418     }
419 
420     return true;
421 }
422 
Encode(SkWStream * dst,const SkPixmap & src,const Options & options)423 bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
424     auto encoder = SkPngEncoder::Make(dst, src, options);
425     return encoder.get() && encoder->encodeRows(src.height());
426 }
427 
428 #endif
429