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
24static_assert(PNG_FILTER_NONE == (int)SkPngEncoder::FilterFlag::kNone, "Skia libpng filter err.");
25static_assert(PNG_FILTER_SUB == (int)SkPngEncoder::FilterFlag::kSub, "Skia libpng filter err.");
26static_assert(PNG_FILTER_UP == (int)SkPngEncoder::FilterFlag::kUp, "Skia libpng filter err.");
27static_assert(PNG_FILTER_AVG == (int)SkPngEncoder::FilterFlag::kAvg, "Skia libpng filter err.");
28static_assert(PNG_FILTER_PAETH == (int)SkPngEncoder::FilterFlag::kPaeth, "Skia libpng filter err.");
29static_assert(PNG_ALL_FILTERS == (int)SkPngEncoder::FilterFlag::kAll, "Skia libpng filter err.");
30
31static constexpr bool kSuppressPngEncodeWarnings = true;
32
33static 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
41static 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
48class SkPngEncoderMgr final : SkNoncopyable {
49public:
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
62 png_structp pngPtr() { return fPngPtr; }
63 png_infop infoPtr() { return fInfoPtr; }
64 int pngBytesPerPixel() const { return fPngBytesPerPixel; }
65 transform_scanline_proc proc() const { return fProc; }
66
67 ~SkPngEncoderMgr() {
68 png_destroy_write_struct(&fPngPtr, &fInfoPtr);
69 }
70
71private:
72
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
84std::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
101bool 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
235static 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
343static 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
360bool 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
374bool 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
391void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) {
392 fProc = choose_proc(srcInfo);
393}
394
395std::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
423SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
424 : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
425 , fEncoderMgr(std::move(encoderMgr))
426{}
427
428SkPngEncoder::~SkPngEncoder() {}
429
430bool 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
457bool 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