blob: e1adcf25d8ff56bc5fc70a5e83f1622713c5c5b9 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/graphics/image_decoder_wrapper.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
namespace blink {
namespace {
bool CompatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) {
if (src == dst)
return true;
// It is legal to write kOpaque_SkAlphaType pixels into a kPremul_SkAlphaType
// buffer. This can happen when DeferredImageDecoder allocates an
// kOpaque_SkAlphaType image generator based on cached frame info, while the
// ImageFrame-allocated dest bitmap stays kPremul_SkAlphaType.
if (src.alphaType() == kOpaque_SkAlphaType &&
dst.alphaType() == kPremul_SkAlphaType) {
const SkImageInfo& tmp = src.makeAlphaType(kPremul_SkAlphaType);
return tmp == dst;
}
return false;
}
// Creates a SkPixelRef such that the memory for pixels is given by an external
// body. This is used to write directly to the memory given by Skia during
// decoding.
class ExternalMemoryAllocator final : public SkBitmap::Allocator {
USING_FAST_MALLOC(ExternalMemoryAllocator);
public:
ExternalMemoryAllocator(const SkImageInfo& info,
void* pixels,
size_t row_bytes)
: info_(info), pixels_(pixels), row_bytes_(row_bytes) {}
bool allocPixelRef(SkBitmap* dst) override {
const SkImageInfo& info = dst->info();
if (kUnknown_SkColorType == info.colorType())
return false;
if (!CompatibleInfo(info_, info) || row_bytes_ != dst->rowBytes())
return false;
return dst->installPixels(info, pixels_, row_bytes_);
}
private:
SkImageInfo info_;
void* pixels_;
size_t row_bytes_;
DISALLOW_COPY_AND_ASSIGN(ExternalMemoryAllocator);
};
} // namespace
ImageDecoderWrapper::ImageDecoderWrapper(
ImageFrameGenerator* generator,
SegmentReader* data,
const SkISize& scaled_size,
ImageDecoder::AlphaOption alpha_option,
ColorBehavior decoder_color_behavior,
ImageDecoder::HighBitDepthDecodingOption decoding_option,
size_t index,
const SkImageInfo& info,
void* pixels,
size_t row_bytes,
bool all_data_received,
cc::PaintImage::GeneratorClientId client_id)
: generator_(generator),
data_(data),
scaled_size_(scaled_size),
alpha_option_(alpha_option),
decoder_color_behavior_(decoder_color_behavior),
decoding_option_(decoding_option),
frame_index_(index),
info_(info),
pixels_(pixels),
row_bytes_(row_bytes),
all_data_received_(all_data_received),
client_id_(client_id) {}
ImageDecoderWrapper::~ImageDecoderWrapper() = default;
bool ImageDecoderWrapper::Decode(ImageDecoderFactory* factory,
size_t* frame_count,
bool* has_alpha) {
DCHECK(frame_count);
DCHECK(has_alpha);
ImageDecoder* decoder = nullptr;
std::unique_ptr<ImageDecoder> new_decoder;
const bool resume_decoding = ImageDecodingStore::Instance().LockDecoder(
generator_, scaled_size_, alpha_option_, client_id_, &decoder);
DCHECK(!resume_decoding || decoder);
if (resume_decoding) {
decoder->SetData(data_, all_data_received_);
} else {
new_decoder = CreateDecoderWithData(factory);
if (!new_decoder)
return false;
decoder = new_decoder.get();
}
// For multi-frame image decoders, we need to know how many frames are
// in that image in order to release the decoder when all frames are
// decoded. FrameCount() is reliable only if all data is received and set in
// decoder, particularly with GIF.
if (all_data_received_)
*frame_count = decoder->FrameCount();
const bool decode_to_external_memory =
ShouldDecodeToExternalMemory(*frame_count, resume_decoding);
ExternalMemoryAllocator external_memory_allocator(info_, pixels_, row_bytes_);
if (decode_to_external_memory)
decoder->SetMemoryAllocator(&external_memory_allocator);
ImageFrame* frame = nullptr;
{
// This trace event is important since it is used by telemetry scripts to
// measure the decode time.
TRACE_EVENT0("blink,benchmark", "ImageFrameGenerator::decode");
frame = decoder->DecodeFrameBufferAtIndex(frame_index_);
}
// SetMemoryAllocator() can try to access decoder's data, so we have to
// clear it before clearing SegmentReader.
if (decode_to_external_memory)
decoder->SetMemoryAllocator(nullptr);
// Verify we have the only ref-count.
DCHECK(external_memory_allocator.unique());
decoder->SetData(scoped_refptr<SegmentReader>(nullptr), false);
decoder->ClearCacheExceptFrame(frame_index_);
const bool has_decoded_frame =
frame && frame->GetStatus() != ImageFrame::kFrameEmpty &&
!frame->Bitmap().isNull();
if (!has_decoded_frame) {
decode_failed_ = decoder->Failed();
if (resume_decoding) {
ImageDecodingStore::Instance().UnlockDecoder(generator_, client_id_,
decoder);
}
return false;
}
SkBitmap scaled_size_bitmap = frame->Bitmap();
DCHECK_EQ(scaled_size_bitmap.width(), scaled_size_.width());
DCHECK_EQ(scaled_size_bitmap.height(), scaled_size_.height());
// If we decoded into external memory, the bitmap should be backed by the
// pixels passed to the allocator.
DCHECK(!decode_to_external_memory ||
scaled_size_bitmap.getPixels() == pixels_);
*has_alpha = !scaled_size_bitmap.isOpaque();
if (!decode_to_external_memory)
scaled_size_bitmap.readPixels(info_, pixels_, row_bytes_, 0, 0);
// Free as much memory as possible. For single-frame images, we can
// just delete the decoder entirely if they use the external allocator.
// For multi-frame images, we keep the decoder around in order to preserve
// decoded information such as the required previous frame indexes, but if
// we've reached the last frame we can at least delete all the cached frames.
// (If we were to do this before reaching the last frame, any subsequent
// requested frames which relied on the current frame would trigger extra
// re-decoding of all frames in the dependency chain).
const bool frame_was_completely_decoded =
frame->GetStatus() == ImageFrame::kFrameComplete || all_data_received_;
PurgeAllFramesIfNecessary(decoder, frame_was_completely_decoded,
*frame_count);
const bool should_remove_decoder = ShouldRemoveDecoder(
frame_was_completely_decoded, decode_to_external_memory);
if (resume_decoding) {
if (should_remove_decoder) {
ImageDecodingStore::Instance().RemoveDecoder(generator_, client_id_,
decoder);
} else {
ImageDecodingStore::Instance().UnlockDecoder(generator_, client_id_,
decoder);
}
} else if (!should_remove_decoder) {
// If we have a newly created decoder which we don't want to remove, add
// it to the cache.
ImageDecodingStore::Instance().InsertDecoder(generator_, client_id_,
std::move(new_decoder));
}
return true;
}
bool ImageDecoderWrapper::ShouldDecodeToExternalMemory(
size_t frame_count,
bool resume_decoding) const {
// Some multi-frame images need their decode cached in the decoder to allow
// future frames to reference previous frames.
//
// This implies extra requirements on external memory allocators for
// multi-frame images. However, there is no enforcement of these extra
// requirements. As a result, do not attempt to use external memory
// allocators for multi-frame images.
if (generator_->IsMultiFrame())
return false;
// On low-end devices, always use the external allocator, to avoid storing
// duplicate copies of the data for partial decodes in the ImageDecoder's
// cache.
if (Platform::Current()->IsLowEndDevice()) {
DCHECK(!resume_decoding);
return true;
}
// TODO (scroggo): If !is_multi_frame_ && new_decoder && frame_count_, it
// should always be the case that 1u == frame_count_. But it looks like it
// is currently possible for frame_count_ to be another value.
if (1u == frame_count && all_data_received_ && !resume_decoding) {
// Also use external allocator in situations when all of the data has been
// received and there is not already a partial cache in the image decoder.
return true;
}
return false;
}
bool ImageDecoderWrapper::ShouldRemoveDecoder(
bool frame_was_completely_decoded,
bool decoded_to_external_memory) const {
// Mult-frame images need the decode cached to allow decoding subsequent
// frames without having to decode the complete dependency chain. For this
// reason, we should never be decoding directly to external memory for these
// images.
if (generator_->IsMultiFrame()) {
DCHECK(!decoded_to_external_memory);
return false;
}
// If the decode was done directly to external memory, the decoder has no
// data to cache. Remove it.
if (decoded_to_external_memory)
return true;
// If we were caching a decoder with a partially decoded frame which has
// now been completely decoded, we don't need to cache this decoder anymore.
if (frame_was_completely_decoded)
return true;
return false;
}
void ImageDecoderWrapper::PurgeAllFramesIfNecessary(
ImageDecoder* decoder,
bool frame_was_completely_decoded,
size_t frame_count) const {
// We only purge all frames when we have decoded the last frame for a
// multi-frame image. This is because once the last frame is decoded, the
// animation will loop back to the first frame which does not need the last
// frame as a dependency and therefore can be purged.
// For single-frame images, the complete decoder is removed once it has been
// completely decoded.
if (!generator_->IsMultiFrame())
return;
// The frame was only partially decoded, we need to retain it to be able to
// resume the decoder.
if (!frame_was_completely_decoded)
return;
const size_t last_frame_index = frame_count - 1;
if (frame_index_ == last_frame_index)
decoder->ClearCacheExceptFrame(kNotFound);
}
std::unique_ptr<ImageDecoder> ImageDecoderWrapper::CreateDecoderWithData(
ImageDecoderFactory* factory) const {
if (factory) {
auto decoder = factory->Create();
if (decoder)
decoder->SetData(data_, all_data_received_);
return decoder;
}
// The newly created decoder just grabbed the data. No need to reset it.
return ImageDecoder::Create(data_, all_data_received_, alpha_option_,
decoding_option_, decoder_color_behavior_,
scaled_size_);
}
} // namespace blink