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 |