| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "SkBitmap.h" | 8 #include "SkBitmap.h" |
| 9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 fPng_ptr = nullptr; | 85 fPng_ptr = nullptr; |
| 86 fInfo_ptr = nullptr; | 86 fInfo_ptr = nullptr; |
| 87 } | 87 } |
| 88 | 88 |
| 89 private: | 89 private: |
| 90 png_structp fPng_ptr; | 90 png_structp fPng_ptr; |
| 91 png_infop fInfo_ptr; | 91 png_infop fInfo_ptr; |
| 92 }; | 92 }; |
| 93 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) | 93 #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) |
| 94 | 94 |
| 95 // Note: SkColorTable claims to store SkPMColors, which is not necessarily | 95 // Note: SkColorTable claims to store SkPMColors, which is not necessarily the c
ase here. |
| 96 // the case here. | |
| 97 // TODO: If we add support for non-native swizzles, we'll need to handle that he
re. | |
| 98 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { | 96 bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, in
t* ctableCount) { |
| 99 | 97 |
| 100 int numColors; | 98 int numColors; |
| 101 png_color* palette; | 99 png_color* palette; |
| 102 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { | 100 if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { |
| 103 return false; | 101 return false; |
| 104 } | 102 } |
| 105 | 103 |
| 106 // Note: These are not necessarily SkPMColors. | 104 // Note: These are not necessarily SkPMColors. |
| 107 SkPMColor colorPtr[256]; | 105 SkPMColor colorPtr[256]; |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 // implemented to guess sRGB in this case. | 332 // implemented to guess sRGB in this case. |
| 335 return nullptr; | 333 return nullptr; |
| 336 } | 334 } |
| 337 | 335 |
| 338 static int bytes_per_pixel(int bitsPerPixel) { | 336 static int bytes_per_pixel(int bitsPerPixel) { |
| 339 // Note that we will have to change this implementation if we start | 337 // Note that we will have to change this implementation if we start |
| 340 // supporting outputs from libpng that are less than 8-bits per component. | 338 // supporting outputs from libpng that are less than 8-bits per component. |
| 341 return bitsPerPixel / 8; | 339 return bitsPerPixel / 8; |
| 342 } | 340 } |
| 343 | 341 |
| 344 // Subclass of SkPngCodec which supports scanline decoding | 342 static bool png_conversion_possible(const SkImageInfo& dst, const SkImageInfo& s
rc) { |
| 345 class SkPngScanlineDecoder : public SkPngCodec { | 343 // Ensure the alpha type is valid |
| 344 if (!valid_alpha(dst.alphaType(), src.alphaType())) { |
| 345 return false; |
| 346 } |
| 347 |
| 348 // Check for supported color types |
| 349 switch (dst.colorType()) { |
| 350 case kRGBA_8888_SkColorType: |
| 351 case kBGRA_8888_SkColorType: |
| 352 case kRGBA_F16_SkColorType: |
| 353 return true; |
| 354 case kRGB_565_SkColorType: |
| 355 return kOpaque_SkAlphaType == src.alphaType(); |
| 356 default: |
| 357 return dst.colorType() == src.colorType(); |
| 358 } |
| 359 } |
| 360 |
| 361 void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) { |
| 362 const int width = this->getInfo().width(); |
| 363 size_t colorXformBytes = fColorXform ? width * sizeof(uint32_t) : 0; |
| 364 |
| 365 fStorage.reset(SkAlign4(fSrcRowBytes) + colorXformBytes); |
| 366 fSwizzlerSrcRow = fStorage.get(); |
| 367 fColorXformSrcRow = |
| 368 fColorXform ? SkTAddOffset<uint32_t>(fSwizzlerSrcRow, SkAlign4(fSrcR
owBytes)) : 0; |
| 369 } |
| 370 |
| 371 class SkPngNormalCodec : public SkPngCodec { |
| 346 public: | 372 public: |
| 347 SkPngScanlineDecoder(int width, int height, const SkEncodedInfo& info, SkStr
eam* stream, | 373 SkPngNormalCodec(int width, int height, const SkEncodedInfo& info, SkStream*
stream, |
| 348 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, | 374 SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_p
tr, int bitDepth, |
| 349 sk_sp<SkColorSpace> colorSpace) | 375 sk_sp<SkColorSpace> colorSpace) |
| 350 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, | 376 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, 1, |
| 351 colorSpace) | 377 colorSpace) |
| 352 , fSrcRow(nullptr) | |
| 353 {} | 378 {} |
| 354 | 379 |
| 355 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 380 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
| 356 SkPMColor ctable[], int* ctableCount) override { | 381 SkPMColor ctable[], int* ctableCount) override { |
| 357 if (!conversion_possible(dstInfo, this->getInfo())) { | 382 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 383 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 384 { |
| 358 return kInvalidConversion; | 385 return kInvalidConversion; |
| 359 } | 386 } |
| 360 | 387 |
| 361 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 388 this->allocateStorage(dstInfo); |
| 362 ctableCount); | |
| 363 if (result != kSuccess) { | |
| 364 return result; | |
| 365 } | |
| 366 | |
| 367 fStorage.reset(this->getInfo().width() * | |
| 368 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()))); | |
| 369 fSrcRow = fStorage.get(); | |
| 370 | |
| 371 return kSuccess; | 389 return kSuccess; |
| 372 } | 390 } |
| 373 | 391 |
| 374 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | 392 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou
nt, int startRow) |
| 393 override { |
| 394 SkASSERT(0 == startRow); |
| 395 |
| 375 // Assume that an error in libpng indicates an incomplete input. | 396 // Assume that an error in libpng indicates an incomplete input. |
| 376 int row = 0; | 397 int y = 0; |
| 377 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 398 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 378 SkCodecPrintf("setjmp long jump!\n"); | 399 SkCodecPrintf("Failed to read row.\n"); |
| 379 return row; | 400 return y; |
| 380 } | 401 } |
| 381 | 402 |
| 382 void* dstRow = dst; | 403 void* swizzlerDstRow = dst; |
| 383 for (; row < count; row++) { | 404 size_t swizzlerDstRowBytes = rowBytes; |
| 384 png_read_row(this->png_ptr(), fSrcRow, nullptr); | 405 if (fColorXform) { |
| 385 this->swizzler()->swizzle(dstRow, fSrcRow); | 406 swizzlerDstRow = fColorXformSrcRow; |
| 386 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | 407 swizzlerDstRowBytes = 0; |
| 387 } | 408 } |
| 388 | 409 |
| 389 return row; | 410 SkAlphaType xformAlphaType = (kOpaque_SkAlphaType == this->getInfo().alp
haType()) ? |
| 411 kOpaque_SkAlphaType : dstInfo.alphaType(); |
| 412 for (; y < count; y++) { |
| 413 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 414 fSwizzler->swizzle(swizzlerDstRow, fSwizzlerSrcRow); |
| 415 |
| 416 if (fColorXform) { |
| 417 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, dstInf
o.width(), |
| 418 dstInfo.colorType(), xformAlphaType); |
| 419 dst = SkTAddOffset<void>(dst, rowBytes); |
| 420 } |
| 421 |
| 422 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy
tes); |
| 423 } |
| 424 |
| 425 return y; |
| 426 } |
| 427 |
| 428 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 429 return this->readRows(this->dstInfo(), dst, rowBytes, count, 0); |
| 390 } | 430 } |
| 391 | 431 |
| 392 bool onSkipScanlines(int count) override { | 432 bool onSkipScanlines(int count) override { |
| 393 // Assume that an error in libpng indicates an incomplete input. | 433 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 394 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 434 SkCodecPrintf("Failed to skip row.\n"); |
| 395 SkCodecPrintf("setjmp long jump!\n"); | |
| 396 return false; | 435 return false; |
| 397 } | 436 } |
| 398 | 437 |
| 399 for (int row = 0; row < count; row++) { | 438 for (int row = 0; row < count; row++) { |
| 400 png_read_row(this->png_ptr(), fSrcRow, nullptr); | 439 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 401 } | 440 } |
| 402 return true; | 441 return true; |
| 403 } | 442 } |
| 404 | 443 |
| 405 private: | |
| 406 SkAutoTMalloc<uint8_t> fStorage; | |
| 407 uint8_t* fSrcRow; | |
| 408 | |
| 409 typedef SkPngCodec INHERITED; | 444 typedef SkPngCodec INHERITED; |
| 410 }; | 445 }; |
| 411 | 446 |
| 412 | 447 |
| 413 class SkPngInterlacedScanlineDecoder : public SkPngCodec { | 448 class SkPngInterlacedCodec : public SkPngCodec { |
| 414 public: | 449 public: |
| 415 SkPngInterlacedScanlineDecoder(int width, int height, const SkEncodedInfo& i
nfo, | 450 SkPngInterlacedCodec(int width, int height, const SkEncodedInfo& info, |
| 416 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, | 451 SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr
, |
| 417 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) | 452 png_infop info_ptr, int bitDepth, int numberPasses, sk_sp<SkColorSpa
ce> colorSpace) |
| 418 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, | 453 : INHERITED(width, height, info, stream, chunkReader, png_ptr, info_ptr,
bitDepth, |
| 419 numberPasses, colorSpace) | 454 numberPasses, colorSpace) |
| 420 , fHeight(-1) | |
| 421 , fCanSkipRewind(false) | 455 , fCanSkipRewind(false) |
| 422 { | 456 { |
| 423 SkASSERT(numberPasses != 1); | 457 SkASSERT(numberPasses != 1); |
| 424 } | 458 } |
| 425 | 459 |
| 426 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, | 460 Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& opti
ons, |
| 427 SkPMColor ctable[], int* ctableCount) override { | 461 SkPMColor ctable[], int* ctableCount) override { |
| 428 if (!conversion_possible(dstInfo, this->getInfo())) { | 462 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 463 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 464 { |
| 429 return kInvalidConversion; | 465 return kInvalidConversion; |
| 430 } | 466 } |
| 431 | 467 |
| 432 const Result result = this->initializeSwizzler(dstInfo, options, ctable, | 468 this->allocateStorage(dstInfo); |
| 433 ctableCount); | |
| 434 if (result != kSuccess) { | |
| 435 return result; | |
| 436 } | |
| 437 | |
| 438 fHeight = dstInfo.height(); | |
| 439 // FIXME: This need not be called on a second call to onStartScanlineDec
ode. | |
| 440 fSrcRowBytes = this->getInfo().width() * | |
| 441 (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel())); | |
| 442 fGarbageRow.reset(fSrcRowBytes); | |
| 443 fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); | |
| 444 fCanSkipRewind = true; | 469 fCanSkipRewind = true; |
| 445 | |
| 446 return SkCodec::kSuccess; | 470 return SkCodec::kSuccess; |
| 447 } | 471 } |
| 448 | 472 |
| 449 int onGetScanlines(void* dst, int count, size_t dstRowBytes) override { | 473 int readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int cou
nt, int startRow) |
| 474 override { |
| 475 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 476 SkCodecPrintf("Failed to get scanlines.\n"); |
| 477 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, |
| 478 // we may be able to report that all of the memory has been initiali
zed. Even if we |
| 479 // fail on the first pass, we can still report than some scanlines a
re initialized. |
| 480 return 0; |
| 481 } |
| 482 |
| 483 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); |
| 484 uint8_t* srcRow; |
| 485 for (int i = 0; i < fNumberPasses; i++) { |
| 486 // Discard rows that we planned to skip. |
| 487 for (int y = 0; y < startRow; y++){ |
| 488 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 489 } |
| 490 // Read rows we care about into storage. |
| 491 srcRow = storage.get(); |
| 492 for (int y = 0; y < count; y++) { |
| 493 png_read_row(fPng_ptr, srcRow, nullptr); |
| 494 srcRow += fSrcRowBytes; |
| 495 } |
| 496 // Discard rows that we don't need. |
| 497 for (int y = 0; y < this->getInfo().height() - startRow - count; y++
) { |
| 498 png_read_row(fPng_ptr, fSwizzlerSrcRow, nullptr); |
| 499 } |
| 500 } |
| 501 |
| 502 // Swizzle and xform the rows we care about |
| 503 void* swizzlerDstRow = dst; |
| 504 size_t swizzlerDstRowBytes = rowBytes; |
| 505 if (fColorXform) { |
| 506 swizzlerDstRow = fColorXformSrcRow; |
| 507 swizzlerDstRowBytes = 0; |
| 508 } |
| 509 |
| 510 SkAlphaType xformAlphaType = (kOpaque_SkAlphaType == this->getInfo().alp
haType()) ? |
| 511 kOpaque_SkAlphaType : dstInfo.alphaType(); |
| 512 srcRow = storage.get(); |
| 513 for (int y = 0; y < count; y++) { |
| 514 fSwizzler->swizzle(swizzlerDstRow, srcRow); |
| 515 srcRow = SkTAddOffset<uint8_t>(srcRow, fSrcRowBytes); |
| 516 |
| 517 if (fColorXform) { |
| 518 if (fColorXform) { |
| 519 fColorXform->apply(dst, (const uint32_t*) swizzlerDstRow, ds
tInfo.width(), |
| 520 dstInfo.colorType(), xformAlphaType); |
| 521 dst = SkTAddOffset<void>(dst, rowBytes); |
| 522 } |
| 523 } |
| 524 |
| 525 swizzlerDstRow = SkTAddOffset<void>(swizzlerDstRow, swizzlerDstRowBy
tes); |
| 526 } |
| 527 |
| 528 return count; |
| 529 } |
| 530 |
| 531 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 450 // rewind stream if have previously called onGetScanlines, | 532 // rewind stream if have previously called onGetScanlines, |
| 451 // since we need entire progressive image to get scanlines | 533 // since we need entire progressive image to get scanlines |
| 452 if (fCanSkipRewind) { | 534 if (fCanSkipRewind) { |
| 453 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. | 535 // We already rewound in onStartScanlineDecode, so there is no reaso
n to rewind. |
| 454 // Next time onGetScanlines is called, we will need to rewind. | 536 // Next time onGetScanlines is called, we will need to rewind. |
| 455 fCanSkipRewind = false; | 537 fCanSkipRewind = false; |
| 456 } else { | 538 } else { |
| 457 // rewindIfNeeded resets fCurrScanline, since it assumes that start | 539 // rewindIfNeeded resets fCurrScanline, since it assumes that start |
| 458 // needs to be called again before scanline decoding. PNG scanline | 540 // needs to be called again before scanline decoding. PNG scanline |
| 459 // decoding is the exception, since it needs to rewind between | 541 // decoding is the exception, since it needs to rewind between |
| 460 // calls to getScanlines. Keep track of fCurrScanline, to undo the | 542 // calls to getScanlines. Keep track of fCurrScanline, to undo the |
| 461 // reset. | 543 // reset. |
| 462 const int currScanline = this->nextScanline(); | 544 const int currScanline = this->nextScanline(); |
| 463 // This method would never be called if currScanline is -1 | 545 // This method would never be called if currScanline is -1 |
| 464 SkASSERT(currScanline != -1); | 546 SkASSERT(currScanline != -1); |
| 465 | 547 |
| 466 if (!this->rewindIfNeeded()) { | 548 if (!this->rewindIfNeeded()) { |
| 467 return kCouldNotRewind; | 549 return kCouldNotRewind; |
| 468 } | 550 } |
| 469 this->updateCurrScanline(currScanline); | 551 this->updateCurrScanline(currScanline); |
| 470 } | 552 } |
| 471 | 553 |
| 472 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 554 return this->readRows(this->dstInfo(), dst, rowBytes, count, this->nextS
canline()); |
| 473 SkCodecPrintf("setjmp long jump!\n"); | |
| 474 // FIXME (msarett): Returning 0 is pessimistic. If we can complete
a single pass, | |
| 475 // we may be able to report that all of the memory has been initiali
zed. Even if we | |
| 476 // fail on the first pass, we can still report than some scanlines a
re initialized. | |
| 477 return 0; | |
| 478 } | |
| 479 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | |
| 480 uint8_t* storagePtr = storage.get(); | |
| 481 uint8_t* srcRow; | |
| 482 const int startRow = this->nextScanline(); | |
| 483 for (int i = 0; i < this->numberPasses(); i++) { | |
| 484 // read rows we planned to skip into garbage row | |
| 485 for (int y = 0; y < startRow; y++){ | |
| 486 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
| 487 } | |
| 488 // read rows we care about into buffer | |
| 489 srcRow = storagePtr; | |
| 490 for (int y = 0; y < count; y++) { | |
| 491 png_read_row(this->png_ptr(), srcRow, nullptr); | |
| 492 srcRow += fSrcRowBytes; | |
| 493 } | |
| 494 // read rows we don't want into garbage buffer | |
| 495 for (int y = 0; y < fHeight - startRow - count; y++) { | |
| 496 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); | |
| 497 } | |
| 498 } | |
| 499 //swizzle the rows we care about | |
| 500 srcRow = storagePtr; | |
| 501 void* dstRow = dst; | |
| 502 for (int y = 0; y < count; y++) { | |
| 503 this->swizzler()->swizzle(dstRow, srcRow); | |
| 504 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 505 srcRow += fSrcRowBytes; | |
| 506 } | |
| 507 | |
| 508 return count; | |
| 509 } | 555 } |
| 510 | 556 |
| 511 bool onSkipScanlines(int count) override { | 557 bool onSkipScanlines(int count) override { |
| 512 // The non-virtual version will update fCurrScanline. | 558 // The non-virtual version will update fCurrScanline. |
| 513 return true; | 559 return true; |
| 514 } | 560 } |
| 515 | 561 |
| 516 SkScanlineOrder onGetScanlineOrder() const override { | 562 SkScanlineOrder onGetScanlineOrder() const override { |
| 517 return kNone_SkScanlineOrder; | 563 return kNone_SkScanlineOrder; |
| 518 } | 564 } |
| 519 | 565 |
| 520 private: | 566 private: |
| 521 int fHeight; | |
| 522 size_t fSrcRowBytes; | |
| 523 SkAutoMalloc fGarbageRow; | |
| 524 uint8_t* fGarbageRowPtr; | |
| 525 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function | 567 // FIXME: This imitates behavior in SkCodec::rewindIfNeeded. That function |
| 526 // is called whenever some action is taken that reads the stream and | 568 // is called whenever some action is taken that reads the stream and |
| 527 // therefore the next call will require a rewind. So it modifies a boolean | 569 // therefore the next call will require a rewind. So it modifies a boolean |
| 528 // to note that the *next* time it is called a rewind is needed. | 570 // to note that the *next* time it is called a rewind is needed. |
| 529 // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling | 571 // SkPngInterlacedCodec has an extra wrinkle - calling |
| 530 // onStartScanlineDecode followed by onGetScanlines does *not* require a | 572 // onStartScanlineDecode followed by onGetScanlines does *not* require a |
| 531 // rewind. Since rewindIfNeeded does not have this flexibility, we need to | 573 // rewind. Since rewindIfNeeded does not have this flexibility, we need to |
| 532 // add another layer. | 574 // add another layer. |
| 533 bool fCanSkipRewind; | 575 bool fCanSkipRewind; |
| 534 | 576 |
| 535 typedef SkPngCodec INHERITED; | 577 typedef SkPngCodec INHERITED; |
| 536 }; | 578 }; |
| 537 | 579 |
| 538 // Reads the header and initializes the output fields, if not NULL. | 580 // Reads the header and initializes the output fields, if not NULL. |
| 539 // | 581 // |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 if (outCodec) { | 719 if (outCodec) { |
| 678 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); | 720 sk_sp<SkColorSpace> colorSpace = read_color_space(png_ptr, info_ptr); |
| 679 if (!colorSpace) { | 721 if (!colorSpace) { |
| 680 // Treat unmarked pngs as sRGB. | 722 // Treat unmarked pngs as sRGB. |
| 681 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); | 723 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); |
| 682 } | 724 } |
| 683 | 725 |
| 684 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); | 726 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); |
| 685 | 727 |
| 686 if (1 == numberPasses) { | 728 if (1 == numberPasses) { |
| 687 *outCodec = new SkPngScanlineDecoder(origWidth, origHeight, info, st
ream, | 729 *outCodec = new SkPngNormalCodec(origWidth, origHeight, info, stream
, |
| 688 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); | 730 chunkReader, png_ptr, info_ptr, bitDepth, colorSpace); |
| 689 } else { | 731 } else { |
| 690 *outCodec = new SkPngInterlacedScanlineDecoder(origWidth, origHeight
, info, stream, | 732 *outCodec = new SkPngInterlacedCodec(origWidth, origHeight, info, st
ream, |
| 691 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); | 733 chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, colo
rSpace); |
| 692 } | 734 } |
| 693 } | 735 } |
| 694 | 736 |
| 695 return true; | 737 return true; |
| 696 } | 738 } |
| 697 | 739 |
| 698 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, | 740 SkPngCodec::SkPngCodec(int width, int height, const SkEncodedInfo& info, SkStrea
m* stream, |
| 699 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, | 741 SkPngChunkReader* chunkReader, png_structp png_ptr, png_i
nfop info_ptr, |
| 700 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) | 742 int bitDepth, int numberPasses, sk_sp<SkColorSpace> color
Space) |
| 701 : INHERITED(width, height, info, stream, colorSpace) | 743 : INHERITED(width, height, info, stream, colorSpace) |
| 702 , fPngChunkReader(SkSafeRef(chunkReader)) | 744 , fPngChunkReader(SkSafeRef(chunkReader)) |
| 703 , fPng_ptr(png_ptr) | 745 , fPng_ptr(png_ptr) |
| 704 , fInfo_ptr(info_ptr) | 746 , fInfo_ptr(info_ptr) |
| 747 , fSwizzlerSrcRow(nullptr) |
| 748 , fColorXformSrcRow(nullptr) |
| 749 , fSrcRowBytes(width * (bytes_per_pixel(this->getEncodedInfo().bitsPerPixel(
)))) |
| 705 , fNumberPasses(numberPasses) | 750 , fNumberPasses(numberPasses) |
| 706 , fBitDepth(bitDepth) | 751 , fBitDepth(bitDepth) |
| 707 {} | 752 {} |
| 708 | 753 |
| 709 SkPngCodec::~SkPngCodec() { | 754 SkPngCodec::~SkPngCodec() { |
| 710 this->destroyReadStruct(); | 755 this->destroyReadStruct(); |
| 711 } | 756 } |
| 712 | 757 |
| 713 void SkPngCodec::destroyReadStruct() { | 758 void SkPngCodec::destroyReadStruct() { |
| 714 if (fPng_ptr) { | 759 if (fPng_ptr) { |
| 715 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr | 760 // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr |
| 716 SkASSERT(fInfo_ptr); | 761 SkASSERT(fInfo_ptr); |
| 717 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); | 762 png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); |
| 718 fPng_ptr = nullptr; | 763 fPng_ptr = nullptr; |
| 719 fInfo_ptr = nullptr; | 764 fInfo_ptr = nullptr; |
| 720 } | 765 } |
| 721 } | 766 } |
| 722 | 767 |
| 723 /////////////////////////////////////////////////////////////////////////////// | 768 /////////////////////////////////////////////////////////////////////////////// |
| 724 // Getting the pixels | 769 // Getting the pixels |
| 725 /////////////////////////////////////////////////////////////////////////////// | 770 /////////////////////////////////////////////////////////////////////////////// |
| 726 | 771 |
| 727 SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, | 772 bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& opt
ions, |
| 728 const Options& options, | 773 SkPMColor ctable[], int* ctableCount) { |
| 729 SkPMColor ctable[], | |
| 730 int* ctableCount) { | |
| 731 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 732 // error? | |
| 733 if (setjmp(png_jmpbuf(fPng_ptr))) { | 774 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 734 SkCodecPrintf("setjmp long jump!\n"); | 775 SkCodecPrintf("Failed on png_read_update_info.\n"); |
| 735 return kInvalidInput; | 776 return false; |
| 736 } | 777 } |
| 737 png_read_update_info(fPng_ptr, fInfo_ptr); | 778 png_read_update_info(fPng_ptr, fInfo_ptr); |
| 738 | 779 |
| 780 // It's important to reset fColorXform to nullptr. We don't do this on rewi
nding |
| 781 // because the interlaced scanline decoder may need to rewind. |
| 782 fColorXform = nullptr; |
| 783 SkImageInfo swizzlerInfo = dstInfo; |
| 784 bool needsColorXform = needs_color_xform(dstInfo, this->getInfo()); |
| 785 if (needsColorXform) { |
| 786 switch (dstInfo.colorType()) { |
| 787 case kRGBA_8888_SkColorType: |
| 788 case kBGRA_8888_SkColorType: |
| 789 case kRGBA_F16_SkColorType: |
| 790 swizzlerInfo = swizzlerInfo.makeColorType(kRGBA_8888_SkColorType
); |
| 791 if (kPremul_SkAlphaType == dstInfo.alphaType()) { |
| 792 swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaT
ype); |
| 793 } |
| 794 break; |
| 795 default: |
| 796 return false; |
| 797 } |
| 798 |
| 799 fColorXform = SkColorSpaceXform::New(sk_ref_sp(this->getInfo().colorSpac
e()), |
| 800 sk_ref_sp(dstInfo.colorSpace())); |
| 801 |
| 802 if (!fColorXform && kRGBA_F16_SkColorType == dstInfo.colorType()) { |
| 803 return false; |
| 804 } |
| 805 } |
| 806 |
| 739 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { | 807 if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) { |
| 740 if (!this->createColorTable(requestedInfo.colorType(), | 808 if (!this->createColorTable(swizzlerInfo.colorType(), |
| 741 kPremul_SkAlphaType == requestedInfo.alphaType(), ctableCount))
{ | 809 kPremul_SkAlphaType == swizzlerInfo.alphaTyp
e(), ctableCount)) { |
| 742 return kInvalidInput; | 810 return false; |
| 743 } | 811 } |
| 744 } | 812 } |
| 745 | 813 |
| 746 // Copy the color table to the client if they request kIndex8 mode | 814 // Copy the color table to the client if they request kIndex8 mode |
| 747 copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); | 815 copy_color_table(swizzlerInfo, fColorTable, ctable, ctableCount); |
| 748 | 816 |
| 749 // Create the swizzler. SkPngCodec retains ownership of the color table. | 817 // Create the swizzler. SkPngCodec retains ownership of the color table. |
| 750 const SkPMColor* colors = get_color_ptr(fColorTable.get()); | 818 const SkPMColor* colors = get_color_ptr(fColorTable.get()); |
| 751 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, r
equestedInfo, | 819 fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, s
wizzlerInfo, |
| 752 options)); | 820 options)); |
| 753 SkASSERT(fSwizzler); | 821 SkASSERT(fSwizzler); |
| 754 | 822 return true; |
| 755 return kSuccess; | |
| 756 } | 823 } |
| 757 | 824 |
| 758 | |
| 759 bool SkPngCodec::onRewind() { | 825 bool SkPngCodec::onRewind() { |
| 760 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header | 826 // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header |
| 761 // succeeds, they will be repopulated, and if it fails, they will | 827 // succeeds, they will be repopulated, and if it fails, they will |
| 762 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will | 828 // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will |
| 763 // come through this function which will rewind and again attempt | 829 // come through this function which will rewind and again attempt |
| 764 // to reinitialize them. | 830 // to reinitialize them. |
| 765 this->destroyReadStruct(); | 831 this->destroyReadStruct(); |
| 766 | 832 |
| 767 png_structp png_ptr; | 833 png_structp png_ptr; |
| 768 png_infop info_ptr; | 834 png_infop info_ptr; |
| 769 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &
info_ptr)) { | 835 if (!read_header(this->stream(), fPngChunkReader.get(), nullptr, &png_ptr, &
info_ptr)) { |
| 770 return false; | 836 return false; |
| 771 } | 837 } |
| 772 | 838 |
| 773 fPng_ptr = png_ptr; | 839 fPng_ptr = png_ptr; |
| 774 fInfo_ptr = info_ptr; | 840 fInfo_ptr = info_ptr; |
| 775 return true; | 841 return true; |
| 776 } | 842 } |
| 777 | 843 |
| 778 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
dst, | 844 SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, |
| 779 size_t dstRowBytes, const Options& optio
ns, | 845 size_t rowBytes, const Options& options, |
| 780 SkPMColor ctable[], int* ctableCount, | 846 SkPMColor ctable[], int* ctableCount, |
| 781 int* rowsDecoded) { | 847 int* rowsDecoded) { |
| 782 if (!conversion_possible(requestedInfo, this->getInfo())) { | 848 if (!png_conversion_possible(dstInfo, this->getInfo()) || |
| 849 !this->initializeXforms(dstInfo, options, ctable, ctableCount)) |
| 850 { |
| 783 return kInvalidConversion; | 851 return kInvalidConversion; |
| 784 } | 852 } |
| 853 |
| 785 if (options.fSubset) { | 854 if (options.fSubset) { |
| 786 // Subsets are not supported. | |
| 787 return kUnimplemented; | 855 return kUnimplemented; |
| 788 } | 856 } |
| 789 | 857 |
| 790 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 858 this->allocateStorage(dstInfo); |
| 791 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | 859 int count = this->readRows(dstInfo, dst, rowBytes, dstInfo.height(), 0); |
| 792 if (result != kSuccess) { | 860 if (count > dstInfo.height()) { |
| 793 return result; | 861 *rowsDecoded = count; |
| 862 return kIncompleteInput; |
| 794 } | 863 } |
| 795 | 864 |
| 796 const int width = requestedInfo.width(); | |
| 797 const int height = requestedInfo.height(); | |
| 798 const int bpp = bytes_per_pixel(this->getEncodedInfo().bitsPerPixel()); | |
| 799 const size_t srcRowBytes = width * bpp; | |
| 800 | |
| 801 // FIXME: Could we use the return value of setjmp to specify the type of | |
| 802 // error? | |
| 803 int row = 0; | |
| 804 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | |
| 805 SkAutoTMalloc<uint8_t> storage; | |
| 806 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 807 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | |
| 808 if (fNumberPasses > 1) { | |
| 809 // FIXME (msarett): Handle incomplete interlaced pngs. | |
| 810 return (row == height) ? kSuccess : kInvalidInput; | |
| 811 } | |
| 812 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, | |
| 813 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 | |
| 814 // bytes), and if we can't fill the buffer, we immediately fail. | |
| 815 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains | |
| 816 // half that, which may have been enough to contain a non-zero number of
lines, we fail | |
| 817 // when we could have decoded a few more lines and then failed. | |
| 818 // The read function that we provide for libpng has no way of indicating
that we have | |
| 819 // made a partial read. | |
| 820 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does | |
| 821 // it have on regular decode performance? Should we investigate using a
different API | |
| 822 // instead of png_read_row? Chromium uses png_process_data. | |
| 823 *rowsDecoded = row; | |
| 824 return (row == height) ? kSuccess : kIncompleteInput; | |
| 825 } | |
| 826 | |
| 827 // FIXME: We could split these out based on subclass. | |
| 828 void* dstRow = dst; | |
| 829 if (fNumberPasses > 1) { | |
| 830 storage.reset(height * srcRowBytes); | |
| 831 uint8_t* const base = storage.get(); | |
| 832 | |
| 833 for (int i = 0; i < fNumberPasses; i++) { | |
| 834 uint8_t* srcRow = base; | |
| 835 for (int y = 0; y < height; y++) { | |
| 836 png_read_row(fPng_ptr, srcRow, nullptr); | |
| 837 srcRow += srcRowBytes; | |
| 838 } | |
| 839 } | |
| 840 | |
| 841 // Now swizzle it. | |
| 842 uint8_t* srcRow = base; | |
| 843 for (; row < height; row++) { | |
| 844 fSwizzler->swizzle(dstRow, srcRow); | |
| 845 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 846 srcRow += srcRowBytes; | |
| 847 } | |
| 848 } else { | |
| 849 storage.reset(srcRowBytes); | |
| 850 uint8_t* srcRow = storage.get(); | |
| 851 for (; row < height; row++) { | |
| 852 png_read_row(fPng_ptr, srcRow, nullptr); | |
| 853 fSwizzler->swizzle(dstRow, srcRow); | |
| 854 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | |
| 855 } | |
| 856 } | |
| 857 | |
| 858 // read rest of file, and get additional comment and time chunks in info_ptr | |
| 859 png_read_end(fPng_ptr, fInfo_ptr); | |
| 860 | |
| 861 return kSuccess; | 865 return kSuccess; |
| 862 } | 866 } |
| 863 | 867 |
| 864 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 868 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
| 865 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 869 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 866 if (colorPtr) { | 870 if (colorPtr) { |
| 867 return get_color_table_fill_value(colorType, colorPtr, 0); | 871 return get_color_table_fill_value(colorType, colorPtr, 0); |
| 868 } | 872 } |
| 869 return INHERITED::onGetFillValue(colorType); | 873 return INHERITED::onGetFillValue(colorType); |
| 870 } | 874 } |
| 871 | 875 |
| 872 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { | 876 SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead
er) { |
| 873 SkAutoTDelete<SkStream> streamDeleter(stream); | 877 SkAutoTDelete<SkStream> streamDeleter(stream); |
| 874 | 878 |
| 875 SkCodec* outCodec; | 879 SkCodec* outCodec; |
| 876 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { | 880 if (read_header(stream, chunkReader, &outCodec, nullptr, nullptr)) { |
| 877 // Codec has taken ownership of the stream. | 881 // Codec has taken ownership of the stream. |
| 878 SkASSERT(outCodec); | 882 SkASSERT(outCodec); |
| 879 streamDeleter.release(); | 883 streamDeleter.release(); |
| 880 return outCodec; | 884 return outCodec; |
| 881 } | 885 } |
| 882 | 886 |
| 883 return nullptr; | 887 return nullptr; |
| 884 } | 888 } |
| OLD | NEW |