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