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