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 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 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 61 png_structp pngPtr() { return fPngPtr; } 62 png_infop infoPtr() { return fInfoPtr; } 63 int pngBytesPerPixel() const { return fPngBytesPerPixel; } 64 transform_scanline_proc proc() const { return fProc; } 65 66 ~SkPngEncoderMgr() { 67 png_destroy_write_struct(&fPngPtr, &fInfoPtr); 68 } 69 70 private: 71 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 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 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_F16_SkColorType: 110 case kRGBA_F32_SkColorType: 111 sigBit.red = 16; 112 sigBit.green = 16; 113 sigBit.blue = 16; 114 sigBit.alpha = 16; 115 bitDepth = 16; 116 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 117 fPngBytesPerPixel = 8; 118 break; 119 case kGray_8_SkColorType: 120 sigBit.gray = 8; 121 pngColorType = PNG_COLOR_TYPE_GRAY; 122 fPngBytesPerPixel = 1; 123 SkASSERT(srcInfo.isOpaque()); 124 break; 125 case kRGBA_8888_SkColorType: 126 case kBGRA_8888_SkColorType: 127 sigBit.red = 8; 128 sigBit.green = 8; 129 sigBit.blue = 8; 130 sigBit.alpha = 8; 131 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 132 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; 133 break; 134 case kRGB_888x_SkColorType: 135 sigBit.red = 8; 136 sigBit.green = 8; 137 sigBit.blue = 8; 138 pngColorType = PNG_COLOR_TYPE_RGB; 139 fPngBytesPerPixel = 3; 140 SkASSERT(srcInfo.isOpaque()); 141 break; 142 case kARGB_4444_SkColorType: 143 if (kUnpremul_SkAlphaType == srcInfo.alphaType()) { 144 return false; 145 } 146 147 sigBit.red = 4; 148 sigBit.green = 4; 149 sigBit.blue = 4; 150 sigBit.alpha = 4; 151 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 152 fPngBytesPerPixel = srcInfo.isOpaque() ? 3 : 4; 153 break; 154 case kRGB_565_SkColorType: 155 sigBit.red = 5; 156 sigBit.green = 6; 157 sigBit.blue = 5; 158 pngColorType = PNG_COLOR_TYPE_RGB; 159 fPngBytesPerPixel = 3; 160 SkASSERT(srcInfo.isOpaque()); 161 break; 162 case kAlpha_8_SkColorType: // store as gray+alpha, but ignore gray 163 sigBit.gray = kGraySigBit_GrayAlphaIsJustAlpha; 164 sigBit.alpha = 8; 165 pngColorType = PNG_COLOR_TYPE_GRAY_ALPHA; 166 fPngBytesPerPixel = 2; 167 break; 168 case kRGBA_1010102_SkColorType: 169 bitDepth = 16; 170 sigBit.red = 10; 171 sigBit.green = 10; 172 sigBit.blue = 10; 173 sigBit.alpha = 2; 174 pngColorType = srcInfo.isOpaque() ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 175 fPngBytesPerPixel = 8; 176 break; 177 case kRGB_101010x_SkColorType: 178 bitDepth = 16; 179 sigBit.red = 10; 180 sigBit.green = 10; 181 sigBit.blue = 10; 182 pngColorType = PNG_COLOR_TYPE_RGB; 183 fPngBytesPerPixel = 6; 184 break; 185 default: 186 return false; 187 } 188 189 png_set_IHDR(fPngPtr, fInfoPtr, srcInfo.width(), srcInfo.height(), 190 bitDepth, pngColorType, 191 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 192 PNG_FILTER_TYPE_BASE); 193 png_set_sBIT(fPngPtr, fInfoPtr, &sigBit); 194 195 int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll; 196 SkASSERT(filters == (int)options.fFilterFlags); 197 png_set_filter(fPngPtr, PNG_FILTER_TYPE_BASE, filters); 198 199 int zlibLevel = SkTMin(SkTMax(0, options.fZLibLevel), 9); 200 SkASSERT(zlibLevel == options.fZLibLevel); 201 png_set_compression_level(fPngPtr, zlibLevel); 202 203 // Set comments in tEXt chunk 204 const sk_sp<SkDataTable>& comments = options.fComments; 205 if (comments != nullptr) { 206 std::vector<png_text> png_texts(comments->count()); 207 std::vector<SkString> clippedKeys; 208 for (int i = 0; i < comments->count() / 2; ++i) { 209 const char* keyword; 210 const char* originalKeyword = comments->atStr(2 * i); 211 const char* text = comments->atStr(2 * i + 1); 212 if (strlen(originalKeyword) <= PNG_KEYWORD_MAX_LENGTH) { 213 keyword = originalKeyword; 214 } else { 215 SkDEBUGFAILF("PNG tEXt keyword should be no longer than %d.", 216 PNG_KEYWORD_MAX_LENGTH); 217 clippedKeys.emplace_back(originalKeyword, PNG_KEYWORD_MAX_LENGTH); 218 keyword = clippedKeys.back().c_str(); 219 } 220 // It seems safe to convert png_const_charp to png_charp for key/text, 221 // and we don't have to provide text_length and other fields as we're providing 222 // 0-terminated c_str with PNG_TEXT_COMPRESSION_NONE (no compression, no itxt). 223 png_texts[i].compression = PNG_TEXT_COMPRESSION_NONE; 224 png_texts[i].key = (png_charp)keyword; 225 png_texts[i].text = (png_charp)text; 226 } 227 png_set_text(fPngPtr, fInfoPtr, png_texts.data(), png_texts.size()); 228 } 229 230 return true; 231 } 232 233 static transform_scanline_proc choose_proc(const SkImageInfo& info) { 234 switch (info.colorType()) { 235 case kRGBA_8888_SkColorType: 236 switch (info.alphaType()) { 237 case kOpaque_SkAlphaType: 238 return transform_scanline_RGBX; 239 case kUnpremul_SkAlphaType: 240 return transform_scanline_memcpy; 241 case kPremul_SkAlphaType: 242 return transform_scanline_rgbA; 243 default: 244 SkASSERT(false); 245 return nullptr; 246 } 247 case kBGRA_8888_SkColorType: 248 switch (info.alphaType()) { 249 case kOpaque_SkAlphaType: 250 return transform_scanline_BGRX; 251 case kUnpremul_SkAlphaType: 252 return transform_scanline_BGRA; 253 case kPremul_SkAlphaType: 254 return transform_scanline_bgrA; 255 default: 256 SkASSERT(false); 257 return nullptr; 258 } 259 case kRGB_565_SkColorType: 260 return transform_scanline_565; 261 case kRGB_888x_SkColorType: 262 return transform_scanline_RGBX; 263 case kARGB_4444_SkColorType: 264 switch (info.alphaType()) { 265 case kOpaque_SkAlphaType: 266 return transform_scanline_444; 267 case kPremul_SkAlphaType: 268 return transform_scanline_4444; 269 default: 270 SkASSERT(false); 271 return nullptr; 272 } 273 case kGray_8_SkColorType: 274 return transform_scanline_memcpy; 275 case kRGBA_F16_SkColorType: 276 switch (info.alphaType()) { 277 case kOpaque_SkAlphaType: 278 case kUnpremul_SkAlphaType: 279 return transform_scanline_F16; 280 case kPremul_SkAlphaType: 281 return transform_scanline_F16_premul; 282 default: 283 SkASSERT(false); 284 return nullptr; 285 } 286 case kRGBA_F32_SkColorType: 287 switch (info.alphaType()) { 288 case kOpaque_SkAlphaType: 289 case kUnpremul_SkAlphaType: 290 return transform_scanline_F32; 291 case kPremul_SkAlphaType: 292 return transform_scanline_F32_premul; 293 default: 294 SkASSERT(false); 295 return nullptr; 296 } 297 case kRGBA_1010102_SkColorType: 298 switch (info.alphaType()) { 299 case kOpaque_SkAlphaType: 300 case kUnpremul_SkAlphaType: 301 return transform_scanline_1010102; 302 case kPremul_SkAlphaType: 303 return transform_scanline_1010102_premul; 304 default: 305 SkASSERT(false); 306 return nullptr; 307 } 308 case kRGB_101010x_SkColorType: 309 return transform_scanline_101010x; 310 case kAlpha_8_SkColorType: 311 return transform_scanline_A8_to_GrayAlpha; 312 default: 313 SkASSERT(false); 314 return nullptr; 315 } 316 } 317 318 static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) { 319 sk_sp<SkData> icc = icc_from_color_space(info); 320 if (!icc) { 321 return; 322 } 323 324 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5) 325 const char* name = "Skia"; 326 png_const_bytep iccPtr = icc->bytes(); 327 #else 328 SkString str("Skia"); 329 char* name = str.writable_str(); 330 png_charp iccPtr = (png_charp) icc->writable_data(); 331 #endif 332 png_set_iCCP(png_ptr, info_ptr, name, 0, iccPtr, icc->size()); 333 } 334 335 bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info) { 336 if (setjmp(png_jmpbuf(fPngPtr))) { 337 return false; 338 } 339 340 if (info.colorSpace() && info.colorSpace()->isSRGB()) { 341 png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL); 342 } else { 343 set_icc(fPngPtr, fInfoPtr, info); 344 } 345 346 return true; 347 } 348 349 bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) { 350 if (setjmp(png_jmpbuf(fPngPtr))) { 351 return false; 352 } 353 354 png_write_info(fPngPtr, fInfoPtr); 355 if (kRGBA_F16_SkColorType == srcInfo.colorType() && 356 kOpaque_SkAlphaType == srcInfo.alphaType()) 357 { 358 // For kOpaque, kRGBA_F16, we will keep the row as RGBA and tell libpng 359 // to skip the alpha channel. 360 png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER); 361 } 362 363 return true; 364 } 365 366 void SkPngEncoderMgr::chooseProc(const SkImageInfo& srcInfo) { 367 fProc = choose_proc(srcInfo); 368 } 369 370 std::unique_ptr<SkEncoder> SkPngEncoder::Make(SkWStream* dst, const SkPixmap& src, 371 const Options& options) { 372 if (!SkPixmapIsValid(src)) { 373 return nullptr; 374 } 375 376 std::unique_ptr<SkPngEncoderMgr> encoderMgr = SkPngEncoderMgr::Make(dst); 377 if (!encoderMgr) { 378 return nullptr; 379 } 380 381 if (!encoderMgr->setHeader(src.info(), options)) { 382 return nullptr; 383 } 384 385 if (!encoderMgr->setColorSpace(src.info())) { 386 return nullptr; 387 } 388 389 if (!encoderMgr->writeInfo(src.info())) { 390 return nullptr; 391 } 392 393 encoderMgr->chooseProc(src.info()); 394 395 return std::unique_ptr<SkPngEncoder>(new SkPngEncoder(std::move(encoderMgr), src)); 396 } 397 398 SkPngEncoder::SkPngEncoder(std::unique_ptr<SkPngEncoderMgr> encoderMgr, const SkPixmap& src) 399 : INHERITED(src, encoderMgr->pngBytesPerPixel() * src.width()) 400 , fEncoderMgr(std::move(encoderMgr)) 401 {} 402 403 SkPngEncoder::~SkPngEncoder() {} 404 405 bool SkPngEncoder::onEncodeRows(int numRows) { 406 if (setjmp(png_jmpbuf(fEncoderMgr->pngPtr()))) { 407 return false; 408 } 409 410 const void* srcRow = fSrc.addr(0, fCurrRow); 411 for (int y = 0; y < numRows; y++) { 412 fEncoderMgr->proc()((char*)fStorage.get(), 413 (const char*)srcRow, 414 fSrc.width(), 415 SkColorTypeBytesPerPixel(fSrc.colorType())); 416 417 png_bytep rowPtr = (png_bytep) fStorage.get(); 418 png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1); 419 srcRow = SkTAddOffset<const void>(srcRow, fSrc.rowBytes()); 420 } 421 422 fCurrRow += numRows; 423 if (fCurrRow == fSrc.height()) { 424 png_write_end(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr()); 425 } 426 427 return true; 428 } 429 430 bool SkPngEncoder::Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { 431 auto encoder = SkPngEncoder::Make(dst, src, options); 432 return encoder.get() && encoder->encodeRows(src.height()); 433 } 434 435 #endif 436