blob: 08f0012d9e539cdf97edb501303f6c6a7b4aad8b [file] [log] [blame]
// Copyright 2017 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 "cc/paint/paint_image.h"
#include <memory>
#include "base/atomic_sequence_num.h"
#include "base/hash.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_image_generator.h"
#include "cc/paint/paint_record.h"
#include "cc/paint/skia_paint_image_generator.h"
#include "ui/gfx/skia_util.h"
namespace cc {
namespace {
base::AtomicSequenceNumber g_next_image_id;
base::AtomicSequenceNumber g_next_image_content_id;
base::AtomicSequenceNumber g_next_generator_client_id;
} // namespace
const PaintImage::Id PaintImage::kNonLazyStableId = -1;
const size_t PaintImage::kDefaultFrameIndex = 0u;
const PaintImage::Id PaintImage::kInvalidId = -2;
const PaintImage::ContentId PaintImage::kInvalidContentId = -1;
const PaintImage::GeneratorClientId PaintImage::kDefaultGeneratorClientId = 0;
PaintImage::PaintImage() = default;
PaintImage::PaintImage(const PaintImage& other) = default;
PaintImage::PaintImage(PaintImage&& other) = default;
PaintImage::~PaintImage() = default;
PaintImage& PaintImage::operator=(const PaintImage& other) = default;
PaintImage& PaintImage::operator=(PaintImage&& other) = default;
bool PaintImage::operator==(const PaintImage& other) const {
if (sk_image_ != other.sk_image_)
return false;
if (paint_record_ != other.paint_record_)
return false;
if (paint_record_rect_ != other.paint_record_rect_)
return false;
if (content_id_ != other.content_id_)
return false;
if (paint_image_generator_ != other.paint_image_generator_)
return false;
if (id_ != other.id_)
return false;
if (animation_type_ != other.animation_type_)
return false;
if (completion_state_ != other.completion_state_)
return false;
if (subset_rect_ != other.subset_rect_)
return false;
if (is_multipart_ != other.is_multipart_)
return false;
return true;
}
// static
PaintImage::DecodingMode PaintImage::GetConservative(DecodingMode one,
DecodingMode two) {
if (one == two)
return one;
if (one == DecodingMode::kSync || two == DecodingMode::kSync)
return DecodingMode::kSync;
if (one == DecodingMode::kUnspecified || two == DecodingMode::kUnspecified)
return DecodingMode::kUnspecified;
DCHECK_EQ(one, DecodingMode::kAsync);
DCHECK_EQ(two, DecodingMode::kAsync);
return DecodingMode::kAsync;
}
// static
PaintImage::Id PaintImage::GetNextId() {
return g_next_image_id.GetNext();
}
// static
PaintImage::ContentId PaintImage::GetNextContentId() {
return g_next_image_content_id.GetNext();
}
// static
PaintImage::GeneratorClientId PaintImage::GetNextGeneratorClientId() {
// These IDs must start from 1, since 0 is the kDefaultGeneratorClientId.
return g_next_generator_client_id.GetNext() + 1;
}
// static
PaintImage PaintImage::CreateFromBitmap(SkBitmap bitmap) {
if (bitmap.drawsNothing())
return PaintImage();
return PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_image(SkImage::MakeFromBitmap(bitmap),
PaintImage::GetNextContentId())
.TakePaintImage();
}
const sk_sp<SkImage>& PaintImage::GetSkImage() const {
return cached_sk_image_;
}
PaintImage PaintImage::MakeSubset(const gfx::Rect& subset) const {
DCHECK(!subset.IsEmpty());
// If the subset is the same as the image bounds, we can return the same
// image.
gfx::Rect bounds(width(), height());
if (bounds == subset)
return *this;
DCHECK(bounds.Contains(subset))
<< "Subset should not be greater than the image bounds";
PaintImage result(*this);
result.subset_rect_ = subset;
// Store the subset from the original image.
result.subset_rect_.Offset(subset_rect_.x(), subset_rect_.y());
// Creating the |cached_sk_image_| using the SkImage from the original
// PaintImage is an optimization to allow re-use of the original decode for
// image subsets in skia, for cases that rely on skia's image decode cache.
result.cached_sk_image_ =
GetSkImage()->makeSubset(gfx::RectToSkIRect(subset));
return result;
}
void PaintImage::CreateSkImage() {
DCHECK(!cached_sk_image_);
if (sk_image_) {
cached_sk_image_ = sk_image_;
} else if (paint_record_) {
cached_sk_image_ = SkImage::MakeFromPicture(
ToSkPicture(paint_record_, gfx::RectToSkRect(paint_record_rect_)),
SkISize::Make(paint_record_rect_.width(), paint_record_rect_.height()),
nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB());
} else if (paint_image_generator_) {
cached_sk_image_ =
SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>(
paint_image_generator_, kDefaultFrameIndex,
kDefaultGeneratorClientId));
}
if (!subset_rect_.IsEmpty() && cached_sk_image_) {
cached_sk_image_ =
cached_sk_image_->makeSubset(gfx::RectToSkIRect(subset_rect_));
}
}
SkISize PaintImage::GetSupportedDecodeSize(
const SkISize& requested_size) const {
// TODO(vmpstr): In some cases we do not support decoding to any other
// size than the original. See the comment in CanDecodeFromGenerator()
// for more detail.
if (CanDecodeFromGenerator())
return paint_image_generator_->GetSupportedDecodeSize(requested_size);
return SkISize::Make(width(), height());
}
bool PaintImage::Decode(void* memory,
SkImageInfo* info,
sk_sp<SkColorSpace> color_space,
size_t frame_index,
GeneratorClientId client_id) const {
// We don't support SkImageInfo's with color spaces on them. Color spaces
// should always be passed via the |color_space| arg.
DCHECK(!info->colorSpace());
// We only support decode to supported decode size.
DCHECK(info->dimensions() == GetSupportedDecodeSize(info->dimensions()));
// TODO(vmpstr): In some cases we do not support decoding to any other
// size than the original. See the comment in CanDecodeFromGenerator()
// for more detail. For now, fallback to DecodeFromSkImage().
if (CanDecodeFromGenerator()) {
return DecodeFromGenerator(memory, info, std::move(color_space),
frame_index, client_id);
}
return DecodeFromSkImage(memory, info, std::move(color_space), frame_index,
client_id);
}
bool PaintImage::DecodeYuv(void* planes[SkYUVASizeInfo::kMaxCount],
size_t frame_index,
GeneratorClientId client_id,
const SkYUVASizeInfo& yuva_size_info) const {
SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
// Passing nullptr for the SkYUVASizeInfo forces IsYuv to create and fill out
// a temporary object instead because |yuva_size_info| is const.
bool is_yuv = IsYuv(nullptr, indices);
DCHECK(is_yuv);
DCHECK(CanDecodeFromGenerator());
const uint32_t lazy_pixel_ref = unique_id();
return paint_image_generator_->GetYUVA8Planes(yuva_size_info, indices, planes,
frame_index, lazy_pixel_ref);
}
bool PaintImage::DecodeFromGenerator(void* memory,
SkImageInfo* info,
sk_sp<SkColorSpace> color_space,
size_t frame_index,
GeneratorClientId client_id) const {
DCHECK(CanDecodeFromGenerator());
// First convert the info to have the requested color space, since the decoder
// will convert this for us.
*info = info->makeColorSpace(std::move(color_space));
const uint32_t lazy_pixel_ref = unique_id();
return paint_image_generator_->GetPixels(*info, memory, info->minRowBytes(),
frame_index, client_id,
lazy_pixel_ref);
}
// TODO(vmpstr): If we're using a subset_rect_ then the info specifies the
// requested size relative to the subset. However, the generator isn't aware
// of this subsetting and would need a size that is relative to the original
// image size. We could still implement this case, but we need to convert the
// requested size into the space of the original image.
bool PaintImage::CanDecodeFromGenerator() const {
return paint_image_generator_ && subset_rect_.IsEmpty();
}
bool PaintImage::DecodeFromSkImage(void* memory,
SkImageInfo* info,
sk_sp<SkColorSpace> color_space,
size_t frame_index,
GeneratorClientId client_id) const {
auto image = GetSkImageForFrame(frame_index, client_id);
DCHECK(image);
if (color_space) {
image = image->makeColorSpace(color_space);
if (!image)
return false;
}
// Note that the readPixels has to happen before converting the info to the
// given color space, since it can produce incorrect results.
bool result = image->readPixels(*info, memory, info->minRowBytes(), 0, 0,
SkImage::kDisallow_CachingHint);
*info = info->makeColorSpace(std::move(color_space));
return result;
}
bool PaintImage::ShouldAnimate() const {
return animation_type_ == AnimationType::ANIMATED &&
repetition_count_ != kAnimationNone && FrameCount() > 1;
}
PaintImage::FrameKey PaintImage::GetKeyForFrame(size_t frame_index) const {
DCHECK_LT(frame_index, FrameCount());
// Query the content id that uniquely identifies the content for this frame
// from the content provider.
ContentId content_id = kInvalidContentId;
if (paint_image_generator_)
content_id = paint_image_generator_->GetContentIdForFrame(frame_index);
else if (paint_record_ || sk_image_)
content_id = content_id_;
DCHECK_NE(content_id, kInvalidContentId);
return FrameKey(content_id, frame_index, subset_rect_);
}
SkColorType PaintImage::GetColorType() const {
if (paint_image_generator_)
return paint_image_generator_->GetSkImageInfo().colorType();
if (GetSkImage())
return GetSkImage()->colorType();
return kUnknown_SkColorType;
}
bool PaintImage::IsYuv(SkYUVASizeInfo* yuva_size_info,
SkYUVAIndex* plane_indices) const {
SkYUVASizeInfo temp_yuva_size_info;
SkYUVAIndex temp_plane_indices[SkYUVAIndex::kIndexCount];
if (!yuva_size_info) {
yuva_size_info = &temp_yuva_size_info;
}
if (!plane_indices) {
plane_indices = temp_plane_indices;
}
// We pass nullptr for color_space because QueryYUVA8 hardcodes it to
// kJPEG_SkYUVColorSpace when it should be kRec601_SkYUVColorSpace for WebP.
return CanDecodeFromGenerator() &&
paint_image_generator_->QueryYUVA8(yuva_size_info, plane_indices,
nullptr /* color_space */);
}
const std::vector<FrameMetadata>& PaintImage::GetFrameMetadata() const {
DCHECK_EQ(animation_type_, AnimationType::ANIMATED);
DCHECK(paint_image_generator_);
return paint_image_generator_->GetFrameMetadata();
}
size_t PaintImage::FrameCount() const {
if (!GetSkImage())
return 0u;
return paint_image_generator_
? paint_image_generator_->GetFrameMetadata().size()
: 1u;
}
sk_sp<SkImage> PaintImage::GetSkImageForFrame(
size_t index,
GeneratorClientId client_id) const {
DCHECK_LT(index, FrameCount());
// |client_id| and |index| are only relevant for generator backed images which
// perform lazy decoding and can be multi-frame.
if (!paint_image_generator_) {
DCHECK_EQ(index, kDefaultFrameIndex);
return GetSkImage();
}
// The internally cached SkImage is constructed using the default frame index
// and GeneratorClientId. Avoid creating a new SkImage.
if (index == kDefaultFrameIndex && client_id == kDefaultGeneratorClientId)
return GetSkImage();
sk_sp<SkImage> image =
SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>(
paint_image_generator_, index, client_id));
if (!subset_rect_.IsEmpty())
image = image->makeSubset(gfx::RectToSkIRect(subset_rect_));
return image;
}
std::string PaintImage::ToString() const {
std::ostringstream str;
str << "sk_image_: " << sk_image_ << " paint_record_: " << paint_record_
<< " paint_record_rect_: " << paint_record_rect_.ToString()
<< " paint_image_generator_: " << paint_image_generator_
<< " id_: " << id_
<< " animation_type_: " << static_cast<int>(animation_type_)
<< " completion_state_: " << static_cast<int>(completion_state_)
<< " subset_rect_: " << subset_rect_.ToString()
<< " is_multipart_: " << is_multipart_ << " is YUV: " << IsYuv();
return str.str();
}
PaintImage::FrameKey::FrameKey(ContentId content_id,
size_t frame_index,
gfx::Rect subset_rect)
: content_id_(content_id),
frame_index_(frame_index),
subset_rect_(subset_rect) {
size_t original_hash = base::HashInts(static_cast<uint64_t>(content_id_),
static_cast<uint64_t>(frame_index_));
if (subset_rect_.IsEmpty()) {
hash_ = original_hash;
} else {
size_t subset_hash =
base::HashInts(static_cast<uint64_t>(
base::HashInts(subset_rect_.x(), subset_rect_.y())),
static_cast<uint64_t>(base::HashInts(
subset_rect_.width(), subset_rect_.height())));
hash_ = base::HashInts(original_hash, subset_hash);
}
}
bool PaintImage::FrameKey::operator==(const FrameKey& other) const {
return content_id_ == other.content_id_ &&
frame_index_ == other.frame_index_ &&
subset_rect_ == other.subset_rect_;
}
bool PaintImage::FrameKey::operator!=(const FrameKey& other) const {
return !(*this == other);
}
std::string PaintImage::FrameKey::ToString() const {
std::ostringstream str;
str << "content_id: " << content_id_ << ","
<< "frame_index: " << frame_index_ << ","
<< "subset_rect: " << subset_rect_.ToString();
return str.str();
}
} // namespace cc