Chromium Code Reviews| 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 "SkCodec_libgif.h" | 8 #include "SkCodec_libgif.h" |
| 9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
| 10 #include "SkColorPriv.h" | 10 #include "SkColorPriv.h" |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 // There should only be one graphics control extension for the image frame | 89 // There should only be one graphics control extension for the image frame |
| 90 break; | 90 break; |
| 91 } | 91 } |
| 92 } | 92 } |
| 93 | 93 |
| 94 // Use maximum unsigned int (surely an invalid index) to indicate that a val id | 94 // Use maximum unsigned int (surely an invalid index) to indicate that a val id |
| 95 // index was not found. | 95 // index was not found. |
| 96 return SK_MaxU32; | 96 return SK_MaxU32; |
| 97 } | 97 } |
| 98 | 98 |
| 99 static inline uint32_t ceil_div(uint32_t a, uint32_t b) { | |
| 100 return (a + b - 1) / b; | |
| 101 } | |
| 102 | |
| 103 /* | |
| 104 * Gets the output row corresponding to the encoded row for interlaced gifs | |
| 105 */ | |
| 106 static uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) { | |
| 107 SkASSERT(encodedRow < height); | |
| 108 // First pass | |
| 109 if (encodedRow * 8 < height) { | |
| 110 return encodedRow * 8; | |
| 111 } | |
| 112 // Second pass | |
| 113 if (encodedRow * 4 < height) { | |
| 114 return 4 + 8 * (encodedRow - ceil_div(height, 8)); | |
| 115 } | |
| 116 // Third pass | |
| 117 if (encodedRow * 2 < height) { | |
| 118 return 2 + 4 * (encodedRow - ceil_div(height, 4)); | |
| 119 } | |
| 120 // Fourth pass | |
| 121 return 1 + 2 * (encodedRow - ceil_div(height, 2)); | |
| 122 } | |
| 123 | |
| 124 /* | 99 /* |
| 125 * This function cleans up the gif object after the decode completes | 100 * This function cleans up the gif object after the decode completes |
| 126 * It is used in a SkAutoTCallIProc template | 101 * It is used in a SkAutoTCallIProc template |
| 127 */ | 102 */ |
| 128 void SkGifCodec::CloseGif(GifFileType* gif) { | 103 void SkGifCodec::CloseGif(GifFileType* gif) { |
| 129 DGifCloseFile(gif, NULL); | 104 DGifCloseFile(gif, NULL); |
| 130 } | 105 } |
| 131 | 106 |
| 132 /* | 107 /* |
| 133 * This function free extension data that has been saved to assist the image | 108 * This function free extension data that has been saved to assist the image |
| (...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 ZeroInitialized zeroInit) { | 465 ZeroInitialized zeroInit) { |
| 491 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 466 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 492 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, | 467 fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, |
| 493 colorPtr, dstInfo, zeroInit, this->getInfo())); | 468 colorPtr, dstInfo, zeroInit, this->getInfo())); |
| 494 if (nullptr != fSwizzler.get()) { | 469 if (nullptr != fSwizzler.get()) { |
| 495 return kSuccess; | 470 return kSuccess; |
| 496 } | 471 } |
| 497 return kUnimplemented; | 472 return kUnimplemented; |
| 498 } | 473 } |
| 499 | 474 |
| 500 SkCodec::Result SkGifCodec::readRow() { | 475 bool SkGifCodec::readRow() { |
| 501 if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) { | 476 if (GIF_ERROR == DGifGetLine(fGif, fSrcBuffer.get(), fFrameDims.width())) { |
|
scroggo
2015/09/25 15:55:06
nit: This can be one line:
return GIF_ERROR != DG
msarett
2015/10/01 12:44:52
Done.
| |
| 502 return kIncompleteInput; | 477 return false; |
| 503 } | 478 } |
| 504 return kSuccess; | 479 return true; |
| 505 } | 480 } |
| 506 | 481 |
| 507 /* | 482 /* |
| 508 * Initiates the gif decode | 483 * Initiates the gif decode |
| 509 */ | 484 */ |
| 510 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, | 485 SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, |
| 511 void* dst, size_t dstRowBytes, | 486 void* dst, size_t dstRowBytes, |
| 512 const Options& opts, | 487 const Options& opts, |
| 513 SkPMColor* inputColorPtr, | 488 SkPMColor* inputColorPtr, |
| 514 int* inputColorCount) { | 489 int* inputColorCount, |
| 490 int* incompleteScanlines) { | |
| 515 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCoun t, opts); | 491 Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCoun t, opts); |
| 516 if (kSuccess != result) { | 492 if (kSuccess != result) { |
| 517 return result; | 493 return result; |
| 518 } | 494 } |
| 519 | 495 |
| 520 if (dstInfo.dimensions() != this->getInfo().dimensions()) { | 496 if (dstInfo.dimensions() != this->getInfo().dimensions()) { |
| 521 return gif_error("Scaling not supported.\n", kInvalidScale); | 497 return gif_error("Scaling not supported.\n", kInvalidScale); |
| 522 } | 498 } |
| 523 | 499 |
| 524 // Initialize the swizzler | 500 // Initialize the swizzler |
| 525 if (fFrameIsSubset) { | 501 if (fFrameIsSubset) { |
| 526 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFr ameDims.height()); | 502 const SkImageInfo subsetDstInfo = dstInfo.makeWH(fFrameDims.width(), fFr ameDims.height()); |
| 527 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitia lized)) { | 503 if (kSuccess != this->initializeSwizzler(subsetDstInfo, opts.fZeroInitia lized)) { |
| 528 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; | 504 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; |
| 529 } | 505 } |
| 530 | 506 |
| 531 // Fill the background | 507 // Fill the background |
| 532 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 508 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); |
| 533 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getInfo().height(), | 509 SkSwizzler::Fill(dst, dstInfo, dstRowBytes, this->getFillValue(dstInfo), |
| 534 fFillIndex, colorPtr, opts.fZeroInitialized); | 510 opts.fZeroInitialized); |
| 535 | 511 |
| 536 // Modify the dst pointer | 512 // Modify the dst pointer |
| 537 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorT ype()); | 513 const int32_t dstBytesPerPixel = SkColorTypeBytesPerPixel(dstInfo.colorT ype()); |
| 538 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() + | 514 dst = SkTAddOffset<void*>(dst, dstRowBytes * fFrameDims.top() + |
| 539 dstBytesPerPixel * fFrameDims.left()); | 515 dstBytesPerPixel * fFrameDims.left()); |
| 540 } else { | 516 } else { |
| 541 if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized) ) { | 517 if (kSuccess != this->initializeSwizzler(dstInfo, opts.fZeroInitialized) ) { |
| 542 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; | 518 return gif_error("Could not initialize swizzler.\n", kUnimplemented) ; |
| 543 } | 519 } |
| 544 } | 520 } |
| 545 | 521 |
| 546 // Check the interlace flag and iterate over rows of the input | 522 // Check the interlace flag and iterate over rows of the input |
| 547 uint32_t width = fFrameDims.width(); | 523 uint32_t width = fFrameDims.width(); |
| 548 uint32_t height = fFrameDims.height(); | 524 uint32_t height = fFrameDims.height(); |
| 549 if (fGif->Image.Interlace) { | 525 if (fGif->Image.Interlace) { |
| 550 // In interlace mode, the rows of input are rearranged in | 526 // In interlace mode, the rows of input are rearranged in |
| 551 // the output image. We a helper function to help us | 527 // the output image. We a helper function to help us |
| 552 // rearrange the decoded rows. | 528 // rearrange the decoded rows. |
| 553 for (uint32_t y = 0; y < height; y++) { | 529 for (uint32_t y = 0; y < height; y++) { |
| 554 if (kSuccess != this->readRow()) { | 530 if (!this->readRow()) { |
| 555 // Recover from error by filling remainder of image | 531 // Incomplete interlaced gifs are a unique case. Rather than, |
| 556 memset(fSrcBuffer.get(), fFillIndex, width); | 532 // relying on the base class, we will fill the remaining lines |
| 533 // here. | |
| 534 const SkImageInfo fillInfo = dstInfo.makeWH(dstInfo.width(), 1); | |
| 535 const uint32_t fillValue = this->getFillValue(fillInfo); | |
| 557 for (; y < height; y++) { | 536 for (; y < height; y++) { |
| 558 void* dstRow = SkTAddOffset<void>(dst, | 537 void* dstRow = SkTAddOffset<void>(dst, |
| 559 dstRowBytes * get_output_row_interlaced(y, height)); | 538 dstRowBytes * get_output_row_interlaced(y, height)); |
| 560 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | 539 SkSwizzler::Fill(dstRow, fillInfo, dstRowBytes, fillValue, |
| 540 opts.fZeroInitialized); | |
| 561 } | 541 } |
| 562 return gif_error("Could not decode line.\n", kIncompleteInput); | 542 return gif_error("Could not decode line.\n", kIncompleteInput); |
| 563 } | 543 } |
| 564 void* dstRow = SkTAddOffset<void>(dst, | 544 void* dstRow = SkTAddOffset<void>(dst, |
| 565 dstRowBytes * get_output_row_interlaced(y, height)); | 545 dstRowBytes * get_output_row_interlaced(y, height)); |
| 566 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | 546 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); |
| 567 } | 547 } |
| 568 } else { | 548 } else { |
| 569 // Standard mode | 549 // Standard mode |
| 570 void* dstRow = dst; | 550 void* dstRow = dst; |
| 571 for (uint32_t y = 0; y < height; y++) { | 551 for (uint32_t y = 0; y < height; y++) { |
| 572 if (kSuccess != this->readRow()) { | 552 if (!this->readRow()) { |
| 573 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | 553 *incompleteScanlines = height - y; |
| 574 SkSwizzler::Fill(dstRow, dstInfo, dstRowBytes, | |
| 575 height - y, fFillIndex, colorPtr, opts.fZeroInitialized) ; | |
| 576 return gif_error("Could not decode line\n", kIncompleteInput); | 554 return gif_error("Could not decode line\n", kIncompleteInput); |
| 577 } | 555 } |
| 578 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); | 556 fSwizzler->swizzle(dstRow, fSrcBuffer.get()); |
| 579 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); | 557 dstRow = SkTAddOffset<void>(dstRow, dstRowBytes); |
| 580 } | 558 } |
| 581 } | 559 } |
| 582 return kSuccess; | 560 return kSuccess; |
| 583 } | 561 } |
| 584 | 562 |
| 563 uint32_t SkGifCodec::onGetFillValue(const SkImageInfo& dstInfo) const { | |
| 564 const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); | |
| 565 return get_color_table_fill_value(dstInfo.colorType(), colorPtr, fFillIndex) ; | |
| 566 } | |
| 567 | |
| 585 // TODO (msarett): skbug.com/3582 | 568 // TODO (msarett): skbug.com/3582 |
| 586 // Should we implement reallyHasAlpha? Or should we read extens ion blocks in the | 569 // Should we implement reallyHasAlpha? Or should we read extens ion blocks in the |
| 587 // header? Or should we do both? | 570 // header? Or should we do both? |
| 588 | 571 |
| 589 class SkGifScanlineDecoder : public SkScanlineDecoder { | 572 class SkGifScanlineDecoder : public SkScanlineDecoder { |
| 590 public: | 573 public: |
| 591 SkGifScanlineDecoder(const SkImageInfo& srcInfo, SkGifCodec* codec) | 574 SkGifScanlineDecoder(SkGifCodec* codec) |
| 592 : INHERITED(srcInfo) | 575 : INHERITED(codec, codec->getInfo()) |
| 593 , fCodec(codec) | 576 , fCodec(codec) |
| 594 {} | 577 {} |
| 595 | 578 |
| 596 SkEncodedFormat onGetEncodedFormat() const override { | 579 SkEncodedFormat onGetEncodedFormat() const override { |
| 597 return kGIF_SkEncodedFormat; | 580 return kGIF_SkEncodedFormat; |
| 598 } | 581 } |
| 599 | 582 |
| 600 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts, | 583 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& opts, |
| 601 SkPMColor inputColorPtr[], int* inputColorCount) ove rride { | 584 SkPMColor inputColorPtr[], int* inputColorCount) ove rride { |
| 602 SkCodec::Result result = fCodec->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, | 585 SkCodec::Result result = fCodec->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 625 } | 608 } |
| 626 } else { | 609 } else { |
| 627 if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZ eroInitialized)) { | 610 if (SkCodec::kSuccess != fCodec->initializeSwizzler(dstInfo, opts.fZ eroInitialized)) { |
| 628 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented); | 611 return gif_error("Could not initialize swizzler.\n", SkCodec::kU nimplemented); |
| 629 } | 612 } |
| 630 } | 613 } |
| 631 | 614 |
| 632 return SkCodec::kSuccess; | 615 return SkCodec::kSuccess; |
| 633 } | 616 } |
| 634 | 617 |
| 635 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de { | 618 uint32_t onGetScanlines(void* dst, int count, size_t rowBytes) override { |
| 636 if (fCodec->fFrameIsSubset) { | 619 if (fCodec->fFrameIsSubset) { |
| 637 // Fill the requested rows | 620 // Fill the requested rows |
| 638 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.get()) ; | 621 SkSwizzler::Fill(dst, this->dstInfo().makeWH(this->dstInfo().width() , count), rowBytes, |
| 639 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count, fCodec->fFil lIndex, | 622 fCodec->onGetFillValue(dstInfo()), this->options().fZeroInit ialized); |
| 640 colorPtr, this->options().fZeroInitialized); | |
| 641 | 623 |
| 642 // Do nothing for rows before the image frame | 624 // Do nothing for rows before the image frame |
| 643 int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY(); | 625 int rowsBeforeFrame = fCodec->fFrameDims.top() - INHERITED::getY(); |
| 644 if (rowsBeforeFrame > 0) { | 626 if (rowsBeforeFrame > 0) { |
| 645 count = SkTMin(0, count - rowsBeforeFrame); | 627 count = SkTMin(0, count - rowsBeforeFrame); |
| 646 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); | 628 dst = SkTAddOffset<void>(dst, rowBytes * rowsBeforeFrame); |
| 647 } | 629 } |
| 648 | 630 |
| 649 // Do nothing for rows after the image frame | 631 // Do nothing for rows after the image frame |
| 650 int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims. bottom(); | 632 int rowsAfterFrame = INHERITED::getY() + count - fCodec->fFrameDims. bottom(); |
| 651 if (rowsAfterFrame > 0) { | 633 if (rowsAfterFrame > 0) { |
| 652 count = SkTMin(0, count - rowsAfterFrame); | 634 count = SkTMin(0, count - rowsAfterFrame); |
| 653 } | 635 } |
| 654 | 636 |
| 655 // Adjust dst pointer for left offset | 637 // Adjust dst pointer for left offset |
| 656 dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel( | 638 dst = SkTAddOffset<void>(dst, SkColorTypeBytesPerPixel( |
| 657 this->dstInfo().colorType()) * fCodec->fFrameDims.left()); | 639 this->dstInfo().colorType()) * fCodec->fFrameDims.left()); |
| 658 } | 640 } |
| 659 | 641 |
| 642 void* dstRow = dst; | |
| 660 for (int i = 0; i < count; i++) { | 643 for (int i = 0; i < count; i++) { |
| 661 if (SkCodec::kSuccess != fCodec->readRow()) { | 644 if (!fCodec->readRow()) { |
| 662 const SkPMColor* colorPtr = get_color_ptr(fCodec->fColorTable.ge t()); | 645 return i; |
| 663 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, | |
| 664 count - i, fCodec->fFillIndex, colorPtr, | |
| 665 this->options().fZeroInitialized); | |
| 666 return gif_error("Could not decode line\n", SkCodec::kIncomplete Input); | |
| 667 } | 646 } |
| 668 fCodec->fSwizzler->swizzle(dst, fCodec->fSrcBuffer.get()); | 647 fCodec->fSwizzler->swizzle(dstRow, fCodec->fSrcBuffer.get()); |
| 669 dst = SkTAddOffset<void>(dst, rowBytes); | 648 dstRow = SkTAddOffset<void>(dstRow, rowBytes); |
| 670 } | 649 } |
| 671 return SkCodec::kSuccess; | 650 return count; |
| 672 } | 651 } |
| 673 | 652 |
| 674 SkScanlineOrder onGetScanlineOrder() const override { | 653 SkScanlineOrder onGetScanlineOrder() const override { |
| 675 if (fCodec->fGif->Image.Interlace) { | 654 if (fCodec->fGif->Image.Interlace) { |
| 676 return kOutOfOrder_SkScanlineOrder; | 655 return kOutOfOrder_SkScanlineOrder; |
| 677 } else { | 656 } else { |
| 678 return kTopDown_SkScanlineOrder; | 657 return kTopDown_SkScanlineOrder; |
| 679 } | 658 } |
| 680 } | 659 } |
| 681 | 660 |
| 682 int onGetY() const override { | |
| 683 if (fCodec->fGif->Image.Interlace) { | |
| 684 return get_output_row_interlaced(INHERITED::onGetY(), this->dstInfo( ).height()); | |
| 685 } else { | |
| 686 return INHERITED::onGetY(); | |
| 687 } | |
| 688 } | |
| 689 | |
| 690 private: | 661 private: |
| 691 SkAutoTDelete<SkGifCodec> fCodec; | 662 SkGifCodec* fCodec; // Owned by parent class |
| 692 | 663 |
| 693 typedef SkScanlineDecoder INHERITED; | 664 typedef SkScanlineDecoder INHERITED; |
| 694 }; | 665 }; |
| 695 | 666 |
| 696 SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) { | 667 SkScanlineDecoder* SkGifCodec::NewSDFromStream(SkStream* stream) { |
| 697 SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFro mStream(stream))); | 668 SkAutoTDelete<SkGifCodec> codec (static_cast<SkGifCodec*>(SkGifCodec::NewFro mStream(stream))); |
| 698 if (!codec) { | 669 if (!codec) { |
| 699 return NULL; | 670 return NULL; |
| 700 } | 671 } |
| 701 | 672 |
| 702 const SkImageInfo& srcInfo = codec->getInfo(); | 673 return SkNEW_ARGS(SkGifScanlineDecoder, (codec.detach())); |
| 703 | |
| 704 return SkNEW_ARGS(SkGifScanlineDecoder, (srcInfo, codec.detach())); | |
| 705 } | 674 } |
| OLD | NEW |