| // 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 <sstream> |
| #include <utility> |
| |
| #include "base/atomic_sequence_num.h" |
| #include "base/hash/hash.h" |
| #include "base/logging.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 "third_party/skia/include/gpu/GrBackendSurface.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 (is_multipart_ != other.is_multipart_) |
| return false; |
| if (texture_backing_ != other.texture_backing_) |
| return false; |
| if (paint_worklet_input_ != other.paint_worklet_input_) |
| 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_; |
| } |
| |
| sk_sp<SkImage> PaintImage::GetSwSkImage() const { |
| if (texture_backing_) { |
| return texture_backing_->GetSkImageViaReadback(); |
| } else if (cached_sk_image_ && cached_sk_image_->isTextureBacked()) { |
| return cached_sk_image_->makeNonTextureImage(); |
| } |
| return cached_sk_image_; |
| } |
| |
| sk_sp<SkImage> PaintImage::GetAcceleratedSkImage() const { |
| DCHECK(!cached_sk_image_ || cached_sk_image_->isTextureBacked()); |
| return cached_sk_image_; |
| } |
| |
| bool PaintImage::readPixels(const SkImageInfo& dst_info, |
| void* dst_pixels, |
| size_t dst_row_bytes, |
| int src_x, |
| int src_y) const { |
| if (texture_backing_) { |
| return texture_backing_->readPixels(dst_info, dst_pixels, dst_row_bytes, |
| src_x, src_y); |
| } else if (cached_sk_image_) { |
| return cached_sk_image_->readPixels(dst_info, dst_pixels, dst_row_bytes, |
| src_x, src_y); |
| } |
| return false; |
| } |
| |
| SkImageInfo PaintImage::GetSkImageInfo() const { |
| if (paint_image_generator_) { |
| return paint_image_generator_->GetSkImageInfo(); |
| } else if (texture_backing_) { |
| return texture_backing_->GetSkImageInfo(); |
| } else if (cached_sk_image_) { |
| return cached_sk_image_->imageInfo(); |
| } else { |
| return SkImageInfo::MakeUnknown(); |
| } |
| } |
| |
| gpu::Mailbox PaintImage::GetMailbox() const { |
| DCHECK(texture_backing_); |
| return texture_backing_->GetMailbox(); |
| } |
| |
| 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)); |
| } else if (texture_backing_) { |
| cached_sk_image_ = texture_backing_->GetAcceleratedSkImage(); |
| } |
| } |
| |
| SkISize PaintImage::GetSupportedDecodeSize( |
| const SkISize& requested_size) const { |
| if (paint_image_generator_) |
| 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())); |
| |
| if (paint_image_generator_) { |
| 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(const SkYUVAPixmaps& pixmaps, |
| size_t frame_index, |
| GeneratorClientId client_id) const { |
| DCHECK(pixmaps.isValid()); |
| DCHECK(paint_image_generator_); |
| const uint32_t lazy_pixel_ref = stable_id(); |
| return paint_image_generator_->GetYUVAPlanes(pixmaps, 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(paint_image_generator_); |
| // 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 = stable_id(); |
| return paint_image_generator_->GetPixels(*info, memory, info->minRowBytes(), |
| frame_index, client_id, |
| lazy_pixel_ref); |
| } |
| |
| 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, nullptr); |
| 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); |
| } |
| |
| 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 { |
| return GetSkImageInfo().colorType(); |
| } |
| |
| SkAlphaType PaintImage::GetAlphaType() const { |
| return GetSkImageInfo().alphaType(); |
| } |
| |
| bool PaintImage::IsTextureBacked() const { |
| if (texture_backing_) |
| return true; |
| if (cached_sk_image_) |
| return cached_sk_image_->isTextureBacked(); |
| return false; |
| } |
| |
| void PaintImage::FlushPendingSkiaOps() { |
| if (texture_backing_) |
| texture_backing_->FlushPendingSkiaOps(); |
| } |
| |
| bool PaintImage::HasExclusiveTextureAccess() const { |
| DCHECK(IsTextureBacked()); |
| return texture_backing_->unique(); |
| } |
| |
| int PaintImage::width() const { |
| return paint_worklet_input_ |
| ? static_cast<int>(paint_worklet_input_->GetSize().width()) |
| : GetSkImageInfo().width(); |
| } |
| |
| int PaintImage::height() const { |
| return paint_worklet_input_ |
| ? static_cast<int>(paint_worklet_input_->GetSize().height()) |
| : GetSkImageInfo().height(); |
| } |
| |
| gfx::ContentColorUsage PaintImage::GetContentColorUsage() const { |
| // Right now, JS paint worklets can only be in sRGB |
| if (paint_worklet_input_) |
| return gfx::ContentColorUsage::kSRGB; |
| |
| const auto* color_space = GetSkImageInfo().colorSpace(); |
| |
| // Assume the image will be sRGB if we don't know yet. |
| if (!color_space || color_space->isSRGB()) |
| return gfx::ContentColorUsage::kSRGB; |
| |
| skcms_TransferFunction fn; |
| if (!color_space->isNumericalTransferFn(&fn) && |
| (skcms_TransferFunction_isPQish(&fn) || |
| skcms_TransferFunction_isHLGish(&fn))) { |
| return gfx::ContentColorUsage::kHDR; |
| } |
| |
| // If it's not HDR and not SRGB, report it as WCG. |
| return gfx::ContentColorUsage::kWideColorGamut; |
| } |
| |
| const ImageHeaderMetadata* PaintImage::GetImageHeaderMetadata() const { |
| if (paint_image_generator_) |
| return paint_image_generator_->GetMetadataForDecodeAcceleration(); |
| return nullptr; |
| } |
| |
| bool PaintImage::IsYuv( |
| const SkYUVAPixmapInfo::SupportedDataTypes& supported_data_types, |
| SkYUVAPixmapInfo* info) const { |
| SkYUVAPixmapInfo temp_info; |
| if (!info) |
| info = &temp_info; |
| // ImageDecoder will fill out the SkYUVColorSpace in |info| depending on the |
| // codec's specification. |
| return paint_image_generator_ && |
| paint_image_generator_->QueryYUVA(supported_data_types, info); |
| } |
| |
| 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 (!*this) |
| 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()); |
| 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 GetSwSkImage(); |
| } |
| |
| // 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 GetSwSkImage(); |
| |
| sk_sp<SkImage> image = |
| SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>( |
| paint_image_generator_, index, client_id)); |
| 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_) |
| << " is_multipart_: " << is_multipart_ |
| << " is YUV: " << IsYuv(SkYUVAPixmapInfo::SupportedDataTypes::All()); |
| return str.str(); |
| } |
| |
| PaintImage::FrameKey::FrameKey(ContentId content_id, size_t frame_index) |
| : content_id_(content_id), frame_index_(frame_index) { |
| hash_ = base::HashInts(static_cast<uint64_t>(content_id_), |
| static_cast<uint64_t>(frame_index_)); |
| } |
| |
| bool PaintImage::FrameKey::operator==(const FrameKey& other) const { |
| return content_id_ == other.content_id_ && frame_index_ == other.frame_index_; |
| } |
| |
| 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_; |
| return str.str(); |
| } |
| |
| } // namespace cc |