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