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