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