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 "SkImageEncoderPriv.h"
9
10 #ifdef SK_HAS_PNG_LIBRARY
11
12 #include "SkColorTable.h"
13 #include "SkImageEncoderFns.h"
14 #include "SkImageInfoPriv.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkPngEncoder.h"
18 #include "SkPngPriv.h"
19
20 #include "png.h"
21
22 static_assert(PNG_FILTER_NONE == (int)SkPngEncoder::FilterFlag::kNone, "Skia libpng filter err.");
23 static_assert(PNG_FILTER_SUB == (int)SkPngEncoder::FilterFlag::kSub, "Skia libpng filter err.");
24 static_assert(PNG_FILTER_UP == (int)SkPngEncoder::FilterFlag::kUp, "Skia libpng filter err.");
25 static_assert(PNG_FILTER_AVG == (int)SkPngEncoder::FilterFlag::kAvg, "Skia libpng filter err.");
26 static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err.");
27 static_assert(PNG_ALL_FILTERS == (int)SkPngEncoder::FilterFlag::kAll, "Skia libpng filter err.");
28
29 static constexpr bool kSuppressPngEncodeWarnings = true;
30
sk_error_fn(png_structp png_ptr,png_const_charp msg)31 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
32 if (!kSuppressPngEncodeWarnings) {
33 SkDebugf("libpng encode error: %s\n", msg);
34 }
35
36 longjmp(png_jmpbuf(png_ptr), 1);
37 }
38
sk_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)39 static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
40 SkWStream* stream = (SkWStream*)png_get_io_ptr(png_ptr);
41 if (!stream->write(data, len)) {
42 png_error(png_ptr, "sk_write_fn cannot write to stream");
43 }
44 }
45
46 class SkPngEncoderMgr final : SkNoncopyable {
47 public:
48
49 /*
50 * Create the decode manager
51 * Does not take ownership of stream
52 */
53 static std::unique_ptr<SkPngEncoderMgr> Make(SkWStream* stream);
54
55 bool setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options);
56 bool setColorSpace(const SkImageInfo& info);
57 bool writeInfo(const SkImageInfo& srcInfo);
58 void chooseProc(const SkImageInfo& srcInfo, SkTransferFunctionBehavior unpremulBehavior);
59
pngPtr()60 png_structp pngPtr() { return fPngPtr; }
infoPtr()61 png_infop infoPtr() { return fInfoPtr; }
pngBytesPerPixel() const62 int pngBytesPerPixel() const { return fPngBytesPerPixel; }
proc() const63 transform_scanline_proc proc() const { return fProc; }
64
~SkPngEncoderMgr()65 ~SkPngEncoderMgr() {
66 png_destroy_write_struct(&fPngPtr, &fInfoPtr);
67 }
68
69 private:
70
SkPngEncoderMgr(png_structp pngPtr,png_infop infoPtr)71 SkPngEncoderMgr(png_structp pngPtr, png_infop infoPtr)
72 : fPngPtr(pngPtr)
73 , fInfoPtr(infoPtr)
74 {}
75
76 png_structp fPngPtr;
77 png_infop fInfoPtr;
78 int fPngBytesPerPixel;
79 transform_scanline_proc fProc;
80 };
81
Make(SkWStream * stream)82 std::unique_ptr<SkPngEncoderMgr> SkPngEncoderMgr::Make(SkWStream* stream) {
83 png_structp pngPtr =
84 png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, sk_error_fn, nullptr);
85 if (!pngPtr) {
86 return nullptr;
87 }
88
89 png_infop infoPtr = png_create_info_struct(pngPtr);
90 if (!infoPtr) {
91 png_destroy_write_struct(&pngPtr, nullptr);
92 return nullptr;
93 }
94
95 png_set_write_fn(pngPtr, (void*)stream, sk_write_fn, nullptr);
96 return std::unique_ptr<SkPngEncoderMgr>(new SkPngEncoderMgr(pngPtr, infoPtr));
97 }
98
setHeader(const SkImageInfo & srcInfo,const SkPngEncoder::Options & options)99 bool SkPngEncoderMgr::setHeader(const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) {
100 if (setjmp(png_jmpbuf(fPngPtr))) {
101 return false;
102 }
103
104 int pngColorType;
105 png_color_8 sigBit;
106 int bitDepth = 8;
107 switch (srcInfo.colorType()) {
108 case kRGBA_F16_SkColorType:
109 SkASSERT(srcInfo.colorSpace() && srcInfo.colorSpace()->gammaIsLinear());
110 sigBit.red = 16;
111 sigBit.green = 16;
112 sigBit.blue = 16;
113 sigBit.alpha = 16;
114 bitDepth = 16;
115 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
116 fPngBytesPerPixel = 8;
117 break;
118 case kGray_8_SkColorType:
119 sigBit.gray = 8;
120 pngColorType = PNG_COLOR_TYPE_GRAY;
121 fPngBytesPerPixel = 1;
122 SkASSERT(srcInfo.isOpaque());
123 break;
124 case kRGBA_8888_SkColorType:
125 case kBGRA_8888_SkColorType:
126 sigBit.red = 8;
127 sigBit.green = 8;
128 sigBit.blue = 8;
129 sigBit.alpha = 8;
130 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
131 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
132 break;
133 case kRGB_888x_SkColorType:
134 sigBit.red = 8;
135 sigBit.green = 8;
136 sigBit.blue = 8;
137 pngColorType = PNG_COLOR_TYPE_RGB;
138 fPngBytesPerPixel = 3;
139 SkASSERT(srcInfo.isOpaque());
140 break;
141 case kARGB_4444_SkColorType:
142 if (kUnpremul_SkAlphaType == srcInfo.alphaType()) {
143 return false;
144 }
145
146 sigBit.red = 4;
147 sigBit.green = 4;
148 sigBit.blue = 4;
149 sigBit.alpha = 4;
150 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
151 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4;
152 break;
153 case kRGB_565_SkColorType:
154 sigBit.red = 5;
155 sigBit.green = 6;
156 sigBit.blue = 5;
157 pngColorType = PNG_COLOR_TYPE_RGB;
158 fPngBytesPerPixel = 3;
159 SkASSERT(srcInfo.isOpaque());
160 break;
161 case kAlpha_8_SkColorType: // store as gray+alpha, but ignore gray
162 sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha;
163 sigBit.alpha = 8;
164 pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
165 fPngBytesPerPixel = 2;
166 break;
167 case kRGBA_1010102_SkColorType:
168 bitDepth = 16;
169 sigBit.red = 10;
170 sigBit.green = 10;
171 sigBit.blue = 10;
172 sigBit.alpha = 2;
173 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
174 fPngBytesPerPixel = 8;
175 break;
176 case kRGB_101010x_SkColorType:
177 bitDepth = 16;
178 sigBit.red = 10;
179 sigBit.green = 10;
180 sigBit.blue = 10;
181 pngColorType = PNG_COLOR_TYPE_RGB;
182 fPngBytesPerPixel = 6;
183 break;
184 default:
185 return false;
186 }
187
188 png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(),
189 bitDepth, pngColorType,
190 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
191 PNG_FILTER_TYPE_BASE);
192 png_set_sBIT(fPngPtr, fInfoPtr, &sigBit);
193
194 int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll;
195 SkASSERT(filters == (int)options.fFilterFlags);
196 png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters);
197
198 int zlibLevel = SkTMin(SkTMax(0, options.fZLibLevel), 9);
199 SkASSERT(zlibLevel == options.fZLibLevel);
200 png_set_compression_level(fPngPtr, zlibLevel);
201
202 // Set comments in tEXt chunk
203 const sk_sp<SkDataTable>& comments = options.fComments;
204 if (comments != nullptr) {
205 std::vector<png_text> png_texts(comments->count());
206 std::vector<SkString> clippedKeys;
207 for (int i = 0; i < comments->count() / 2; ++i) {
208 const char* keyword;
209 const char* originalKeyword = comments->atStr(2 * i);
210 const char* text = comments->atStr(2 * i + 1);
211 if (strlen(originalKeyword) <= PNG_KEYWORD_MAX_LENGTH) {
212 keyword = originalKeyword;
213 } else {
214 SkDEBUGFAILF("PNG tEXt keyword should be no longer than %d.",
215 PNG_KEYWORD_MAX_LENGTH);
216 clippedKeys.emplace_back(originalKeyword, PNG_KEYWORD_MAX_LENGTH);
217 keyword = clippedKeys.back().c_str();
218 }
219 // It seems safe to convert png_const_charp to png_charp for key/text,
220 // and we don't have to provide text_length and other fields as we're providing
221 // 0-terminated c_str with PNG_TEXT_COMPRESSION_NONE (no compression, no itxt).
222 png_texts[i].compression = PNG_TEXT_COMPRESSION_NONE;
223 png_texts[i].key = (png_charp)keyword;
224 png_texts[i].text = (png_charp)text;
225 }
226 png_set_text(fPngPtr, fInfoPtr, png_texts.data(), png_texts.size());
227 }
228
229 return true;
230 }
231
choose_proc(const SkImageInfo & info,SkTransferFunctionBehavior unpremulBehavior)232 static transform_scanline_proc choose_proc(const SkImageInfo& info,
233 SkTransferFunctionBehavior unpremulBehavior) {
234 const bool isSRGBTransferFn =
235 (SkTransferFunctionBehavior::kRespect == unpremulBehavior) && info.gammaCloseToSRGB();
236 switch (info.colorType()) {
237 case kRGBA_8888_SkColorType:
238 switch (info.alphaType()) {
239 case kOpaque_SkAlphaType:
240 return transform_scanline_RGBX;
241 case kUnpremul_SkAlphaType:
242 return transform_scanline_memcpy;
243 case kPremul_SkAlphaType:
244 return isSRGBTransferFn ? transform_scanline_srgbA :
245 transform_scanline_rgbA;
246 default:
247 SkASSERT(false);
248 return nullptr;
249 }
250 case kBGRA_8888_SkColorType:
251 switch (info.alphaType()) {
252 case kOpaque_SkAlphaType:
253 return transform_scanline_BGRX;
254 case kUnpremul_SkAlphaType:
255 return transform_scanline_BGRA;
256 case kPremul_SkAlphaType:
257 return isSRGBTransferFn ? transform_scanline_sbgrA :
258 transform_scanline_bgrA;
259 default:
260 SkASSERT(false);
261 return nullptr;
262 }
263 case kRGB_565_SkColorType:
264 return transform_scanline_565;
265 case kRGB_888x_SkColorType:
266 return transform_scanline_888x;
267 case kARGB_4444_SkColorType:
268 switch (info.alphaType()) {
269 case kOpaque_SkAlphaType:
270 return transform_scanline_444;
271 case kPremul_SkAlphaType:
272 // 4444 is assumed to be legacy premul.
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 case kRGBA_F16_SkColorType:
281 switch (info.alphaType()) {
282 case kOpaque_SkAlphaType:
283 case kUnpremul_SkAlphaType:
284 return transform_scanline_F16;
285 case kPremul_SkAlphaType:
286 return transform_scanline_F16_premul;
287 default:
288 SkASSERT(false);
289 return nullptr;
290 }
291 case kRGBA_1010102_SkColorType:
292 switch (info.alphaType()) {
293 case kOpaque_SkAlphaType:
294 case kUnpremul_SkAlphaType:
295 return transform_scanline_1010102;
296 case kPremul_SkAlphaType:
297 return transform_scanline_1010102_premul;
298 default:
299 SkASSERT(false);
300 return nullptr;
301 }
302 case kRGB_101010x_SkColorType:
303 return transform_scanline_101010x;
304 case kAlpha_8_SkColorType:
305 return transform_scanline_A8_to_GrayAlpha;
306 default:
307 SkASSERT(false);
308 return nullptr;
309 }
310 }
311
set_icc(png_structp png_ptr,png_infop info_ptr,const SkImageInfo & info)312 static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) {
313 sk_sp<SkData> icc = icc_from_color_space(info);
314 if (!icc) {
315 return;
316 }
317
318 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
319 const char* name = "Skia";
320 png_const_bytep iccPtr = icc->bytes();
321 #else
322 SkString str("Skia");
323 char* name = str.writable_str();
324 png_charp iccPtr = (png_charp) icc->writable_data();
325 #endif
326 png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size());
327 }
328
setColorSpace(const SkImageInfo & info)329 bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) {
330 if (setjmp(png_jmpbuf(fPngPtr))) {
331 return false;
332 }
333
334 if (info.colorSpace() && info.colorSpace()->isSRGB()) {
335 png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL);
336 } else {
337 set_icc(fPngPtr, fInfoPtr, info);
338 }
339
340 return true;
341 }
342
writeInfo(const SkImageInfo & srcInfo)343 bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) {
344 if (setjmp(png_jmpbuf(fPngPtr))) {
345 return false;
346 }
347
348 png_write_info(fPngPtr, fInfoPtr);
349 if (kRGBA_F16_SkColorType == srcInfo.colorType() &&
350 kOpaque_SkAlphaType == srcInfo.alphaType())
351 {
352 // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng
353 // to skip the alpha channel.
354 png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER);
355 }
356
357 return true;
358 }
359
chooseProc(const SkImageInfo & srcInfo,SkTransferFunctionBehavior unpremulBehavior)360 void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo,
361 SkTransferFunctionBehavior unpremulBehavior) {
362 fProc = choose_proc(srcInfo, unpremulBehavior);
363 }
364
Make(SkWStream * dst,const SkPixmap & src,const Options & options)365 std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src,
366 const Options& options) {
367 if (!SkPixmapIsValid(src, options.fUnpremulBehavior)) {
368 return nullptr;
369 }
370
371 std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst);
372 if (!encoderMgr) {
373 return nullptr;
374 }
375
376 if (!encoderMgr->setHeader(src.info(), options)) {
377 return nullptr;
378 }
379
380 if (!encoderMgr->setColorSpace(src.info())) {
381 return nullptr;
382 }
383
384 if (!encoderMgr->writeInfo(src.info())) {
385 return nullptr;
386 }
387
388 encoderMgr->chooseProc(src.info(), options.fUnpremulBehavior);
389
390 return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src));
391 }
392
SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr,const SkPixmap & src)393 SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
394 : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
395 , fEncoderMgr(std::move(encoderMgr))
396 {}
397
~SkPngEncoder()398 SkPngEncoder::~SkPngEncoder() {}
399
onEncodeRows(int numRows)400 bool SkPngEncoder::onEncodeRows(int numRows) {
401 if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) {
402 return false;
403 }
404
405 const void* srcRow = fSrc.addr(0, fCurrRow);
406 for (int y = 0; y < numRows; y++) {
407 fEncoderMgr->proc()((char*) fStorage.get(), (const char*) srcRow, fSrc.width(),
408 SkColorTypeBytesPerPixel(fSrc.colorType()), nullptr);
409
410 png_bytep rowPtr = (png_bytep) fStorage.get();
411 png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1);
412 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes());
413 }
414
415 fCurrRow += numRows;
416 if (fCurrRow == fSrc.height()) {
417 png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr());
418 }
419
420 return true;
421 }
422
Encode(SkWStream * dst,const SkPixmap & src,const Options & options)423 bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) {
424 auto encoder = SkPngEncoder::Make(dst, src, options);
425 return encoder.get() && encoder->encodeRows(src.height());
426 }
427
428 #endif
429