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