| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2015 Google Inc. | |
| 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 "SkCodec_libpng.h" | |
| 9 #include "SkCodecPriv.h" | |
| 10 #include "SkColorPriv.h" | |
| 11 #include "SkColorTable.h" | |
| 12 #include "SkBitmap.h" | |
| 13 #include "SkMath.h" | |
| 14 #include "SkSize.h" | |
| 15 #include "SkStream.h" | |
| 16 #include "SkSwizzler.h" | |
| 17 #include "SkTemplates.h" | |
| 18 | |
| 19 /////////////////////////////////////////////////////////////////////////////// | |
| 20 // Helper macros | |
| 21 /////////////////////////////////////////////////////////////////////////////// | |
| 22 | |
| 23 #ifndef png_jmpbuf | |
| 24 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) | |
| 25 #endif | |
| 26 | |
| 27 /* These were dropped in libpng >= 1.4 */ | |
| 28 #ifndef png_infopp_NULL | |
| 29 #define png_infopp_NULL nullptr | |
| 30 #endif | |
| 31 | |
| 32 #ifndef png_bytepp_NULL | |
| 33 #define png_bytepp_NULL nullptr | |
| 34 #endif | |
| 35 | |
| 36 #ifndef int_p_NULL | |
| 37 #define int_p_NULL nullptr | |
| 38 #endif | |
| 39 | |
| 40 #ifndef png_flush_ptr_NULL | |
| 41 #define png_flush_ptr_NULL nullptr | |
| 42 #endif | |
| 43 | |
| 44 /////////////////////////////////////////////////////////////////////////////// | |
| 45 // Callback functions | |
| 46 /////////////////////////////////////////////////////////////////////////////// | |
| 47 | |
| 48 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { | |
| 49 SkCodecPrintf("------ png error %s\n", msg); | |
| 50 longjmp(png_jmpbuf(png_ptr), 1); | |
| 51 } | |
| 52 | |
| 53 void sk_warning_fn(png_structp, png_const_charp msg) { | |
| 54 SkCodecPrintf("----- png warning %s\n", msg); | |
| 55 } | |
| 56 | |
| 57 static void sk_read_fn(png_structp png_ptr, png_bytep data, | |
| 58 png_size_t length) { | |
| 59 SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr)); | |
| 60 const size_t bytes = stream->read(data, length); | |
| 61 if (bytes != length) { | |
| 62 // FIXME: We want to report the fact that the stream was truncated. | |
| 63 // One way to do that might be to pass a enum to longjmp so setjmp can | |
| 64 // specify the failure. | |
| 65 png_error(png_ptr, "Read Error!"); | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | |
| 70 static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { | |
| 71 SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(pn
g_ptr); | |
| 72 // readChunk() returning true means continue decoding | |
| 73 return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->
size) ? 1 : -1; | |
| 74 } | |
| 75 #endif | |
| 76 | |
| 77 /////////////////////////////////////////////////////////////////////////////// | |
| 78 // Helpers | |
| 79 /////////////////////////////////////////////////////////////////////////////// | |
| 80 | |
| 81 class AutoCleanPng : public SkNoncopyable { | |
| 82 public: | |
| 83 AutoCleanPng(png_structp png_ptr) | |
| 84 : fPng_ptr(png_ptr) | |
| 85 , fInfo_ptr(nullptr) {} | |
| 86 | |
| 87 ~AutoCleanPng() { | |
| 88 // fInfo_ptr will never be non-nullptr unless fPng_ptr is. | |
| 89 if (fPng_ptr) { | |
| 90 png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; | |
| 91 png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 void setInfoPtr(png_infop info_ptr) { | |
| 96 SkASSERT(nullptr == fInfo_ptr); | |
| 97 fInfo_ptr = info_ptr; | |
| 98 } | |
| 99 | |
| 100 void detach() { | |
| 101 fPng_ptr = nullptr; | |
| 102 fInfo_ptr = nullptr; | |
| 103 } | |
| 104 | |
| 105 private: | |
| 106 png_structp fPng_ptr; | |
| 107 png_infop fInfo_ptr; | |
| 108 }; | |
| 109 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) | |
| 110 | |
| 111 //checks if there is transparency info in the tRNS chunk | |
| 112 //image types which could have data in the tRNS chunk include: Index8, Gray8, RG
B | |
| 113 static bool has_transparency_in_tRNS(png_structp png_ptr, | |
| 114 png_infop info_ptr) { | |
| 115 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 png_bytep trans; | |
| 120 int num_trans; | |
| 121 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr); | |
| 122 return num_trans > 0; | |
| 123 } | |
| 124 | |
| 125 // Method for coverting to either an SkPMColor or a similarly packed | |
| 126 // unpremultiplied color. | |
| 127 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); | |
| 128 | |
| 129 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | |
| 130 // the case here. | |
| 131 bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { | |
| 132 int numPalette; | |
| 133 png_colorp palette; | |
| 134 png_bytep trans; | |
| 135 | |
| 136 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { | |
| 137 return false; | |
| 138 } | |
| 139 | |
| 140 // Note: These are not necessarily SkPMColors | |
| 141 SkPMColor colorStorage[256]; // worst-case storage | |
| 142 SkPMColor* colorPtr = colorStorage; | |
| 143 | |
| 144 int numTrans; | |
| 145 if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { | |
| 146 png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, nullptr); | |
| 147 } else { | |
| 148 numTrans = 0; | |
| 149 } | |
| 150 | |
| 151 // check for bad images that might make us crash | |
| 152 if (numTrans > numPalette) { | |
| 153 numTrans = numPalette; | |
| 154 } | |
| 155 | |
| 156 int index = 0; | |
| 157 | |
| 158 // Choose which function to use to create the color table. If the final dest
ination's | |
| 159 // colortype is unpremultiplied, the color table will store unpremultiplied
colors. | |
| 160 PackColorProc proc; | |
| 161 if (premultiply) { | |
| 162 proc = &SkPreMultiplyARGB; | |
| 163 } else { | |
| 164 proc = &SkPackARGB32NoCheck; | |
| 165 } | |
| 166 for (; index < numTrans; index++) { | |
| 167 *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue
); | |
| 168 palette++; | |
| 169 } | |
| 170 | |
| 171 for (; index < numPalette; index++) { | |
| 172 *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->
blue); | |
| 173 palette++; | |
| 174 } | |
| 175 | |
| 176 /* BUGGY IMAGE WORKAROUND | |
| 177 Invalid images could contain pixel values that are greater than the numb
er of palette | |
| 178 entries. Since we use pixel values as indices into the palette this coul
d result in reading | |
| 179 beyond the end of the palette which could leak the contents of uninitial
ized memory. To | |
| 180 ensure this doesn't happen, we grow the colortable to the maximum size t
hat can be | |
| 181 addressed by the bitdepth of the image and fill it with the last palette
color or black if | |
| 182 the palette is empty (really broken image). | |
| 183 */ | |
| 184 int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8)); | |
| 185 SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0)
; | |
| 186 for (; index < colorCount; index++) { | |
| 187 *colorPtr++ = lastColor; | |
| 188 } | |
| 189 | |
| 190 // Set the new color count | |
| 191 if (ctableCount != nullptr) { | |
| 192 *ctableCount = colorCount; | |
| 193 } | |
| 194 | |
| 195 fColorTable.reset(new SkColorTable(colorStorage, colorCount)); | |
| 196 return true; | |
| 197 } | |
| 198 | |
| 199 /////////////////////////////////////////////////////////////////////////////// | |
| 200 // Creation | |
| 201 /////////////////////////////////////////////////////////////////////////////// | |
| 202 | |
| 203 bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) { | |
| 204 return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead); | |
| 205 } | |
| 206 | |
| 207 // Reads the header and initializes the output fields, if not NULL. | |
| 208 // | |
| 209 // @param stream Input data. Will be read to get enough information to properly | |
| 210 // setup the codec. | |
| 211 // @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL. | |
| 212 // If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is | |
| 213 // expected to continue to own it for the lifetime of the png_ptr. | |
| 214 // @param png_ptrp Optional output variable. If non-NULL, will be set to a new | |
| 215 // png_structp on success. | |
| 216 // @param info_ptrp Optional output variable. If non-NULL, will be set to a new | |
| 217 // png_infop on success; | |
| 218 // @param imageInfo Optional output variable. If non-NULL, will be set to | |
| 219 // reflect the properties of the encoded image on success. | |
| 220 // @param bitDepthPtr Optional output variable. If non-NULL, will be set to the | |
| 221 // bit depth of the encoded image on success. | |
| 222 // @param numberPassesPtr Optional output variable. If non-NULL, will be set to | |
| 223 // the number_passes of the encoded image on success. | |
| 224 // @return true on success, in which case the caller is responsible for calling | |
| 225 // png_destroy_read_struct(png_ptrp, info_ptrp). | |
| 226 // If it returns false, the passed in fields (except stream) are unchanged. | |
| 227 static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, | |
| 228 png_structp* png_ptrp, png_infop* info_ptrp, | |
| 229 SkImageInfo* imageInfo, int* bitDepthPtr, int* numberPas
sesPtr) { | |
| 230 // The image is known to be a PNG. Decode enough to know the SkImageInfo. | |
| 231 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, | |
| 232 sk_error_fn, sk_warning_fn); | |
| 233 if (!png_ptr) { | |
| 234 return false; | |
| 235 } | |
| 236 | |
| 237 AutoCleanPng autoClean(png_ptr); | |
| 238 | |
| 239 png_infop info_ptr = png_create_info_struct(png_ptr); | |
| 240 if (info_ptr == nullptr) { | |
| 241 return false; | |
| 242 } | |
| 243 | |
| 244 autoClean.setInfoPtr(info_ptr); | |
| 245 | |
| 246 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 247 // error? | |
| 248 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn); | |
| 253 | |
| 254 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED | |
| 255 // Hookup our chunkReader so we can see any user-chunks the caller may be in
terested in. | |
| 256 // This needs to be installed before we read the png header. Android may st
ore ninepatch | |
| 257 // chunks in the header. | |
| 258 if (chunkReader) { | |
| 259 png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*
)"", 0); | |
| 260 png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_use
r_chunk); | |
| 261 } | |
| 262 #endif | |
| 263 | |
| 264 // The call to png_read_info() gives us all of the information from the | |
| 265 // PNG file before the first IDAT (image data chunk). | |
| 266 png_read_info(png_ptr, info_ptr); | |
| 267 png_uint_32 origWidth, origHeight; | |
| 268 int bitDepth, colorType; | |
| 269 png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, | |
| 270 &colorType, int_p_NULL, int_p_NULL, int_p_NULL); | |
| 271 | |
| 272 if (bitDepthPtr) { | |
| 273 *bitDepthPtr = bitDepth; | |
| 274 } | |
| 275 | |
| 276 // sanity check for size | |
| 277 { | |
| 278 int64_t size = sk_64_mul(origWidth, origHeight); | |
| 279 // now check that if we are 4-bytes per pixel, we also don't overflow | |
| 280 if (size < 0 || size > (0x7FFFFFFF >> 2)) { | |
| 281 return false; | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 // Tell libpng to strip 16 bit/color files down to 8 bits/color | |
| 286 if (bitDepth == 16) { | |
| 287 png_set_strip_16(png_ptr); | |
| 288 } | |
| 289 #ifdef PNG_READ_PACK_SUPPORTED | |
| 290 // Extract multiple pixels with bit depths of 1, 2, and 4 from a single | |
| 291 // byte into separate bytes (useful for paletted and grayscale images). | |
| 292 if (bitDepth < 8) { | |
| 293 png_set_packing(png_ptr); | |
| 294 } | |
| 295 #endif | |
| 296 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. | |
| 297 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { | |
| 298 png_set_expand_gray_1_2_4_to_8(png_ptr); | |
| 299 } | |
| 300 | |
| 301 // Now determine the default SkColorType and SkAlphaType and set required tr
ansforms | |
| 302 SkColorType skColorType = kUnknown_SkColorType; | |
| 303 SkAlphaType skAlphaType = kUnknown_SkAlphaType; | |
| 304 switch (colorType) { | |
| 305 case PNG_COLOR_TYPE_PALETTE: | |
| 306 skColorType = kIndex_8_SkColorType; | |
| 307 skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ? | |
| 308 kUnpremul_SkAlphaType : kOpaque_SkAlphaType; | |
| 309 break; | |
| 310 case PNG_COLOR_TYPE_RGB: | |
| 311 if (has_transparency_in_tRNS(png_ptr, info_ptr)) { | |
| 312 //convert to RGBA with tranparency information in tRNS chunk if
it exists | |
| 313 png_set_tRNS_to_alpha(png_ptr); | |
| 314 skAlphaType = kUnpremul_SkAlphaType; | |
| 315 } else { | |
| 316 skAlphaType = kOpaque_SkAlphaType; | |
| 317 } | |
| 318 skColorType = kN32_SkColorType; | |
| 319 break; | |
| 320 case PNG_COLOR_TYPE_GRAY: | |
| 321 if (has_transparency_in_tRNS(png_ptr, info_ptr)) { | |
| 322 //FIXME: support gray with alpha as a color type | |
| 323 //convert to RGBA if there is transparentcy info in the tRNS chu
nk | |
| 324 png_set_tRNS_to_alpha(png_ptr); | |
| 325 png_set_gray_to_rgb(png_ptr); | |
| 326 skColorType = kN32_SkColorType; | |
| 327 skAlphaType = kUnpremul_SkAlphaType; | |
| 328 } else { | |
| 329 skColorType = kGray_8_SkColorType; | |
| 330 skAlphaType = kOpaque_SkAlphaType; | |
| 331 } | |
| 332 break; | |
| 333 case PNG_COLOR_TYPE_GRAY_ALPHA: | |
| 334 //FIXME: support gray with alpha as a color type | |
| 335 //convert to RGBA | |
| 336 png_set_gray_to_rgb(png_ptr); | |
| 337 skColorType = kN32_SkColorType; | |
| 338 skAlphaType = kUnpremul_SkAlphaType; | |
| 339 break; | |
| 340 case PNG_COLOR_TYPE_RGBA: | |
| 341 skColorType = kN32_SkColorType; | |
| 342 skAlphaType = kUnpremul_SkAlphaType; | |
| 343 break; | |
| 344 default: | |
| 345 //all the color types have been covered above | |
| 346 SkASSERT(false); | |
| 347 } | |
| 348 | |
| 349 int numberPasses = png_set_interlace_handling(png_ptr); | |
| 350 if (numberPassesPtr) { | |
| 351 *numberPassesPtr = numberPasses; | |
| 352 } | |
| 353 | |
| 354 // FIXME: Also need to check for sRGB ( https://bug.skia.org/3471 ). | |
| 355 | |
| 356 if (imageInfo) { | |
| 357 *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlp
haType); | |
| 358 } | |
| 359 autoClean.detach(); | |
| 360 if (png_ptrp) { | |
| 361 *png_ptrp = png_ptr; | |
| 362 } | |
| 363 if (info_ptrp) { | |
| 364 *info_ptrp = info_ptr; | |
| 365 } | |
| 366 | |
| 367 return true; | |
| 368 } | |
| 369 | |
| 370 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkRead
er* chunkReader, | |
| 371 png_structp png_ptr, png_infop info_ptr, int bitDepth, in
t numberPasses) | |
| 372 : INHERITED(info, stream) | |
| 373 , fPngChunkReader(SkSafeRef(chunkReader)) | |
| 374 , fPng_ptr(png_ptr) | |
| 375 , fInfo_ptr(info_ptr) | |
| 376 , fSrcConfig(SkSwizzler::kUnknown) | |
| 377 , fNumberPasses(numberPasses) | |
| 378 , fBitDepth(bitDepth) | |
| 379 {} | |
| 380 | |
| 381 SkPngCodec::~SkPngCodec() { | |
| 382 this->destroyReadStruct(); | |
| 383 } | |
| 384 | |
| 385 void SkPngCodec::destroyReadStruct() { | |
| 386 if (fPng_ptr) { | |
| 387 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr | |
| 388 SkASSERT(fInfo_ptr); | |
| 389 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); | |
| 390 fPng_ptr = nullptr; | |
| 391 fInfo_ptr = nullptr; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 /////////////////////////////////////////////////////////////////////////////// | |
| 396 // Getting the pixels | |
| 397 /////////////////////////////////////////////////////////////////////////////// | |
| 398 | |
| 399 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, | |
| 400 const Options& options, | |
| 401 SkPMColor ctable[], | |
| 402 int* ctableCount) { | |
| 403 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 404 // error? | |
| 405 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 406 SkCodecPrintf("setjmp long jump!\n"); | |
| 407 return kInvalidInput; | |
| 408 } | |
| 409 png_read_update_info(fPng_ptr, fInfo_ptr); | |
| 410 | |
| 411 //srcColorType was determined in read_header() which determined png color ty
pe | |
| 412 const SkColorType srcColorType = this->getInfo().colorType(); | |
| 413 | |
| 414 switch (srcColorType) { | |
| 415 case kIndex_8_SkColorType: | |
| 416 //decode palette to Skia format | |
| 417 fSrcConfig = SkSwizzler::kIndex; | |
| 418 if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaT
ype(), | |
| 419 ctableCount)) { | |
| 420 return kInvalidInput; | |
| 421 } | |
| 422 break; | |
| 423 case kGray_8_SkColorType: | |
| 424 fSrcConfig = SkSwizzler::kGray; | |
| 425 break; | |
| 426 case kN32_SkColorType: | |
| 427 if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { | |
| 428 fSrcConfig = SkSwizzler::kRGB; | |
| 429 } else { | |
| 430 fSrcConfig = SkSwizzler::kRGBA; | |
| 431 } | |
| 432 break; | |
| 433 default: | |
| 434 //would have exited before now if the colorType was supported by png | |
| 435 SkASSERT(false); | |
| 436 } | |
| 437 | |
| 438 // Copy the color table to the client if they request kIndex8 mode | |
| 439 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | |
| 440 | |
| 441 // Create the swizzler. SkPngCodec retains ownership of the color table. | |
| 442 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | |
| 443 fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo
, options)); | |
| 444 if (!fSwizzler) { | |
| 445 // FIXME: CreateSwizzler could fail for another reason. | |
| 446 return kUnimplemented; | |
| 447 } | |
| 448 return kSuccess; | |
| 449 } | |
| 450 | |
| 451 | |
| 452 bool SkPngCodec::onRewind() { | |
| 453 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | |
| 454 // succeeds, they will be repopulated, and if it fails, they will | |
| 455 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | |
| 456 // come through this function which will rewind and again attempt | |
| 457 // to reinitialize them. | |
| 458 this->destroyReadStruct(); | |
| 459 | |
| 460 png_structp png_ptr; | |
| 461 png_infop info_ptr; | |
| 462 if (!read_header(this->stream(), fPngChunkReader.get(), &png_ptr, &info_ptr, | |
| 463 nullptr, nullptr, nullptr)) { | |
| 464 return false; | |
| 465 } | |
| 466 | |
| 467 fPng_ptr = png_ptr; | |
| 468 fInfo_ptr = info_ptr; | |
| 469 return true; | |
| 470 } | |
| 471 | |
| 472 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | |
| 473 size_t dstRowBytes, const Options& optio
ns, | |
| 474 SkPMColor ctable[], int* ctableCount, | |
| 475 int* rowsDecoded) { | |
| 476 if (!conversion_possible(requestedInfo, this->getInfo())) { | |
| 477 return kInvalidConversion; | |
| 478 } | |
| 479 if (options.fSubset) { | |
| 480 // Subsets are not supported. | |
| 481 return kUnimplemented; | |
| 482 } | |
| 483 | |
| 484 // Note that ctable and ctableCount may be modified if there is a color tabl
e | |
| 485 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | |
| 486 if (result != kSuccess) { | |
| 487 return result; | |
| 488 } | |
| 489 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 490 // error? | |
| 491 int row = 0; | |
| 492 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | |
| 493 SkAutoTMalloc<uint8_t> storage; | |
| 494 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 495 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | |
| 496 if (fNumberPasses > 1) { | |
| 497 // FIXME (msarett): Handle incomplete interlaced pngs. | |
| 498 return kInvalidInput; | |
| 499 } | |
| 500 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, | |
| 501 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 | |
| 502 // bytes), and if we can't fill the buffer, we immediately fail. | |
| 503 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains | |
| 504 // half that, which may have been enough to contain a non-zero number of
lines, we fail | |
| 505 // when we could have decoded a few more lines and then failed. | |
| 506 // The read function that we provide for libpng has no way of indicating
that we have | |
| 507 // made a partial read. | |
| 508 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does | |
| 509 // it have on regular decode performance? Should we investigate using a
different API | |
| 510 // instead of png_read_row(s)? Chromium uses png_process_data. | |
| 511 *rowsDecoded = row; | |
| 512 return kIncompleteInput; | |
| 513 } | |
| 514 | |
| 515 // FIXME: We could split these out based on subclass. | |
| 516 void* dstRow = dst; | |
| 517 if (fNumberPasses > 1) { | |
| 518 const int width = requestedInfo.width(); | |
| 519 const int height = requestedInfo.height(); | |
| 520 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); | |
| 521 const size_t srcRowBytes = width * bpp; | |
| 522 | |
| 523 storage.reset(width * height * bpp); | |
| 524 uint8_t* const base = storage.get(); | |
| 525 | |
| 526 for (int i = 0; i < fNumberPasses; i++) { | |
| 527 uint8_t* srcRow = base; | |
| 528 for (int y = 0; y < height; y++) { | |
| 529 uint8_t* bmRow = srcRow; | |
| 530 png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1); | |
| 531 srcRow += srcRowBytes; | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 // Now swizzle it. | |
| 536 uint8_t* srcRow = base; | |
| 537 for (int y = 0; y < height; y++) { | |
| 538 fSwizzler->swizzle(dstRow, srcRow); | |
| 539 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 540 srcRow += srcRowBytes; | |
| 541 } | |
| 542 } else { | |
| 543 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConf
ig)); | |
| 544 uint8_t* srcRow = storage.get(); | |
| 545 for (; row < requestedInfo.height(); row++) { | |
| 546 png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); | |
| 547 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetS
canlines. | |
| 548 fSwizzler->swizzle(dstRow, srcRow); | |
| 549 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 // FIXME: do we need substituteTranspColor? Note that we cannot do it for | |
| 554 // scanline decoding, but we could do it here. Alternatively, we could do | |
| 555 // it as we go, instead of in post-processing like SkPNGImageDecoder. | |
| 556 | |
| 557 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 558 // We've already read all the scanlines. This is a success. | |
| 559 return kSuccess; | |
| 560 } | |
| 561 | |
| 562 // read rest of file, and get additional comment and time chunks in info_ptr | |
| 563 png_read_end(fPng_ptr, fInfo_ptr); | |
| 564 | |
| 565 return kSuccess; | |
| 566 } | |
| 567 | |
| 568 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType
) const { | |
| 569 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 570 if (colorPtr) { | |
| 571 return get_color_table_fill_value(colorType, colorPtr, 0); | |
| 572 } | |
| 573 return INHERITED::onGetFillValue(colorType, alphaType); | |
| 574 } | |
| 575 | |
| 576 // Subclass of SkPngCodec which supports scanline decoding | |
| 577 class SkPngScanlineDecoder : public SkPngCodec { | |
| 578 public: | |
| 579 SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, | |
| 580 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth) | |
| 581 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1
) | |
| 582 , fSrcRow(nullptr) | |
| 583 {} | |
| 584 | |
| 585 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | |
| 586 SkPMColor ctable[], int* ctableCount) override { | |
| 587 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 588 return kInvalidConversion; | |
| 589 } | |
| 590 | |
| 591 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | |
| 592 ctableCount); | |
| 593 if (result != kSuccess) { | |
| 594 return result; | |
| 595 } | |
| 596 | |
| 597 fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig())); | |
| 598 fSrcRow = fStorage.get(); | |
| 599 | |
| 600 return kSuccess; | |
| 601 } | |
| 602 | |
| 603 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | |
| 604 // Assume that an error in libpng indicates an incomplete input. | |
| 605 int row = 0; | |
| 606 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
| 607 SkCodecPrintf("setjmp long jump!\n"); | |
| 608 return row; | |
| 609 } | |
| 610 | |
| 611 void* dstRow = dst; | |
| 612 for (; row < count; row++) { | |
| 613 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); | |
| 614 this->swizzler()->swizzle(dstRow, fSrcRow); | |
| 615 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | |
| 616 } | |
| 617 | |
| 618 return row; | |
| 619 } | |
| 620 | |
| 621 bool onSkipScanlines(int count) override { | |
| 622 // Assume that an error in libpng indicates an incomplete input. | |
| 623 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
| 624 SkCodecPrintf("setjmp long jump!\n"); | |
| 625 return false; | |
| 626 } | |
| 627 //there is a potential tradeoff of memory vs speed created by putting th
is in a loop. | |
| 628 //calling png_read_rows in a loop is insignificantly slower than calling
it once with count | |
| 629 //as png_read_rows has it's own loop which calls png_read_row count time
s. | |
| 630 for (int row = 0; row < count; row++) { | |
| 631 png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); | |
| 632 } | |
| 633 return true; | |
| 634 } | |
| 635 | |
| 636 private: | |
| 637 SkAutoTMalloc<uint8_t> fStorage; | |
| 638 uint8_t* fSrcRow; | |
| 639 | |
| 640 typedef SkPngCodec INHERITED; | |
| 641 }; | |
| 642 | |
| 643 | |
| 644 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | |
| 645 public: | |
| 646 SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, | |
| 647 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, | |
| 648 int bitDepth, int numberPasses) | |
| 649 : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, n
umberPasses) | |
| 650 , fHeight(-1) | |
| 651 , fCanSkipRewind(false) | |
| 652 { | |
| 653 SkASSERT(numberPasses != 1); | |
| 654 } | |
| 655 | |
| 656 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | |
| 657 SkPMColor ctable[], int* ctableCount) override { | |
| 658 if (!conversion_possible(dstInfo, this->getInfo())) { | |
| 659 return kInvalidConversion; | |
| 660 } | |
| 661 | |
| 662 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | |
| 663 ctableCount); | |
| 664 if (result != kSuccess) { | |
| 665 return result; | |
| 666 } | |
| 667 | |
| 668 fHeight = dstInfo.height(); | |
| 669 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | |
| 670 fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this-
>srcConfig()); | |
| 671 fGarbageRow.reset(fSrcRowBytes); | |
| 672 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | |
| 673 fCanSkipRewind = true; | |
| 674 | |
| 675 return SkCodec::kSuccess; | |
| 676 } | |
| 677 | |
| 678 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | |
| 679 // rewind stream if have previously called onGetScanlines, | |
| 680 // since we need entire progressive image to get scanlines | |
| 681 if (fCanSkipRewind) { | |
| 682 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | |
| 683 // Next time onGetScanlines is called, we will need to rewind. | |
| 684 fCanSkipRewind = false; | |
| 685 } else { | |
| 686 // rewindIfNeeded resets fCurrScanline, since it assumes that start | |
| 687 // needs to be called again before scanline decoding. PNG scanline | |
| 688 // decoding is the exception, since it needs to rewind between | |
| 689 // calls to getScanlines. Keep track of fCurrScanline, to undo the | |
| 690 // reset. | |
| 691 const int currScanline = this->nextScanline(); | |
| 692 // This method would never be called if currScanline is -1 | |
| 693 SkASSERT(currScanline != -1); | |
| 694 | |
| 695 if (!this->rewindIfNeeded()) { | |
| 696 return kCouldNotRewind; | |
| 697 } | |
| 698 this->updateCurrScanline(currScanline); | |
| 699 } | |
| 700 | |
| 701 if (setjmp(png_jmpbuf(this->png_ptr()))) { | |
| 702 SkCodecPrintf("setjmp long jump!\n"); | |
| 703 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | |
| 704 // we may be able to report that all of the memory has been initiali
zed. Even if we | |
| 705 // fail on the first pass, we can still report than some scanlines a
re initialized. | |
| 706 return 0; | |
| 707 } | |
| 708 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | |
| 709 uint8_t* storagePtr = storage.get(); | |
| 710 uint8_t* srcRow; | |
| 711 const int startRow = this->nextScanline(); | |
| 712 for (int i = 0; i < this->numberPasses(); i++) { | |
| 713 // read rows we planned to skip into garbage row | |
| 714 for (int y = 0; y < startRow; y++){ | |
| 715 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL,
1); | |
| 716 } | |
| 717 // read rows we care about into buffer | |
| 718 srcRow = storagePtr; | |
| 719 for (int y = 0; y < count; y++) { | |
| 720 png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); | |
| 721 srcRow += fSrcRowBytes; | |
| 722 } | |
| 723 // read rows we don't want into garbage buffer | |
| 724 for (int y = 0; y < fHeight - startRow - count; y++) { | |
| 725 png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL,
1); | |
| 726 } | |
| 727 } | |
| 728 //swizzle the rows we care about | |
| 729 srcRow = storagePtr; | |
| 730 void* dstRow = dst; | |
| 731 for (int y = 0; y < count; y++) { | |
| 732 this->swizzler()->swizzle(dstRow, srcRow); | |
| 733 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 734 srcRow += fSrcRowBytes; | |
| 735 } | |
| 736 | |
| 737 return count; | |
| 738 } | |
| 739 | |
| 740 bool onSkipScanlines(int count) override { | |
| 741 // The non-virtual version will update fCurrScanline. | |
| 742 return true; | |
| 743 } | |
| 744 | |
| 745 SkScanlineOrder onGetScanlineOrder() const override { | |
| 746 return kNone_SkScanlineOrder; | |
| 747 } | |
| 748 | |
| 749 private: | |
| 750 int fHeight; | |
| 751 size_t fSrcRowBytes; | |
| 752 SkAutoMalloc fGarbageRow; | |
| 753 uint8_t* fGarbageRowPtr; | |
| 754 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function | |
| 755 // is called whenever some action is taken that reads the stream and | |
| 756 // therefore the next call will require a rewind. So it modifies a boolean | |
| 757 // to note that the *next* time it is called a rewind is needed. | |
| 758 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling | |
| 759 // onStartScanlineDecode followed by onGetScanlines does *not* require a | |
| 760 // rewind. Since rewindIfNeeded does not have this flexibility, we need to | |
| 761 // add another layer. | |
| 762 bool fCanSkipRewind; | |
| 763 | |
| 764 typedef SkPngCodec INHERITED; | |
| 765 }; | |
| 766 | |
| 767 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | |
| 768 SkAutoTDelete<SkStream> streamDeleter(stream); | |
| 769 png_structp png_ptr; | |
| 770 png_infop info_ptr; | |
| 771 SkImageInfo imageInfo; | |
| 772 int bitDepth; | |
| 773 int numberPasses; | |
| 774 | |
| 775 if (!read_header(stream, chunkReader, &png_ptr, &info_ptr, &imageInfo, &bitD
epth, | |
| 776 &numberPasses)) { | |
| 777 return nullptr; | |
| 778 } | |
| 779 | |
| 780 if (1 == numberPasses) { | |
| 781 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk
Reader, | |
| 782 png_ptr, info_ptr, bitDepth); | |
| 783 } | |
| 784 | |
| 785 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(),
chunkReader, | |
| 786 png_ptr, info_ptr, bitDepth, numbe
rPasses); | |
| 787 } | |
| OLD | NEW |