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        // 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
348static 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
365bool 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
379bool 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
396void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) {
397    fProc = choose_proc(srcInfo);
398}
399
400std::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
428SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src)
429    : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width())
430    , fEncoderMgr(std::move(encoderMgr))
431{}
432
433SkPngEncoder::~SkPngEncoder() {}
434
435bool 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
462bool 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