| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CC_PAINT_PAINT_IMAGE_H_ |
| #define CC_PAINT_PAINT_IMAGE_H_ |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "cc/paint/frame_metadata.h" |
| #include "cc/paint/image_animation_count.h" |
| #include "cc/paint/paint_export.h" |
| #include "cc/paint/paint_record.h" |
| #include "gpu/command_buffer/common/mailbox.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| #include "third_party/skia/include/core/SkImageInfo.h" |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| #include "third_party/skia/include/core/SkYUVAPixmaps.h" |
| #include "ui/gfx/display_color_spaces.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/hdr_metadata.h" |
| |
| class SkBitmap; |
| class SkColorSpace; |
| struct SkISize; |
| |
| namespace blink { |
| class VideoFrame; |
| } |
| |
| namespace cc { |
| |
| class PaintImageGenerator; |
| class PaintWorkletInput; |
| class TextureBacking; |
| |
| enum class ImageType { kPNG, kJPEG, kWEBP, kGIF, kICO, kBMP, kAVIF, kInvalid }; |
| |
| enum class YUVSubsampling { k410, k411, k420, k422, k440, k444, kUnknown }; |
| |
| enum class YUVIndex { kY, kU, kV }; |
| |
| // Should match the number of YUVIndex values. |
| static constexpr int kNumYUVPlanes = 3; |
| |
| struct CC_PAINT_EXPORT ImageHeaderMetadata { |
| public: |
| ImageHeaderMetadata(); |
| ImageHeaderMetadata(const ImageHeaderMetadata& other); |
| ImageHeaderMetadata& operator=(const ImageHeaderMetadata& other); |
| ~ImageHeaderMetadata(); |
| |
| // The image type, e.g., JPEG or WebP. |
| ImageType image_type = ImageType::kInvalid; |
| |
| // The subsampling format used for the chroma planes, e.g., YUV 4:2:0. |
| YUVSubsampling yuv_subsampling = YUVSubsampling::kUnknown; |
| |
| // The HDR metadata included with the image, if present. |
| absl::optional<gfx::HDRMetadata> hdr_metadata; |
| |
| // The visible size of the image (i.e., the area that contains meaningful |
| // pixels). |
| gfx::Size image_size; |
| |
| // The size of the area containing coded data, if known. For example, if the |
| // |image_size| for a 4:2:0 JPEG is 12x31, its coded size should be 16x32 |
| // because the size of a minimum-coded unit for 4:2:0 is 16x16. |
| // A zero-initialized |coded_size| indicates an invalid image. |
| absl::optional<gfx::Size> coded_size; |
| |
| // Whether the image embeds an ICC color profile. |
| bool has_embedded_color_profile = false; |
| |
| // Whether all the data was received prior to starting decoding work. |
| bool all_data_received_prior_to_decode = false; |
| |
| // For JPEGs only: whether the image is progressive (as opposed to baseline). |
| absl::optional<bool> jpeg_is_progressive; |
| |
| // For WebPs only: whether this is a simple-format lossy image. See |
| // https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy. |
| absl::optional<bool> webp_is_non_extended_lossy; |
| }; |
| |
| // A representation of an image for the compositor. This is the most abstract |
| // form of images, and represents what is known at paint time. Note that aside |
| // from default construction, it can only be constructed using a |
| // PaintImageBuilder, or copied/moved into using operator=. PaintImage can |
| // be backed by different kinds of content, such as a lazy generator, a paint |
| // record, a bitmap, or a texture. |
| // |
| // If backed by a generator, this image may not be decoded and information like |
| // the animation frame, the target colorspace, or the scale at which it will be |
| // used are not known yet. A DrawImage is a PaintImage with those decisions |
| // known but that might not have been decoded yet. A DecodedDrawImage is a |
| // DrawImage that has been decoded/scaled/uploaded with all of those parameters |
| // applied. |
| // |
| // The PaintImage -> DrawImage -> DecodedDrawImage -> PaintImage (via SkImage) |
| // path can be used to create a PaintImage that is snapshotted at a particular |
| // scale or animation frame. |
| class CC_PAINT_EXPORT PaintImage { |
| public: |
| using Id = int; |
| using AnimationSequenceId = uint32_t; |
| |
| // A ContentId is used to identify the content for which images which can be |
| // lazily generated (generator/record backed images). As opposed to Id, which |
| // stays constant for the same image, the content id can be updated when the |
| // backing encoded data for this image changes. For instance, in the case of |
| // images which can be progressively updated as more encoded data is received. |
| using ContentId = int; |
| |
| // A GeneratorClientId can be used to namespace different clients that are |
| // using the output of a PaintImageGenerator. |
| // |
| // This is used to allow multiple compositors to simultaneously decode the |
| // same image. Each compositor is assigned a unique GeneratorClientId which is |
| // passed through to the decoder from PaintImage::Decode. Internally the |
| // decoder ensures that requestes from different clients are executed in |
| // parallel. This is particularly important for animated images, where |
| // compositors displaying the same image can request decodes for different |
| // frames from this image. |
| using GeneratorClientId = int; |
| static const GeneratorClientId kDefaultGeneratorClientId; |
| |
| // The default frame index to use if no index is provided. For multi-frame |
| // images, this would imply the first frame of the animation. |
| static const size_t kDefaultFrameIndex; |
| |
| static const Id kInvalidId; |
| static const ContentId kInvalidContentId; |
| |
| class CC_PAINT_EXPORT FrameKey { |
| public: |
| FrameKey(ContentId content_id, size_t frame_index); |
| bool operator==(const FrameKey& other) const; |
| bool operator!=(const FrameKey& other) const; |
| |
| size_t hash() const { return hash_; } |
| std::string ToString() const; |
| size_t frame_index() const { return frame_index_; } |
| ContentId content_id() const { return content_id_; } |
| |
| private: |
| ContentId content_id_; |
| size_t frame_index_; |
| |
| size_t hash_; |
| }; |
| |
| struct CC_PAINT_EXPORT FrameKeyHash { |
| size_t operator()(const FrameKey& frame_key) const { |
| return frame_key.hash(); |
| } |
| }; |
| |
| enum class AnimationType { ANIMATED, VIDEO, STATIC }; |
| enum class CompletionState { DONE, PARTIALLY_DONE }; |
| enum class DecodingMode { |
| // No preference has been specified. The compositor may choose to use sync |
| // or async decoding. See CheckerImageTracker for the default behaviour. |
| kUnspecified, |
| |
| // It's preferred to display this image synchronously with the rest of the |
| // content updates, skipping any heuristics. |
| kSync, |
| |
| // Async is preferred. The compositor may decode async if it meets the |
| // heuristics used to avoid flickering (for instance vetoing of multipart |
| // response, animated, partially loaded images) and would be performant. See |
| // CheckerImageTracker for all heuristics used. |
| kAsync |
| }; |
| |
| // Returns the more conservative mode out of the two given ones. |
| static DecodingMode GetConservative(DecodingMode one, DecodingMode two); |
| |
| static Id GetNextId(); |
| static ContentId GetNextContentId(); |
| static GeneratorClientId GetNextGeneratorClientId(); |
| |
| // Creates a PaintImage wrapping |bitmap|. Note that the pixels will be copied |
| // unless the bitmap is marked immutable. |
| static PaintImage CreateFromBitmap(SkBitmap bitmap); |
| |
| PaintImage(); |
| PaintImage(const PaintImage& other); |
| PaintImage(PaintImage&& other); |
| ~PaintImage(); |
| |
| PaintImage& operator=(const PaintImage& other); |
| PaintImage& operator=(PaintImage&& other); |
| |
| bool operator==(const PaintImage& other) const; |
| bool operator!=(const PaintImage& other) const { return !(*this == other); } |
| |
| // Returns the smallest size that is at least as big as the requested_size |
| // such that we can decode to exactly that scale. If the requested size is |
| // larger than the image, this returns the image size. Any returned value is |
| // guaranteed to be stable. That is, |
| // GetSupportedDecodeSize(GetSupportedDecodeSize(size)) is guaranteed to be |
| // GetSupportedDecodeSize(size). |
| SkISize GetSupportedDecodeSize(const SkISize& requested_size) const; |
| |
| // Decode the image into RGBX into the given memory for the given SkImageInfo. |
| // - Size in |info| must be supported. |
| // - The amount of memory allocated must be at least |
| // |info|.minRowBytes() * |info|.height() |
| // Returns true on success and false on failure. Updates |info| to match the |
| // requested color space, if provided. |
| // Note that for non-lazy images this will do a copy or readback if the image |
| // is texture backed. |
| bool Decode(void* memory, |
| SkImageInfo* info, |
| sk_sp<SkColorSpace> color_space, |
| size_t frame_index, |
| GeneratorClientId client_id) const; |
| |
| // Decode the image into YUV into |pixmaps|. |
| // - SkPixmaps owned by |pixmaps| are preallocated to store the |
| // planar data. They must have have color types, row bytes, |
| // and sizes as indicated by PaintImage::IsYuv(). |
| // - The |frame_index| parameter will be passed along to |
| // ImageDecoder::DecodeToYUV but for multi-frame YUV support, ImageDecoder |
| // needs a separate YUV frame buffer cache. |
| bool DecodeYuv(const SkYUVAPixmaps& pixmaps, |
| size_t frame_index, |
| GeneratorClientId client_id) const; |
| |
| // Returns the SkImage associated with this PaintImage. If PaintImage is |
| // texture backed, this API will always do a readback from GPU to CPU memory, |
| // so avoid using it unless actual pixels are needed. For other cases, prefer |
| // using PaintImage APIs directly or use GetSkImageInfo() for metadata about |
| // the SkImage. |
| sk_sp<SkImage> GetSwSkImage() const; |
| |
| // Reads this image's pixels into caller-owned |dst_pixels| |
| bool readPixels(const SkImageInfo& dst_info, |
| void* dst_pixels, |
| size_t dst_row_bytes, |
| int src_x, |
| int src_y) const; |
| |
| // Returned mailbox must not outlive this PaintImage. |
| gpu::Mailbox GetMailbox() const; |
| |
| Id stable_id() const { return id_; } |
| SkImageInfo GetSkImageInfo() const; |
| AnimationType animation_type() const { return animation_type_; } |
| CompletionState completion_state() const { return completion_state_; } |
| bool is_multipart() const { return is_multipart_; } |
| bool is_high_bit_depth() const { return is_high_bit_depth_; } |
| bool may_be_lcp_candidate() const { return may_be_lcp_candidate_; } |
| int repetition_count() const { return repetition_count_; } |
| bool ShouldAnimate() const; |
| AnimationSequenceId reset_animation_sequence_id() const { |
| return reset_animation_sequence_id_; |
| } |
| DecodingMode decoding_mode() const { return decoding_mode_; } |
| |
| explicit operator bool() const { |
| return paint_worklet_input_ || cached_sk_image_ || texture_backing_; |
| } |
| bool IsLazyGenerated() const { |
| return paint_record_ || paint_image_generator_; |
| } |
| bool IsPaintWorklet() const { return !!paint_worklet_input_; } |
| bool IsTextureBacked() const; |
| // Skia internally buffers commands and flushes them as necessary but there |
| // are some cases where we need to force a flush. |
| void FlushPendingSkiaOps(); |
| int width() const { return GetSkImageInfo().width(); } |
| int height() const { return GetSkImageInfo().height(); } |
| SkColorSpace* color_space() const { |
| return paint_worklet_input_ ? nullptr : GetSkImageInfo().colorSpace(); |
| } |
| |
| gfx::ContentColorUsage GetContentColorUsage(bool* is_hlg = nullptr) const; |
| |
| // Returns whether this image will be decoded and rendered from YUV data |
| // and fills out |info|. |supported_data_types| indicates the bit depths and |
| // data types allowed. If successful, the caller can use |info| to allocate |
| // SkPixmaps to pass DecodeYuv() and render with the correct YUV->RGB |
| // transformation. |
| bool IsYuv(const SkYUVAPixmapInfo::SupportedDataTypes& supported_data_types, |
| SkYUVAPixmapInfo* info = nullptr) const; |
| |
| // Get metadata associated with this image. |
| SkColorType GetColorType() const { return GetSkImageInfo().colorType(); } |
| SkAlphaType GetAlphaType() const { return GetSkImageInfo().alphaType(); } |
| |
| // Returns general information about the underlying image. Returns nullptr if |
| // there is no available |paint_image_generator_|. |
| const ImageHeaderMetadata* GetImageHeaderMetadata() const; |
| |
| // Returns a unique id for the pixel data for the frame at |frame_index|. |
| FrameKey GetKeyForFrame(size_t frame_index) const; |
| |
| PaintImage::ContentId GetContentIdForFrame(size_t frame_index) const; |
| |
| // Returns the metadata for each frame of a multi-frame image. Should only be |
| // used with animated images. |
| const std::vector<FrameMetadata>& GetFrameMetadata() const; |
| |
| // Returns the total number of frames known to exist in this image. |
| size_t FrameCount() const; |
| |
| // Returns an SkImage for the frame at |index|. |
| sk_sp<SkImage> GetSkImageForFrame(size_t index, |
| GeneratorClientId client_id) const; |
| |
| const scoped_refptr<PaintWorkletInput>& paint_worklet_input() const { |
| return paint_worklet_input_; |
| } |
| |
| bool IsOpaque() const; |
| |
| std::string ToString() const; |
| |
| private: |
| friend class PaintImageBuilder; |
| FRIEND_TEST_ALL_PREFIXES(PaintImageTest, Subsetting); |
| |
| // Used internally for PaintImages created at raster. |
| static const Id kNonLazyStableId; |
| friend class ScopedRasterFlags; |
| friend class PaintOpReader; |
| |
| friend class PlaybackImageProvider; |
| friend class DrawImageRectOp; |
| friend class DrawImageOp; |
| friend class DrawSkottieOp; |
| |
| // TODO(crbug.com/1031051): Remove these once GetSkImage() |
| // is fully removed. |
| friend class ImagePaintFilter; |
| friend class PaintShader; |
| friend class blink::VideoFrame; |
| |
| bool DecodeFromGenerator(void* memory, |
| SkImageInfo* info, |
| sk_sp<SkColorSpace> color_space, |
| size_t frame_index, |
| GeneratorClientId client_id) const; |
| bool DecodeFromSkImage(void* memory, |
| SkImageInfo* info, |
| sk_sp<SkColorSpace> color_space, |
| size_t frame_index, |
| GeneratorClientId client_id) const; |
| void CreateSkImage(); |
| |
| // Only supported in non-OOPR contexts by friend callers. |
| sk_sp<SkImage> GetAcceleratedSkImage() const; |
| |
| // GetSkImage() is being deprecated, see crbug.com/1031051. |
| // Prefer using GetSwSkImage() or GetSkImageInfo(). |
| const sk_sp<SkImage>& GetSkImage() const; |
| |
| sk_sp<SkImage> sk_image_; |
| absl::optional<PaintRecord> paint_record_; |
| gfx::Rect paint_record_rect_; |
| |
| ContentId content_id_ = kInvalidContentId; |
| |
| sk_sp<PaintImageGenerator> paint_image_generator_; |
| sk_sp<TextureBacking> texture_backing_; |
| |
| Id id_ = 0; |
| AnimationType animation_type_ = AnimationType::STATIC; |
| CompletionState completion_state_ = CompletionState::DONE; |
| int repetition_count_ = kAnimationNone; |
| |
| // Whether the data fetched for this image is a part of a multpart response. |
| bool is_multipart_ = false; |
| |
| // Whether this image has more than 8 bits per color channel. |
| bool is_high_bit_depth_ = false; |
| |
| // Whether this image may untimately be a candidate for Largest Contentful |
| // Paint. The final LCP contribution of an image is unknown until we present |
| // it, but this flag is intended for metrics on when we do not present the |
| // image when the system claims. |
| bool may_be_lcp_candidate_ = false; |
| |
| // An incrementing sequence number maintained by the painter to indicate if |
| // this animation should be reset in the compositor. Incrementing this number |
| // will reset this animation in the compositor for the first frame which has a |
| // recording with a PaintImage storing the updated sequence id. |
| AnimationSequenceId reset_animation_sequence_id_ = 0u; |
| |
| DecodingMode decoding_mode_ = DecodingMode::kSync; |
| |
| // The |cached_sk_image_| can be derived/created from other inputs present in |
| // the PaintImage but we always construct it at creation time for 2 reasons: |
| // 1) This ensures that the underlying SkImage is shared across PaintImage |
| // copies, which is necessary to allow reuse of decodes from this image in |
| // skia's cache. |
| // 2) Ensures that accesses to it are thread-safe. |
| sk_sp<SkImage> cached_sk_image_; |
| |
| // The input parameters that are needed to execute the JS paint callback. |
| scoped_refptr<PaintWorkletInput> paint_worklet_input_; |
| }; |
| |
| } // namespace cc |
| |
| #endif // CC_PAINT_PAINT_IMAGE_H_ |