// 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 #include #include "base/atomic_sequence_num.h" #include "base/hash/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/paint_worklet_input.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; ImageHeaderMetadata::ImageHeaderMetadata() = default; ImageHeaderMetadata::ImageHeaderMetadata(const ImageHeaderMetadata& other) = default; ImageHeaderMetadata& ImageHeaderMetadata::operator=( const ImageHeaderMetadata& other) = default; ImageHeaderMetadata::ImageHeaderMetadata::~ImageHeaderMetadata() = default; 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& PaintImage::GetSkImage() const { return cached_sk_image_; } const sk_sp& PaintImage::GetRasterSkImage() const { return cached_sk_image_; } gpu::Mailbox PaintImage::GetMailbox() const { DCHECK(texture_backing_); return texture_backing_->GetMailbox(); } 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_ = GetRasterSkImage()->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( paint_image_generator_, kDefaultFrameIndex, kDefaultGeneratorClientId)); } else if (texture_backing_) { cached_sk_image_ = texture_backing_->GetAcceleratedSkImage(); } 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 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, SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount]) const { DCHECK(plane_indices != nullptr); DCHECK(CanDecodeFromGenerator()); const uint32_t lazy_pixel_ref = unique_id(); return paint_image_generator_->GetYUVA8Planes( yuva_size_info, plane_indices, planes, frame_index, lazy_pixel_ref); } bool PaintImage::DecodeFromGenerator(void* memory, SkImageInfo* info, sk_sp 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 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()); return FrameKey(GetContentIdForFrame(frame_index), frame_index, subset_rect_); } PaintImage::ContentId PaintImage::GetContentIdForFrame( size_t frame_index) const { if (paint_image_generator_) return paint_image_generator_->GetContentIdForFrame(frame_index); DCHECK_NE(content_id_, kInvalidContentId); return content_id_; } SkColorType PaintImage::GetColorType() const { if (paint_image_generator_) return paint_image_generator_->GetSkImageInfo().colorType(); if (GetSkImage()) return GetSkImage()->colorType(); return kUnknown_SkColorType; } SkAlphaType PaintImage::GetAlphaType() const { if (paint_image_generator_) return paint_image_generator_->GetSkImageInfo().alphaType(); if (GetSkImage()) return GetSkImage()->alphaType(); return kUnknown_SkAlphaType; } bool PaintImage::IsTextureBacked() const { if (texture_backing_) return true; if (cached_sk_image_) return cached_sk_image_->isTextureBacked(); return false; } int PaintImage::width() const { return paint_worklet_input_ ? static_cast(paint_worklet_input_->GetSize().width()) : GetSkImage()->width(); } int PaintImage::height() const { return paint_worklet_input_ ? static_cast(paint_worklet_input_->GetSize().height()) : GetSkImage()->height(); } bool PaintImage::isSRGB() const { // Right now, JS paint worklets can only be in sRGB if (paint_worklet_input_) return true; auto* color_space = GetSkImage()->colorSpace(); if (!color_space) { // Assume the image will be sRGB if we don't know yet. return true; } return color_space->isSRGB(); } const ImageHeaderMetadata* PaintImage::GetImageHeaderMetadata() const { if (paint_image_generator_) return paint_image_generator_->GetMetadataForDecodeAcceleration(); return nullptr; } bool PaintImage::IsYuv(SkYUVASizeInfo* yuva_size_info, SkYUVAIndex* plane_indices, SkYUVColorSpace* yuv_color_space) const { SkYUVASizeInfo temp_yuva_size_info; SkYUVAIndex temp_plane_indices[SkYUVAIndex::kIndexCount]; SkYUVColorSpace temp_yuv_color_space; if (!yuva_size_info) { yuva_size_info = &temp_yuva_size_info; } if (!plane_indices) { plane_indices = temp_plane_indices; } if (!yuv_color_space) { yuv_color_space = &temp_yuv_color_space; } // ImageDecoder will fill out the value of |yuv_color_space| depending on // the codec's specification. return CanDecodeFromGenerator() && paint_image_generator_->QueryYUVA8(yuva_size_info, plane_indices, yuv_color_space); } const std::vector& 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 PaintImage::GetSkImageForFrame( size_t index, GeneratorClientId client_id) const { DCHECK_LT(index, FrameCount()); DCHECK(!IsTextureBacked()); // |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 GetRasterSkImage(); } // 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 GetRasterSkImage(); sk_sp image = SkImage::MakeFromGenerator(std::make_unique( 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(animation_type_) << " completion_state_: " << static_cast(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(content_id_), static_cast(frame_index_)); if (subset_rect_.IsEmpty()) { hash_ = original_hash; } else { size_t subset_hash = base::HashInts(static_cast( base::HashInts(subset_rect_.x(), subset_rect_.y())), static_cast(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