| 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 "SkCodecPriv.h" | 8 #include "SkCodecPriv.h" |
| 9 #include "SkColorPriv.h" | 9 #include "SkColorPriv.h" |
| 10 #include "SkColorTable.h" | 10 #include "SkColorTable.h" |
| (...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 471 if (options.fSubset) { | 471 if (options.fSubset) { |
| 472 // Subsets are not supported. | 472 // Subsets are not supported. |
| 473 return kUnimplemented; | 473 return kUnimplemented; |
| 474 } | 474 } |
| 475 | 475 |
| 476 // Note that ctable and ctableCount may be modified if there is a color tabl
e | 476 // Note that ctable and ctableCount may be modified if there is a color tabl
e |
| 477 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); | 477 const Result result = this->initializeSwizzler(requestedInfo, options, ctabl
e, ctableCount); |
| 478 if (result != kSuccess) { | 478 if (result != kSuccess) { |
| 479 return result; | 479 return result; |
| 480 } | 480 } |
| 481 |
| 482 const int width = requestedInfo.width(); |
| 483 const int height = requestedInfo.height(); |
| 484 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); |
| 485 const size_t srcRowBytes = width * bpp; |
| 486 |
| 481 // FIXME: Could we use the return value of setjmp to specify the type of | 487 // FIXME: Could we use the return value of setjmp to specify the type of |
| 482 // error? | 488 // error? |
| 483 int row = 0; | 489 int row = 0; |
| 484 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. | 490 // This must be declared above the call to setjmp to avoid memory leaks on i
ncomplete images. |
| 485 SkAutoTMalloc<uint8_t> storage; | 491 SkAutoTMalloc<uint8_t> storage; |
| 486 if (setjmp(png_jmpbuf(fPng_ptr))) { | 492 if (setjmp(png_jmpbuf(fPng_ptr))) { |
| 487 // Assume that any error that occurs while reading rows is caused by an
incomplete input. | 493 // Assume that any error that occurs while reading rows is caused by an
incomplete input. |
| 488 if (fNumberPasses > 1) { | 494 if (fNumberPasses > 1) { |
| 489 // FIXME (msarett): Handle incomplete interlaced pngs. | 495 // FIXME (msarett): Handle incomplete interlaced pngs. |
| 490 return kInvalidInput; | 496 return (row == height) ? kSuccess : kInvalidInput; |
| 491 } | 497 } |
| 492 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, | 498 // FIXME: We do a poor job on incomplete pngs compared to other decoders
(ex: Chromium, |
| 493 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 | 499 // Ubuntu Image Viewer). This is because we use the default buffer size
in libpng (8192 |
| 494 // bytes), and if we can't fill the buffer, we immediately fail. | 500 // bytes), and if we can't fill the buffer, we immediately fail. |
| 495 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains | 501 // For example, if we try to read 8192 bytes, and the image (incorrectly
) only contains |
| 496 // half that, which may have been enough to contain a non-zero number of
lines, we fail | 502 // half that, which may have been enough to contain a non-zero number of
lines, we fail |
| 497 // when we could have decoded a few more lines and then failed. | 503 // when we could have decoded a few more lines and then failed. |
| 498 // The read function that we provide for libpng has no way of indicating
that we have | 504 // The read function that we provide for libpng has no way of indicating
that we have |
| 499 // made a partial read. | 505 // made a partial read. |
| 500 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does | 506 // Making our buffer size smaller improves our incomplete decodes, but w
hat impact does |
| 501 // it have on regular decode performance? Should we investigate using a
different API | 507 // it have on regular decode performance? Should we investigate using a
different API |
| 502 // instead of png_read_row(s)? Chromium uses png_process_data. | 508 // instead of png_read_row? Chromium uses png_process_data. |
| 503 *rowsDecoded = row; | 509 *rowsDecoded = row; |
| 504 return kIncompleteInput; | 510 return (row == height) ? kSuccess : kIncompleteInput; |
| 505 } | 511 } |
| 506 | 512 |
| 507 // FIXME: We could split these out based on subclass. | 513 // FIXME: We could split these out based on subclass. |
| 508 void* dstRow = dst; | 514 void* dstRow = dst; |
| 509 if (fNumberPasses > 1) { | 515 if (fNumberPasses > 1) { |
| 510 const int width = requestedInfo.width(); | 516 storage.reset(height * srcRowBytes); |
| 511 const int height = requestedInfo.height(); | |
| 512 const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); | |
| 513 const size_t srcRowBytes = width * bpp; | |
| 514 | |
| 515 storage.reset(width * height * bpp); | |
| 516 uint8_t* const base = storage.get(); | 517 uint8_t* const base = storage.get(); |
| 517 | 518 |
| 518 for (int i = 0; i < fNumberPasses; i++) { | 519 for (int i = 0; i < fNumberPasses; i++) { |
| 519 uint8_t* srcRow = base; | 520 uint8_t* srcRow = base; |
| 520 for (int y = 0; y < height; y++) { | 521 for (int y = 0; y < height; y++) { |
| 521 uint8_t* bmRow = srcRow; | 522 png_read_row(fPng_ptr, srcRow, nullptr); |
| 522 png_read_rows(fPng_ptr, &bmRow, nullptr, 1); | |
| 523 srcRow += srcRowBytes; | 523 srcRow += srcRowBytes; |
| 524 } | 524 } |
| 525 } | 525 } |
| 526 | 526 |
| 527 // Now swizzle it. | 527 // Now swizzle it. |
| 528 uint8_t* srcRow = base; | 528 uint8_t* srcRow = base; |
| 529 for (int y = 0; y < height; y++) { | 529 for (; row < height; row++) { |
| 530 fSwizzler->swizzle(dstRow, srcRow); | 530 fSwizzler->swizzle(dstRow, srcRow); |
| 531 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 531 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 532 srcRow += srcRowBytes; | 532 srcRow += srcRowBytes; |
| 533 } | 533 } |
| 534 } else { | 534 } else { |
| 535 storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConf
ig)); | 535 storage.reset(srcRowBytes); |
| 536 uint8_t* srcRow = storage.get(); | 536 uint8_t* srcRow = storage.get(); |
| 537 for (; row < requestedInfo.height(); row++) { | 537 for (; row < height; row++) { |
| 538 png_read_rows(fPng_ptr, &srcRow, nullptr, 1); | 538 png_read_row(fPng_ptr, srcRow, nullptr); |
| 539 // FIXME: Only call IsOpaque once, outside the loop. Same for onGetS
canlines. | |
| 540 fSwizzler->swizzle(dstRow, srcRow); | 539 fSwizzler->swizzle(dstRow, srcRow); |
| 541 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 540 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 542 } | 541 } |
| 543 } | 542 } |
| 544 | 543 |
| 545 if (setjmp(png_jmpbuf(fPng_ptr))) { | |
| 546 // We've already read all the scanlines. This is a success. | |
| 547 return kSuccess; | |
| 548 } | |
| 549 | |
| 550 // read rest of file, and get additional comment and time chunks in info_ptr | 544 // read rest of file, and get additional comment and time chunks in info_ptr |
| 551 png_read_end(fPng_ptr, fInfo_ptr); | 545 png_read_end(fPng_ptr, fInfo_ptr); |
| 552 | 546 |
| 553 return kSuccess; | 547 return kSuccess; |
| 554 } | 548 } |
| 555 | 549 |
| 556 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { | 550 uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { |
| 557 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 551 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 558 if (colorPtr) { | 552 if (colorPtr) { |
| 559 return get_color_table_fill_value(colorType, colorPtr, 0); | 553 return get_color_table_fill_value(colorType, colorPtr, 0); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 int onGetScanlines(void* dst, int count, size_t rowBytes) override { | 585 int onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 592 // Assume that an error in libpng indicates an incomplete input. | 586 // Assume that an error in libpng indicates an incomplete input. |
| 593 int row = 0; | 587 int row = 0; |
| 594 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 588 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 595 SkCodecPrintf("setjmp long jump!\n"); | 589 SkCodecPrintf("setjmp long jump!\n"); |
| 596 return row; | 590 return row; |
| 597 } | 591 } |
| 598 | 592 |
| 599 void* dstRow = dst; | 593 void* dstRow = dst; |
| 600 for (; row < count; row++) { | 594 for (; row < count; row++) { |
| 601 png_read_rows(this->png_ptr(), &fSrcRow, nullptr, 1); | 595 png_read_row(this->png_ptr(), fSrcRow, nullptr); |
| 602 this->swizzler()->swizzle(dstRow, fSrcRow); | 596 this->swizzler()->swizzle(dstRow, fSrcRow); |
| 603 dstRow = SkTAddOffset<void>(dstRow, rowBytes); | 597 dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
| 604 } | 598 } |
| 605 | 599 |
| 606 return row; | 600 return row; |
| 607 } | 601 } |
| 608 | 602 |
| 609 bool onSkipScanlines(int count) override { | 603 bool onSkipScanlines(int count) override { |
| 610 // Assume that an error in libpng indicates an incomplete input. | 604 // Assume that an error in libpng indicates an incomplete input. |
| 611 if (setjmp(png_jmpbuf(this->png_ptr()))) { | 605 if (setjmp(png_jmpbuf(this->png_ptr()))) { |
| 612 SkCodecPrintf("setjmp long jump!\n"); | 606 SkCodecPrintf("setjmp long jump!\n"); |
| 613 return false; | 607 return false; |
| 614 } | 608 } |
| 615 //there is a potential tradeoff of memory vs speed created by putting th
is in a loop. | 609 |
| 616 //calling png_read_rows in a loop is insignificantly slower than calling
it once with count | |
| 617 //as png_read_rows has it's own loop which calls png_read_row count time
s. | |
| 618 for (int row = 0; row < count; row++) { | 610 for (int row = 0; row < count; row++) { |
| 619 png_read_rows(this->png_ptr(), &fSrcRow, nullptr, 1); | 611 png_read_row(this->png_ptr(), fSrcRow, nullptr); |
| 620 } | 612 } |
| 621 return true; | 613 return true; |
| 622 } | 614 } |
| 623 | 615 |
| 624 private: | 616 private: |
| 625 SkAutoTMalloc<uint8_t> fStorage; | 617 SkAutoTMalloc<uint8_t> fStorage; |
| 626 uint8_t* fSrcRow; | 618 uint8_t* fSrcRow; |
| 627 | 619 |
| 628 typedef SkPngCodec INHERITED; | 620 typedef SkPngCodec INHERITED; |
| 629 }; | 621 }; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 693 // fail on the first pass, we can still report than some scanlines a
re initialized. | 685 // fail on the first pass, we can still report than some scanlines a
re initialized. |
| 694 return 0; | 686 return 0; |
| 695 } | 687 } |
| 696 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); | 688 SkAutoTMalloc<uint8_t> storage(count * fSrcRowBytes); |
| 697 uint8_t* storagePtr = storage.get(); | 689 uint8_t* storagePtr = storage.get(); |
| 698 uint8_t* srcRow; | 690 uint8_t* srcRow; |
| 699 const int startRow = this->nextScanline(); | 691 const int startRow = this->nextScanline(); |
| 700 for (int i = 0; i < this->numberPasses(); i++) { | 692 for (int i = 0; i < this->numberPasses(); i++) { |
| 701 // read rows we planned to skip into garbage row | 693 // read rows we planned to skip into garbage row |
| 702 for (int y = 0; y < startRow; y++){ | 694 for (int y = 0; y < startRow; y++){ |
| 703 png_read_rows(this->png_ptr(), &fGarbageRowPtr, nullptr, 1); | 695 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); |
| 704 } | 696 } |
| 705 // read rows we care about into buffer | 697 // read rows we care about into buffer |
| 706 srcRow = storagePtr; | 698 srcRow = storagePtr; |
| 707 for (int y = 0; y < count; y++) { | 699 for (int y = 0; y < count; y++) { |
| 708 png_read_rows(this->png_ptr(), &srcRow, nullptr, 1); | 700 png_read_row(this->png_ptr(), srcRow, nullptr); |
| 709 srcRow += fSrcRowBytes; | 701 srcRow += fSrcRowBytes; |
| 710 } | 702 } |
| 711 // read rows we don't want into garbage buffer | 703 // read rows we don't want into garbage buffer |
| 712 for (int y = 0; y < fHeight - startRow - count; y++) { | 704 for (int y = 0; y < fHeight - startRow - count; y++) { |
| 713 png_read_rows(this->png_ptr(), &fGarbageRowPtr, nullptr, 1); | 705 png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); |
| 714 } | 706 } |
| 715 } | 707 } |
| 716 //swizzle the rows we care about | 708 //swizzle the rows we care about |
| 717 srcRow = storagePtr; | 709 srcRow = storagePtr; |
| 718 void* dstRow = dst; | 710 void* dstRow = dst; |
| 719 for (int y = 0; y < count; y++) { | 711 for (int y = 0; y < count; y++) { |
| 720 this->swizzler()->swizzle(dstRow, srcRow); | 712 this->swizzler()->swizzle(dstRow, srcRow); |
| 721 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 713 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 722 srcRow += fSrcRowBytes; | 714 srcRow += fSrcRowBytes; |
| 723 } | 715 } |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 766 } | 758 } |
| 767 | 759 |
| 768 if (1 == numberPasses) { | 760 if (1 == numberPasses) { |
| 769 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk
Reader, | 761 return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunk
Reader, |
| 770 png_ptr, info_ptr, bitDepth); | 762 png_ptr, info_ptr, bitDepth); |
| 771 } | 763 } |
| 772 | 764 |
| 773 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(),
chunkReader, | 765 return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(),
chunkReader, |
| 774 png_ptr, info_ptr, bitDepth, numbe
rPasses); | 766 png_ptr, info_ptr, bitDepth, numbe
rPasses); |
| 775 } | 767 } |
| OLD | NEW |