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