• 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         case kRGBA_8888_SkColorType:
241             switch (info.alphaType()) {
242                 case kOpaque_SkAlphaType:
243                     return transform_scanline_RGBX;
244                 case kUnpremul_SkAlphaType:
245                     return transform_scanline_memcpy;
246                 case kPremul_SkAlphaType:
247                     return transform_scanline_rgbA;
248                 default:
249                     SkASSERT(false);
250                     return nullptr;
251             }
252         case kBGRA_8888_SkColorType:
253             switch (info.alphaType()) {
254                 case kOpaque_SkAlphaType:
255                     return transform_scanline_BGRX;
256                 case kUnpremul_SkAlphaType:
257                     return transform_scanline_BGRA;
258                 case kPremul_SkAlphaType:
259                     return transform_scanline_bgrA;
260                 default:
261                     SkASSERT(false);
262                     return nullptr;
263             }
264         case kRGB_565_SkColorType:
265             return transform_scanline_565;
266         case kRGB_888x_SkColorType:
267             return transform_scanline_RGBX;
268         case kARGB_4444_SkColorType:
269             switch (info.alphaType()) {
270                 case kOpaque_SkAlphaType:
271                     return transform_scanline_444;
272                 case kPremul_SkAlphaType:
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 
281         case kRGBA_F16Norm_SkColorType:
282         case kRGBA_F16_SkColorType:
283             switch (info.alphaType()) {
284                 case kOpaque_SkAlphaType:
285                 case kUnpremul_SkAlphaType:
286                     return transform_scanline_F16;
287                 case kPremul_SkAlphaType:
288                     return transform_scanline_F16_premul;
289                 default:
290                     SkASSERT(false);
291                     return nullptr;
292             }
293         case kRGBA_F32_SkColorType:
294             switch (info.alphaType()) {
295                 case kOpaque_SkAlphaType:
296                 case kUnpremul_SkAlphaType:
297                     return transform_scanline_F32;
298                 case kPremul_SkAlphaType:
299                     return transform_scanline_F32_premul;
300                 default:
301                     SkASSERT(false);
302                     return nullptr;
303             }
304         case kRGBA_1010102_SkColorType:
305             switch (info.alphaType()) {
306                 case kOpaque_SkAlphaType:
307                 case kUnpremul_SkAlphaType:
308                     return transform_scanline_1010102;
309                 case kPremul_SkAlphaType:
310                     return transform_scanline_1010102_premul;
311                 default:
312                     SkASSERT(false);
313                     return nullptr;
314             }
315         case kBGRA_1010102_SkColorType:
316             switch (info.alphaType()) {
317                 case kOpaque_SkAlphaType:
318                 case kUnpremul_SkAlphaType:
319                     return transform_scanline_bgra_1010102;
320                 case kPremul_SkAlphaType:
321                     return transform_scanline_bgra_1010102_premul;
322                 default:
323                     SkASSERT(false);
324                     return nullptr;
325             }
326         case kRGB_101010x_SkColorType: return transform_scanline_101010x;
327         case kBGR_101010x_SkColorType: return transform_scanline_bgr_101010x;
328 
329         case kAlpha_8_SkColorType:
330             return transform_scanline_A8_to_GrayAlpha;
331         case kR8G8_unorm_SkColorType:
332         case kR16G16_unorm_SkColorType:
333         case kR16G16_float_SkColorType:
334         case kA16_unorm_SkColorType:
335         case kA16_float_SkColorType:
336         case kR16G16B16A16_unorm_SkColorType:
337             return nullptr;
338     }
339     SkASSERT(false);
340     return nullptr;
341 }
342 
set_icc(png_structp png_ptr,png_infop info_ptr,const SkImageInfo & info)343 static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) {
344     sk_sp<SkData> icc = icc_from_color_space(info);
345     if (!icc) {
346         return;
347     }
348 
349 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
350     const char* name = "Skia";
351     png_const_bytep iccPtr = icc->bytes();
352 #else
353     SkString str("Skia");
354     char* name = str.writable_str();
355     png_charp iccPtr = (png_charp) icc->writable_data();
356 #endif
357     png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
358 }
359 
setColorSpace(const SkImageInfo & info)360 bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) {
361     if (setjmp(png_jmpbuf(fPngPtr))) {
362         return false;
363     }
364 
365     if (info.colorSpace() && info.colorSpace()->isSRGB()) {
366         png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
367     } else {
368         set_icc(fPngPtr, fInfoPtr, info);
369     }
370 
371     return true;
372 }
373 
writeInfo(const SkImageInfo & srcInfo)374 bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) {
375     if (setjmp(png_jmpbuf(fPngPtr))) {
376         return false;
377     }
378 
379     png_write_info(fPngPtr, fInfoPtr);
380     if (kRGBA_F16_SkColorType == srcInfo.colorType() &&
381         kOpaque_SkAlphaType == srcInfo.alphaType())
382     {
383         // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
384         // to skip the alpha channel.
385         png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER);
386     }
387 
388     return true;
389 }
390 
chooseProc(const SkImageInfo & srcInfo)391 void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) {
392     fProc = choose_proc(srcInfo);
393 }
394 
Make(SkWStream * dst,const SkPixmap & src,const Options & options)395 std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src,
396                                               const Options& options) {
397     if (!SkPixmapIsValid(src)) {
398         return nullptr;
399     }
400 
401     std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst);
402     if (!encoderMgr) {
403         return nullptr;
404     }
405 
406     if (!encoderMgr->setHeader(src.info(), options)) {
407         return nullptr;
408     }
409 
410     if (!encoderMgr->setColorSpace(src.info())) {
411         return nullptr;
412     }
413 
414     if (!encoderMgr->writeInfo(src.info())) {
415         return nullptr;
416     }
417 
418     encoderMgr->chooseProc(src.info());
419 
420     return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src));
421 }
422 
SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr,const SkPixmap & src)423 SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
424     : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
425     , fEncoderMgr(std::move(encoderMgr))
426 {}
427 
~SkPngEncoder()428 SkPngEncoder::~SkPngEncoder() {}
429 
onEncodeRows(int numRows)430 bool SkPngEncoder::onEncodeRows(int numRows) {
431     if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) {
432         return false;
433     }
434 
435     const void* srcRow = fSrc.addr(0, fCurrRow);
436     for (int y = 0; y < numRows; y++) {
437         sk_msan_assert_initialized(srcRow,
438                                    (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel()));
439         fEncoderMgr->proc()((char*)fStorage.get(),
440                             (const char*)srcRow,
441                             fSrc.width(),
442                             SkColorTypeBytesPerPixel(fSrc.colorType()));
443 
444         png_bytep rowPtr = (png_bytep) fStorage.get();
445         png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1);
446         srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
447     }
448 
449     fCurrRow += numRows;
450     if (fCurrRow == fSrc.height()) {
451         png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr());
452     }
453 
454     return true;
455 }
456 
Encode(SkWStream * dst,const SkPixmap & src,const Options & options)457 bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
458     auto encoder = SkPngEncoder::Make(dst, src, options);
459     return encoder.get() && encoder->encodeRows(src.height());
460 }
461 
462 #endif
463