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