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