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