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