blob: 29490055264a4b3d634a818c46621e233912b897 [file] [log] [blame]
/*
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder.h"
#include <limits>
#include "third_party/blink/renderer/platform/image-decoders/segment_stream.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
#include "third_party/skia/include/core/SkImageInfo.h"
namespace blink {
namespace {
ImageFrame::DisposalMethod ConvertDisposalMethod(
SkCodecAnimation::DisposalMethod disposal_method) {
switch (disposal_method) {
case SkCodecAnimation::DisposalMethod::kKeep:
return ImageFrame::kDisposeKeep;
case SkCodecAnimation::DisposalMethod::kRestoreBGColor:
return ImageFrame::kDisposeOverwriteBgcolor;
case SkCodecAnimation::DisposalMethod::kRestorePrevious:
return ImageFrame::kDisposeOverwritePrevious;
default:
return ImageFrame::kDisposeNotSpecified;
}
}
} // anonymous namespace
GIFImageDecoder::GIFImageDecoder(AlphaOption alpha_option,
const ColorBehavior& color_behavior,
size_t max_decoded_bytes)
: ImageDecoder(alpha_option,
ImageDecoder::kDefaultBitDepth,
color_behavior,
max_decoded_bytes) {}
GIFImageDecoder::~GIFImageDecoder() = default;
void GIFImageDecoder::OnSetData(SegmentReader* data) {
if (!data) {
if (segment_stream_)
segment_stream_->SetReader(nullptr);
return;
}
std::unique_ptr<SegmentStream> segment_stream;
if (!segment_stream_) {
segment_stream = std::make_unique<SegmentStream>();
segment_stream_ = segment_stream.get();
}
segment_stream_->SetReader(data);
if (!codec_) {
SkCodec::Result codec_creation_result;
codec_ = SkCodec::MakeFromStream(std::move(segment_stream),
&codec_creation_result, nullptr);
// SkCodec supports many codecs, but this class is only for GIF decoding.
if (codec_ && codec_->getEncodedFormat() != SkEncodedImageFormat::kGIF) {
SetFailed();
return;
}
switch (codec_creation_result) {
case SkCodec::kSuccess: {
// SkCodec::MakeFromStream will read enough of the image to get the
// image size.
SkImageInfo image_info = codec_->getInfo();
SetSize(static_cast<unsigned>(image_info.width()),
static_cast<unsigned>(image_info.height()));
return;
}
case SkCodec::kIncompleteInput:
if (IsAllDataReceived()) {
SetFailed();
return;
}
// |segment_stream_|'s ownership is passed into MakeFromStream.
// It is deleted if MakeFromStream fails.
// If MakeFromStream fails, we set |segment_stream_| to null so
// we aren't pointing to reclaimed memory.
segment_stream_ = nullptr;
return;
default:
SetFailed();
return;
}
}
}
int GIFImageDecoder::RepetitionCount() const {
if (!codec_ || segment_stream_->IsCleared())
return repetition_count_;
DCHECK(!Failed());
// This value can arrive at any point in the image data stream. Most GIFs
// in the wild declare it near the beginning of the file, so it usually is
// set by the time we've decoded the size, but (depending on the GIF and the
// 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: {
// SkCodec returns 0 for both still images and animated images which only
// play once.
if (IsAllDataReceived() && codec_->getFrameCount() == 1) {
repetition_count_ = kAnimationNone;
break;
}
repetition_count_ = kAnimationLoopOnce;
break;
}
case SkCodec::kRepetitionCountInfinite:
repetition_count_ = kAnimationLoopInfinite;
break;
default:
repetition_count_ = repetition_count;
break;
}
return repetition_count_;
}
bool GIFImageDecoder::FrameIsReceivedAtIndex(size_t index) const {
SkCodec::FrameInfo frame_info;
if (!codec_ || !codec_->getFrameInfo(index, &frame_info))
return false;
return frame_info.fFullyReceived;
}
base::TimeDelta GIFImageDecoder::FrameDurationAtIndex(size_t index) const {
if (index < frame_buffer_cache_.size())
return frame_buffer_cache_[index].Duration();
return base::TimeDelta();
}
bool GIFImageDecoder::SetFailed() {
segment_stream_ = nullptr;
codec_.reset();
return ImageDecoder::SetFailed();
}
size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) {
if (frame_buffer_cache_.size() <= 1)
return 0;
// SkCodec attempts to report the earliest possible required frame. But it is
// possible that frame has been evicted. A later frame which could also
// be used as the required frame may still be cached. Try to preserve a frame
// that is still cached.
size_t index2 = kNotFound;
if (index < frame_buffer_cache_.size()) {
const ImageFrame& frame = frame_buffer_cache_[index];
if (frame.RequiredPreviousFrameIndex() != kNotFound &&
(!FrameStatusSufficientForSuccessors(index) ||
frame.GetDisposalMethod() == ImageFrame::kDisposeOverwritePrevious)) {
index2 = GetViableReferenceFrameIndex(index);
}
}
return ClearCacheExceptTwoFrames(index, index2);
}
size_t GIFImageDecoder::DecodeFrameCount() {
if (!codec_ || segment_stream_->IsCleared())
return frame_buffer_cache_.size();
return codec_->getFrameCount();
}
void GIFImageDecoder::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.
// The original frame rect is not used, anyway.
IntSize full_image_size = Size();
frame.SetOriginalFrameRect(IntRect(IntPoint(), full_image_size));
SkCodec::FrameInfo frame_info;
bool frame_info_received = codec_->getFrameInfo(index, &frame_info);
DCHECK(frame_info_received);
frame.SetDuration(base::TimeDelta::FromMilliseconds(frame_info.fDuration));
size_t required_previous_frame_index;
if (frame_info.fRequiredFrame == SkCodec::kNoFrame) {
required_previous_frame_index = kNotFound;
} else {
required_previous_frame_index =
static_cast<size_t>(frame_info.fRequiredFrame);
}
frame.SetRequiredPreviousFrameIndex(required_previous_frame_index);
frame.SetDisposalMethod(ConvertDisposalMethod(frame_info.fDisposalMethod));
}
void GIFImageDecoder::Decode(size_t index) {
if (!codec_ || segment_stream_->IsCleared())
return;
DCHECK(!Failed());
DCHECK_LT(index, frame_buffer_cache_.size());
ImageFrame& frame = frame_buffer_cache_[index];
if (frame.GetStatus() == ImageFrame::kFrameComplete)
return;
UpdateAggressivePurging(index);
if (frame.GetStatus() == ImageFrame::kFrameEmpty) {
size_t required_previous_frame_index = frame.RequiredPreviousFrameIndex();
if (required_previous_frame_index == kNotFound) {
frame.AllocatePixelData(Size().Width(), Size().Height(),
ColorSpaceForSkImages());
frame.ZeroFillPixelData();
prior_frame_ = SkCodec::kNoFrame;
} else {
size_t previous_frame_index = GetViableReferenceFrameIndex(index);
if (previous_frame_index == kNotFound) {
previous_frame_index = required_previous_frame_index;
Decode(previous_frame_index);
if (Failed()) {
return;
}
}
// We try to reuse |previous_frame| as starting state to avoid copying.
// If CanReusePreviousFrameBuffer returns false, we must copy the data
// since |previous_frame| is necessary to decode this or later frames.
// In that case copy the data instead.
ImageFrame& previous_frame = frame_buffer_cache_[previous_frame_index];
if ((!CanReusePreviousFrameBuffer(index) ||
!frame.TakeBitmapDataIfWritable(&previous_frame)) &&
!frame.CopyBitmapData(previous_frame)) {
SetFailed();
return;
}
prior_frame_ = previous_frame_index;
}
}
if (frame.GetStatus() == ImageFrame::kFrameInitialized) {
SkCodec::FrameInfo frame_info;
bool frame_info_received = codec_->getFrameInfo(index, &frame_info);
DCHECK(frame_info_received);
SkAlphaType alpha_type = kOpaque_SkAlphaType;
if (frame_info.fAlphaType != kOpaque_SkAlphaType) {
if (premultiply_alpha_) {
alpha_type = kPremul_SkAlphaType;
} else {
alpha_type = kUnpremul_SkAlphaType;
}
}
SkImageInfo image_info = codec_->getInfo()
.makeColorType(kN32_SkColorType)
.makeColorSpace(ColorSpaceForSkImages())
.makeAlphaType(alpha_type);
SkCodec::Options options;
options.fFrameIndex = index;
options.fPriorFrame = prior_frame_;
options.fZeroInitialized = SkCodec::kNo_ZeroInitialized;
SkCodec::Result start_incremental_decode_result =
codec_->startIncrementalDecode(image_info, frame.Bitmap().getPixels(),
frame.Bitmap().rowBytes(), &options);
switch (start_incremental_decode_result) {
case SkCodec::kSuccess:
break;
case SkCodec::kIncompleteInput:
return;
default:
SetFailed();
return;
}
frame.SetStatus(ImageFrame::kFramePartial);
}
SkCodec::Result incremental_decode_result = codec_->incrementalDecode();
switch (incremental_decode_result) {
case SkCodec::kSuccess: {
SkCodec::FrameInfo frame_info;
bool frame_info_received = codec_->getFrameInfo(index, &frame_info);
DCHECK(frame_info_received);
frame.SetHasAlpha(frame_info.fAlphaType !=
SkAlphaType::kOpaque_SkAlphaType);
frame.SetPixelsChanged(true);
frame.SetStatus(ImageFrame::kFrameComplete);
PostDecodeProcessing(index);
break;
}
case SkCodec::kIncompleteInput:
frame.SetPixelsChanged(true);
if (FrameIsReceivedAtIndex(index) || IsAllDataReceived()) {
SetFailed();
}
break;
default:
frame.SetPixelsChanged(true);
SetFailed();
break;
}
}
bool GIFImageDecoder::CanReusePreviousFrameBuffer(size_t frame_index) const {
DCHECK_LT(frame_index, frame_buffer_cache_.size());
return frame_buffer_cache_[frame_index].GetDisposalMethod() !=
ImageFrame::kDisposeOverwritePrevious;
}
size_t GIFImageDecoder::GetViableReferenceFrameIndex(
size_t dependent_index) const {
DCHECK_LT(dependent_index, frame_buffer_cache_.size());
size_t required_previous_frame_index =
frame_buffer_cache_[dependent_index].RequiredPreviousFrameIndex();
// Any frame in the range [|required_previous_frame_index|, |dependent_index|)
// which has a disposal method other than kRestorePrevious can be provided as
// the prior frame to SkCodec.
//
// SkCodec sets SkCodec::FrameInfo::fRequiredFrame to the earliest frame which
// can be used. This might come up when several frames update the same
// subregion. If that same subregion is about to be overwritten, it doesn't
// matter which frame in that chain is provided.
DCHECK_NE(required_previous_frame_index, kNotFound);
// Loop backwards because the frames most likely to be in cache are the most
// recent.
for (size_t i = dependent_index - 1; i != required_previous_frame_index;
i--) {
const ImageFrame& frame = frame_buffer_cache_[i];
if (frame.GetDisposalMethod() == ImageFrame::kDisposeOverwritePrevious)
continue;
if (frame.GetStatus() == ImageFrame::kFrameComplete) {
return i;
}
}
return kNotFound;
}
} // namespace blink