| Index: third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
|
| diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
|
| index 0b76d3326f24edabb38900dfff72e8440d417c42..3ace861ae0532bf93449db3ea41afefe1c730211 100644
|
| --- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
|
| +++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
|
| @@ -23,88 +23,25 @@
|
| #include <memory>
|
| #include "platform/RuntimeEnabledFeatures.h"
|
| #include "platform/graphics/BitmapImageMetrics.h"
|
| -#include "platform/image-decoders/FastSharedBufferReader.h"
|
| -#include "platform/image-decoders/bmp/BMPImageDecoder.h"
|
| -#include "platform/image-decoders/gif/GIFImageDecoder.h"
|
| -#include "platform/image-decoders/ico/ICOImageDecoder.h"
|
| -#include "platform/image-decoders/jpeg/JPEGImageDecoder.h"
|
| -#include "platform/image-decoders/png/PNGImageDecoder.h"
|
| -#include "platform/image-decoders/webp/WEBPImageDecoder.h"
|
| #include "platform/instrumentation/PlatformInstrumentation.h"
|
| #include "platform/wtf/PtrUtil.h"
|
| +#include "third_party/skia/include/core/SkImageInfo.h"
|
|
|
| namespace blink {
|
|
|
| -inline bool MatchesJPEGSignature(const char* contents) {
|
| - return !memcmp(contents, "\xFF\xD8\xFF", 3);
|
| -}
|
| -
|
| -inline bool MatchesPNGSignature(const char* contents) {
|
| - return !memcmp(contents, "\x89PNG\r\n\x1A\n", 8);
|
| -}
|
| -
|
| -inline bool MatchesGIFSignature(const char* contents) {
|
| - return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
|
| -}
|
| -
|
| -inline bool MatchesWebPSignature(const char* contents) {
|
| - return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
|
| -}
|
| -
|
| -inline bool MatchesICOSignature(const char* contents) {
|
| - return !memcmp(contents, "\x00\x00\x01\x00", 4);
|
| -}
|
| -
|
| -inline bool MatchesCURSignature(const char* contents) {
|
| - return !memcmp(contents, "\x00\x00\x02\x00", 4);
|
| -}
|
| -
|
| -inline bool MatchesBMPSignature(const char* contents) {
|
| - return !memcmp(contents, "BM", 2);
|
| -}
|
| -
|
| -static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
|
| -
|
| std::unique_ptr<ImageDecoder> ImageDecoder::Create(
|
| RefPtr<SegmentReader> data,
|
| bool data_complete,
|
| AlphaOption alpha_option,
|
| const ColorBehavior& color_behavior) {
|
| - // At least kLongestSignatureLength bytes are needed to sniff the signature.
|
| - if (data->size() < kLongestSignatureLength)
|
| - return nullptr;
|
| -
|
| const size_t max_decoded_bytes =
|
| Platform::Current() ? Platform::Current()->MaxDecodedImageBytes()
|
| : kNoDecodedImageByteLimit;
|
|
|
| - // Access the first kLongestSignatureLength chars to sniff the signature.
|
| - // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
|
| - char buffer[kLongestSignatureLength];
|
| - const FastSharedBufferReader fast_reader(data);
|
| - const char* contents =
|
| - fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
|
| -
|
| std::unique_ptr<ImageDecoder> decoder;
|
| - if (MatchesJPEGSignature(contents)) {
|
| - decoder.reset(
|
| - new JPEGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - } else if (MatchesPNGSignature(contents)) {
|
| - decoder.reset(
|
| - new PNGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - } else if (MatchesGIFSignature(contents)) {
|
| - decoder.reset(
|
| - new GIFImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - } else if (MatchesWebPSignature(contents)) {
|
| - decoder.reset(
|
| - new WEBPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - } else if (MatchesICOSignature(contents) || MatchesCURSignature(contents)) {
|
| - decoder.reset(
|
| - new ICOImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - } else if (MatchesBMPSignature(contents)) {
|
| - decoder.reset(
|
| - new BMPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
| - }
|
| +
|
| + decoder.reset(
|
| + new ImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
|
|
|
| if (decoder)
|
| decoder->SetData(std::move(data), data_complete);
|
| @@ -112,13 +49,31 @@ std::unique_ptr<ImageDecoder> ImageDecoder::Create(
|
| return decoder;
|
| }
|
|
|
| -bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) {
|
| - return data.size() >= kLongestSignatureLength;
|
| +ImageDecoder::ImageDecoder(AlphaOption alpha_option,
|
| + const ColorBehavior& color_behavior,
|
| + size_t max_decoded_bytes)
|
| + : premultiply_alpha_(alpha_option == kAlphaPremultiplied),
|
| + color_behavior_(color_behavior),
|
| + max_decoded_bytes_(max_decoded_bytes),
|
| + purge_aggressively_(false),
|
| + codec_() {}
|
| +void ImageDecoder::OnSetData(SegmentReader* data) {
|
| + if (data) {
|
| + if (!codec_) {
|
| + SkCodec* codec = SkCodec::NewFromData(data->GetAsSkData());
|
| + if (codec)
|
| + codec_.reset(codec);
|
| + else
|
| + return;
|
| + }
|
| + SkImageInfo image_info = codec_->getInfo();
|
| + SetSize(image_info.width(), image_info.height());
|
| + }
|
| }
|
|
|
| size_t ImageDecoder::FrameCount() {
|
| const size_t old_size = frame_buffer_cache_.size();
|
| - const size_t new_size = DecodeFrameCount();
|
| + const size_t new_size = codec_ ? codec_->getFrameCount() : 0;
|
| if (old_size != new_size) {
|
| frame_buffer_cache_.resize(new_size);
|
| for (size_t i = old_size; i < new_size; ++i) {
|
| @@ -129,6 +84,70 @@ size_t ImageDecoder::FrameCount() {
|
| return new_size;
|
| }
|
|
|
| +void ImageDecoder::Decode(size_t index) {
|
| + if (!codec_)
|
| + return;
|
| +
|
| + DCHECK(!Failed());
|
| + DCHECK_LT(index, frame_buffer_cache_.size());
|
| +
|
| + UpdateAggressivePurging(index);
|
| + SkImageInfo imageInfo = codec_->getInfo()
|
| + .makeColorType(kN32_SkColorType)
|
| + .makeColorSpace(ColorSpaceForSkImages());
|
| +
|
| + SkCodec::Options options;
|
| + options.fFrameIndex = index;
|
| + options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
|
| +
|
| + ImageFrame& frame = frame_buffer_cache_[index];
|
| + if (frame.GetStatus() == ImageFrame::kFrameEmpty) {
|
| + size_t required_previous_frame_index = frame.RequiredPreviousFrameIndex();
|
| + if (required_previous_frame_index == WTF::kNotFound) {
|
| + frame.AllocatePixelData(Size().Width(), Size().Height(),
|
| + ColorSpaceForSkImages());
|
| + } else {
|
| + ImageFrame& required_previous_frame =
|
| + frame_buffer_cache_[required_previous_frame_index];
|
| +
|
| + if (required_previous_frame.GetStatus() != ImageFrame::kFrameComplete)
|
| + Decode(required_previous_frame_index);
|
| +
|
| + // We try to reuse |required_previous_frame| as starting state to avoid
|
| + // copying. If CanReusePreviousFrameBuffer returns false, we must copy
|
| + // the data since |required_previous_frame| is necessary to decode this
|
| + // or later frames. In that case copy the data instead.
|
| + if ((!CanReusePreviousFrameBuffer(index) ||
|
| + !frame.TakeBitmapDataIfWritable(&required_previous_frame)) &&
|
| + !frame.CopyBitmapData(required_previous_frame)) {
|
| + SetFailed();
|
| + return;
|
| + }
|
| + options.fHasPriorFrame = true;
|
| + }
|
| + }
|
| +
|
| + SkCodec::Result result =
|
| + codec_->getPixels(imageInfo, frame.Bitmap().getPixels(),
|
| + frame.Bitmap().rowBytes(), &options, nullptr, nullptr);
|
| + switch (result) {
|
| + case SkCodec::kSuccess:
|
| + frame.SetPixelsChanged(true);
|
| + frame.SetStatus(ImageFrame::kFrameComplete);
|
| + PostDecodeProcessing(index);
|
| + break;
|
| + default:
|
| + SetFailed();
|
| + return;
|
| + }
|
| +}
|
| +
|
| +bool ImageDecoder::SetFailed() {
|
| + codec_ = nullptr;
|
| + failed_ = true;
|
| + return false;
|
| +}
|
| +
|
| ImageFrame* ImageDecoder::FrameBufferAtIndex(size_t index) {
|
| if (index >= FrameCount())
|
| return 0;
|
| @@ -155,9 +174,22 @@ bool ImageDecoder::FrameHasAlphaAtIndex(size_t index) const {
|
| }
|
|
|
| bool ImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
|
| - // Animated images override this method to return the status based on the data
|
| - // received for the queried frame.
|
| - return IsAllDataReceived();
|
| + if (!codec_)
|
| + return false;
|
| +
|
| + if (codec_->getFrameCount() <= (int)index)
|
| + return false;
|
| +
|
| + if (frame_buffer_cache_.IsEmpty())
|
| + return false;
|
| +
|
| + return frame_buffer_cache_[0].GetStatus() == ImageFrame::kFrameComplete;
|
| +}
|
| +
|
| +float ImageDecoder::FrameDurationAtIndex(size_t index) const {
|
| + if (index < frame_buffer_cache_.size())
|
| + return frame_buffer_cache_[index].Duration();
|
| + return 0;
|
| }
|
|
|
| bool ImageDecoder::FrameIsDecodedAtIndex(size_t index) const {
|
| @@ -357,14 +389,61 @@ bool ImageDecoder::InitFrameBuffer(size_t frame_index) {
|
| }
|
| }
|
|
|
| - OnInitFrameBuffer(frame_index);
|
| -
|
| // Update our status to be partially complete.
|
| buffer->SetStatus(ImageFrame::kFramePartial);
|
|
|
| return true;
|
| }
|
|
|
| +int ImageDecoder::RepetitionCount() {
|
| + if (!codec_)
|
| + return kAnimationLoopOnce;
|
| +
|
| + DCHECK(!Failed());
|
| +
|
| + // packets sent back by the webserver) not always.
|
| + // SkCodec will parse forward in the file if the repetition count has not been
|
| + // seen yet.
|
| + int repetition_count = codec_->getRepetitionCount();
|
| + switch (repetition_count) {
|
| + case 0: {
|
| + size_t frame_count = codec_->getFrameCount();
|
| + if (IsAllDataReceived() && frame_count == 1)
|
| + return kAnimationNone;
|
| + return kAnimationLoopOnce;
|
| + }
|
| + case SkCodec::kRepetitionCountInfinite:
|
| + return kAnimationLoopInfinite;
|
| + default:
|
| + return repetition_count;
|
| + }
|
| +}
|
| +
|
| +bool ImageDecoder::CanReusePreviousFrameBuffer(size_t index) const {
|
| + DCHECK(index < frame_buffer_cache_.size());
|
| +
|
| + // If the current frame and the next frame depend on the same frame, we cannot
|
| + // reuse the old frame. We must preserve it for the next frame.
|
| + //
|
| + // However, if the current and next frame depend on different frames then we
|
| + // know the current frame is the last one to use the frame it depends on. That
|
| + // means the current frame can reuse the previous frame buffer.
|
| + //
|
| + // If we do not have information about the next frame yet, we cannot assume it
|
| + // is safe to reuse the previous frame buffer.
|
| +
|
| + if (index + 1 >= frame_buffer_cache_.size())
|
| + return false;
|
| +
|
| + const ImageFrame& frame = frame_buffer_cache_[index];
|
| + size_t required_frame_index = frame.RequiredPreviousFrameIndex();
|
| +
|
| + const ImageFrame& next_frame = frame_buffer_cache_[index + 1];
|
| + size_t next_required_frame_index = next_frame.RequiredPreviousFrameIndex();
|
| +
|
| + return required_frame_index != next_required_frame_index;
|
| +}
|
| +
|
| void ImageDecoder::UpdateAggressivePurging(size_t index) {
|
| if (purge_aggressively_)
|
| return;
|
| @@ -455,6 +534,36 @@ size_t ImageDecoder::FindRequiredPreviousFrame(size_t frame_index,
|
| }
|
| }
|
|
|
| +void ImageDecoder::InitializeNewFrame(size_t index) {
|
| + DCHECK(codec_);
|
| +
|
| + ImageFrame& frame = frame_buffer_cache_[index];
|
| + // SkCodec does not inform us if only a portion of the image was updated
|
| + // in the current frame. Because of this, rather than correctly filling in
|
| + // the frame rect, we set the frame rect to be the image's full size.
|
| + IntSize fullImageSize = Size();
|
| + frame.SetOriginalFrameRect(IntRect(IntPoint(), fullImageSize));
|
| +
|
| + SkCodec::FrameInfo frame_info;
|
| + memset(&frame_info, 0, sizeof(frame_info));
|
| + codec_->getFrameInfo(index, &frame_info);
|
| + frame.SetDuration(frame_info.fDuration);
|
| + frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType));
|
| + size_t required_previous_frame_index = WTF::kNotFound;
|
| +
|
| + if (frame_info.fRequiredFrame > 0) {
|
| + required_previous_frame_index =
|
| + static_cast<size_t>(frame_info.fRequiredFrame);
|
| + }
|
| +
|
| + frame.SetRequiredPreviousFrameIndex(required_previous_frame_index);
|
| +
|
| + // The disposal method is not required any more, but is left in place
|
| + // for the other image decoders that do not yet rely on SkCodec.
|
| + // For now, fill it with DisposeKeep.
|
| + frame.SetDisposalMethod(ImageFrame::kDisposeKeep);
|
| +}
|
| +
|
| ImagePlanes::ImagePlanes() {
|
| for (int i = 0; i < 3; ++i) {
|
| planes_[i] = 0;
|
|
|