// 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. #ifndef CC_PAINT_PAINT_OP_BUFFER_H_ #define CC_PAINT_PAINT_OP_BUFFER_H_ #include #include #include #include "base/callback.h" #include "base/containers/stack_container.h" #include "base/debug/alias.h" #include "base/logging.h" #include "base/memory/aligned_memory.h" #include "base/optional.h" #include "cc/base/math_util.h" #include "cc/paint/image_provider.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_flags.h" #include "cc/paint/transfer_cache_deserialize_helper.h" #include "cc/paint/transfer_cache_serialize_helper.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkScalar.h" #include "third_party/skia/include/core/SkTextBlob.h" // PaintOpBuffer is a reimplementation of SkLiteDL. // See: third_party/skia/src/core/SkLiteDL.h. namespace cc { class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix { public: explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) { (void)getType(); } }; class CC_PAINT_EXPORT ThreadsafePath : public SkPath { public: explicit ThreadsafePath(const SkPath& path) : SkPath(path) { updateBoundsCache(); } ThreadsafePath() { updateBoundsCache(); } }; // See PaintOp::Serialize/Deserialize for comments. Derived Serialize types // don't write the 4 byte type/skip header because they don't know how much // data they will need to write. PaintOp::Serialize itself must update it. #define HAS_SERIALIZATION_FUNCTIONS() \ static size_t Serialize(const PaintOp* op, void* memory, size_t size, \ const SerializeOptions& options); \ static PaintOp* Deserialize(const volatile void* input, size_t input_size, \ void* output, size_t output_size, \ const DeserializeOptions& options); enum class PaintOpType : uint8_t { Annotate, ClipPath, ClipRect, ClipRRect, Concat, CustomData, DrawColor, DrawDRRect, DrawImage, DrawImageRect, DrawIRect, DrawLine, DrawOval, DrawPath, DrawRecord, DrawRect, DrawRRect, DrawTextBlob, Noop, Restore, Rotate, Save, SaveLayer, SaveLayerAlpha, Scale, SetMatrix, Translate, LastPaintOpType = Translate, }; CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type); CC_PAINT_EXPORT std::ostream& operator<<(std::ostream&, PaintOpType); struct CC_PAINT_EXPORT PlaybackParams { using CustomDataRasterCallback = base::RepeatingCallback; using DidDrawOpCallback = base::RepeatingCallback; explicit PlaybackParams(ImageProvider* image_provider); PlaybackParams( ImageProvider* image_provider, const SkMatrix& original_ctm, CustomDataRasterCallback custom_callback = CustomDataRasterCallback(), DidDrawOpCallback did_draw_op_callback = DidDrawOpCallback()); ~PlaybackParams(); ImageProvider* image_provider; const SkMatrix original_ctm; CustomDataRasterCallback custom_callback; DidDrawOpCallback did_draw_op_callback; }; class CC_PAINT_EXPORT PaintOp { public: uint32_t type : 8; uint32_t skip : 24; explicit PaintOp(PaintOpType type) : type(static_cast(type)) {} PaintOpType GetType() const { return static_cast(type); } // Subclasses should provide a static Raster() method which is called from // here. The Raster method should take a const PaintOp* parameter. It is // static with a pointer to the base type so that we can use it as a function // pointer. void Raster(SkCanvas* canvas, const PlaybackParams& params) const; bool IsDrawOp() const; bool IsPaintOpWithFlags() const; bool operator==(const PaintOp& other) const; bool operator!=(const PaintOp& other) const { return !(*this == other); } struct CC_PAINT_EXPORT SerializeOptions { SerializeOptions(); SerializeOptions(ImageProvider* image_provider, TransferCacheSerializeHelper* transfer_cache, SkCanvas* canvas, const SkMatrix& original_ctm); // Required. TransferCacheSerializeHelper* transfer_cache = nullptr; SkCanvas* canvas = nullptr; // Optional. ImageProvider* image_provider = nullptr; SkMatrix original_ctm = SkMatrix::I(); // The flags to use when serializing this op. This can be used to override // the flags serialized with the op. Valid only for PaintOpWithFlags. const PaintFlags* flags_to_serialize = nullptr; }; struct DeserializeOptions { TransferCacheDeserializeHelper* transfer_cache = nullptr; }; // Indicates how PaintImages are serialized. enum class SerializedImageType : uint8_t { kNoImage, kImageData, kTransferCacheEntry, kLastType = kTransferCacheEntry }; // Subclasses should provide a static Serialize() method called from here. // If the op can be serialized to |memory| in no more than |size| bytes, // then return the number of bytes written. If it won't fit, return 0. size_t Serialize(void* memory, size_t size, const SerializeOptions& options) const; // Deserializes a PaintOp of this type from a given buffer |input| of // at most |input_size| bytes. Returns null on any errors. // The PaintOp is deserialized into the |output| buffer and returned // if valid. nullptr is returned if the deserialization fails. // |output_size| must be at least LargestPaintOp + serialized->skip, // to fit all ops. The caller is responsible for destroying these ops. // After reading, it returns the number of bytes read in |read_bytes|. static PaintOp* Deserialize(const volatile void* input, size_t input_size, void* output, size_t output_size, size_t* read_bytes, const DeserializeOptions& options); // For draw ops, returns true if a conservative bounding rect can be provided // for the op. static bool GetBounds(const PaintOp* op, SkRect* rect); // Returns true if the op lies outside the current clip and should be skipped. // Should only be used with draw ops. static bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas); // Returns true if executing this op will require decoding of any lazy // generated images. static bool OpHasDiscardableImages(const PaintOp* op); // Returns true if the given op type has PaintFlags. static bool TypeHasFlags(PaintOpType type); int CountSlowPaths() const { return 0; } int CountSlowPathsFromFlags() const { return 0; } bool HasNonAAPaint() const { return false; } bool HasDiscardableImages() const { return false; } bool HasDiscardableImagesFromFlags() const { return false; } // Returns the number of bytes used by this op in referenced sub records // and display lists. This doesn't count other objects like paths or blobs. size_t AdditionalBytesUsed() const { return 0; } // Returns the number of ops in referenced sub records and display lists. size_t AdditionalOpCount() const { return 0; } // Run the destructor for the derived op type. Ops are usually contained in // memory buffers and so don't have their destructors run automatically. void DestroyThis(); // DrawColor is more restrictive on the blend modes that can be used. static bool IsValidDrawColorSkBlendMode(SkBlendMode mode) { return static_cast(mode) <= static_cast(SkBlendMode::kLastCoeffMode); } // PaintFlags can have more complex blend modes than DrawColor. static bool IsValidPaintFlagsSkBlendMode(SkBlendMode mode) { return static_cast(mode) <= static_cast(SkBlendMode::kLastMode); } static bool IsValidSkClipOp(SkClipOp op) { return static_cast(op) <= static_cast(SkClipOp::kMax_EnumValue); } static bool IsValidPath(const SkPath& path) { return path.isValid() && path.pathRefIsValid(); } static bool IsUnsetRect(const SkRect& rect) { return rect.fLeft == SK_ScalarInfinity; } static bool IsValidOrUnsetRect(const SkRect& rect) { return IsUnsetRect(rect) || rect.isFinite(); } // PaintOp supports having nans, but some tests want to make sure // that operator== is true on two objects. These helpers compare // various types in a way where nan == nan is true. static bool AreEqualEvenIfNaN(float left, float right) { if (std::isnan(left) && std::isnan(right)) return true; return left == right; } static bool AreSkPointsEqual(const SkPoint& left, const SkPoint& right); static bool AreSkPoint3sEqual(const SkPoint3& left, const SkPoint3& right); static bool AreSkRectsEqual(const SkRect& left, const SkRect& right); static bool AreSkRRectsEqual(const SkRRect& left, const SkRRect& right); static bool AreSkMatricesEqual(const SkMatrix& left, const SkMatrix& right); static bool AreSkFlattenablesEqual(SkFlattenable* left, SkFlattenable* right); static constexpr bool kIsDrawOp = false; static constexpr bool kHasPaintFlags = false; // Since skip and type fit in a uint32_t, this is the max size of skip. static constexpr size_t kMaxSkip = static_cast(1 << 24); static const SkRect kUnsetRect; }; class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp { public: static constexpr bool kHasPaintFlags = true; PaintOpWithFlags(PaintOpType type, const PaintFlags& flags) : PaintOp(type), flags(flags) {} int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; } bool HasNonAAPaint() const { return !flags.isAntiAlias(); } bool HasDiscardableImagesFromFlags() const; void RasterWithFlags(SkCanvas* canvas, const PaintFlags* flags, const PlaybackParams& params) const; // Subclasses should provide a static RasterWithFlags() method which is called // from the Raster() method. The RasterWithFlags() should use the SkPaint // passed to it, instead of the |flags| member directly, as some callers may // provide a modified PaintFlags. The RasterWithFlags() method is static with // a const PaintOpWithFlags* parameter so that it can be used as a function // pointer. PaintFlags flags; protected: explicit PaintOpWithFlags(PaintOpType type) : PaintOp(type) {} }; class CC_PAINT_EXPORT AnnotateOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Annotate; AnnotateOp(PaintCanvas::AnnotationType annotation_type, const SkRect& rect, sk_sp data); ~AnnotateOp(); static void Raster(const AnnotateOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return rect.isFinite(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); PaintCanvas::AnnotationType annotation_type; SkRect rect; sk_sp data; private: AnnotateOp(); }; class CC_PAINT_EXPORT ClipPathOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::ClipPath; ClipPathOp(SkPath path, SkClipOp op, bool antialias) : PaintOp(kType), path(path), op(op), antialias(antialias) {} static void Raster(const ClipPathOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return IsValidSkClipOp(op) && IsValidPath(path); } static bool AreEqual(const PaintOp* left, const PaintOp* right); int CountSlowPaths() const; bool HasNonAAPaint() const { return !antialias; } HAS_SERIALIZATION_FUNCTIONS(); ThreadsafePath path; SkClipOp op; bool antialias; private: ClipPathOp() : PaintOp(kType) {} }; class CC_PAINT_EXPORT ClipRectOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::ClipRect; ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias) : PaintOp(kType), rect(rect), op(op), antialias(antialias) {} static void Raster(const ClipRectOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return IsValidSkClipOp(op) && rect.isFinite(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRect rect; SkClipOp op; bool antialias; }; class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::ClipRRect; ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias) : PaintOp(kType), rrect(rrect), op(op), antialias(antialias) {} static void Raster(const ClipRRectOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return IsValidSkClipOp(op) && rrect.isValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); bool HasNonAAPaint() const { return !antialias; } HAS_SERIALIZATION_FUNCTIONS(); SkRRect rrect; SkClipOp op; bool antialias; }; class CC_PAINT_EXPORT ConcatOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Concat; explicit ConcatOp(const SkMatrix& matrix) : PaintOp(kType), matrix(matrix) {} static void Raster(const ConcatOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); ThreadsafeMatrix matrix; }; class CC_PAINT_EXPORT CustomDataOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::CustomData; explicit CustomDataOp(uint32_t id) : PaintOp(kType), id(id) {} static void Raster(const CustomDataOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); // Stores user defined id as a placeholder op. uint32_t id; }; class CC_PAINT_EXPORT DrawColorOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::DrawColor; static constexpr bool kIsDrawOp = true; DrawColorOp(SkColor color, SkBlendMode mode) : PaintOp(kType), color(color), mode(mode) {} static void Raster(const DrawColorOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return IsValidDrawColorSkBlendMode(mode); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkColor color; SkBlendMode mode; }; class CC_PAINT_EXPORT DrawDRRectOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawDRRect; static constexpr bool kIsDrawOp = true; DrawDRRectOp(const SkRRect& outer, const SkRRect& inner, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), outer(outer), inner(inner) {} static void RasterWithFlags(const DrawDRRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && outer.isValid() && inner.isValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRRect outer; SkRRect inner; private: DrawDRRectOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawImage; static constexpr bool kIsDrawOp = true; DrawImageOp(const PaintImage& image, SkScalar left, SkScalar top, const PaintFlags* flags); ~DrawImageOp(); static void RasterWithFlags(const DrawImageOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); bool HasDiscardableImages() const; bool HasNonAAPaint() const { return false; } HAS_SERIALIZATION_FUNCTIONS(); PaintImage image; SkScalar left; SkScalar top; private: DrawImageOp(); }; class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawImageRect; static constexpr bool kIsDrawOp = true; DrawImageRectOp(const PaintImage& image, const SkRect& src, const SkRect& dst, const PaintFlags* flags, PaintCanvas::SrcRectConstraint constraint); ~DrawImageRectOp(); static void RasterWithFlags(const DrawImageRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && src.isFinite() && dst.isFinite(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); bool HasDiscardableImages() const; HAS_SERIALIZATION_FUNCTIONS(); PaintImage image; SkRect src; SkRect dst; PaintCanvas::SrcRectConstraint constraint; private: DrawImageRectOp(); }; class CC_PAINT_EXPORT DrawIRectOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawIRect; static constexpr bool kIsDrawOp = true; DrawIRectOp(const SkIRect& rect, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), rect(rect) {} static void RasterWithFlags(const DrawIRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); bool HasNonAAPaint() const { return false; } HAS_SERIALIZATION_FUNCTIONS(); SkIRect rect; private: DrawIRectOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawLineOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawLine; static constexpr bool kIsDrawOp = true; DrawLineOp(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), x0(x0), y0(y0), x1(x1), y1(y1) {} static void RasterWithFlags(const DrawLineOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); int CountSlowPaths() const; SkScalar x0; SkScalar y0; SkScalar x1; SkScalar y1; private: DrawLineOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawOvalOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawOval; static constexpr bool kIsDrawOp = true; DrawOvalOp(const SkRect& oval, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), oval(oval) {} static void RasterWithFlags(const DrawOvalOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && oval.isFinite(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRect oval; private: DrawOvalOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawPath; static constexpr bool kIsDrawOp = true; DrawPathOp(const SkPath& path, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), path(path) {} static void RasterWithFlags(const DrawPathOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && IsValidPath(path); } static bool AreEqual(const PaintOp* left, const PaintOp* right); int CountSlowPaths() const; HAS_SERIALIZATION_FUNCTIONS(); ThreadsafePath path; private: DrawPathOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::DrawRecord; static constexpr bool kIsDrawOp = true; explicit DrawRecordOp(sk_sp record); ~DrawRecordOp(); static void Raster(const DrawRecordOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); size_t AdditionalBytesUsed() const; size_t AdditionalOpCount() const; bool HasDiscardableImages() const; int CountSlowPaths() const; bool HasNonAAPaint() const; HAS_SERIALIZATION_FUNCTIONS(); sk_sp record; }; class CC_PAINT_EXPORT DrawRectOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawRect; static constexpr bool kIsDrawOp = true; DrawRectOp(const SkRect& rect, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), rect(rect) {} static void RasterWithFlags(const DrawRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && rect.isFinite(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRect rect; private: DrawRectOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawRRectOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawRRect; static constexpr bool kIsDrawOp = true; DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags) : PaintOpWithFlags(kType, flags), rrect(rrect) {} static void RasterWithFlags(const DrawRRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && rrect.isValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRRect rrect; private: DrawRRectOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::DrawTextBlob; static constexpr bool kIsDrawOp = true; DrawTextBlobOp(scoped_refptr blob, SkScalar x, SkScalar y, const PaintFlags& flags); ~DrawTextBlobOp(); static void RasterWithFlags(const DrawTextBlobOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); scoped_refptr blob; SkScalar x; SkScalar y; private: DrawTextBlobOp(); }; class CC_PAINT_EXPORT NoopOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Noop; NoopOp() : PaintOp(kType) {} static void Raster(const NoopOp* op, SkCanvas* canvas, const PlaybackParams& params) {} bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); }; class CC_PAINT_EXPORT RestoreOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Restore; RestoreOp() : PaintOp(kType) {} static void Raster(const RestoreOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); }; class CC_PAINT_EXPORT RotateOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Rotate; explicit RotateOp(SkScalar degrees) : PaintOp(kType), degrees(degrees) {} static void Raster(const RotateOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkScalar degrees; }; class CC_PAINT_EXPORT SaveOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Save; SaveOp() : PaintOp(kType) {} static void Raster(const SaveOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); }; class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags { public: static constexpr PaintOpType kType = PaintOpType::SaveLayer; SaveLayerOp(const SkRect* bounds, const PaintFlags* flags) : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()), bounds(bounds ? *bounds : kUnsetRect) {} static void RasterWithFlags(const SaveLayerOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return flags.IsValid() && IsValidOrUnsetRect(bounds); } static bool AreEqual(const PaintOp* left, const PaintOp* right); bool HasNonAAPaint() const { return false; } HAS_SERIALIZATION_FUNCTIONS(); SkRect bounds; private: SaveLayerOp() : PaintOpWithFlags(kType) {} }; class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha; SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha, bool preserve_lcd_text_requests) : PaintOp(kType), bounds(bounds ? *bounds : kUnsetRect), alpha(alpha), preserve_lcd_text_requests(preserve_lcd_text_requests) {} static void Raster(const SaveLayerAlphaOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return IsValidOrUnsetRect(bounds); } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkRect bounds; uint8_t alpha; bool preserve_lcd_text_requests; }; class CC_PAINT_EXPORT ScaleOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Scale; ScaleOp(SkScalar sx, SkScalar sy) : PaintOp(kType), sx(sx), sy(sy) {} static void Raster(const ScaleOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkScalar sx; SkScalar sy; private: ScaleOp() : PaintOp(kType) {} }; class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::SetMatrix; explicit SetMatrixOp(const SkMatrix& matrix) : PaintOp(kType), matrix(matrix) {} // This is the only op that needs the original ctm of the SkCanvas // used for raster (since SetMatrix is relative to the recording origin and // shouldn't clobber the SkCanvas raster origin). // // TODO(enne): Find some cleaner way to do this, possibly by making // all SetMatrix calls Concat?? static void Raster(const SetMatrixOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); ThreadsafeMatrix matrix; }; class CC_PAINT_EXPORT TranslateOp final : public PaintOp { public: static constexpr PaintOpType kType = PaintOpType::Translate; TranslateOp(SkScalar dx, SkScalar dy) : PaintOp(kType), dx(dx), dy(dy) {} static void Raster(const TranslateOp* op, SkCanvas* canvas, const PlaybackParams& params); bool IsValid() const { return true; } static bool AreEqual(const PaintOp* left, const PaintOp* right); HAS_SERIALIZATION_FUNCTIONS(); SkScalar dx; SkScalar dy; }; #undef HAS_SERIALIZATION_FUNCTIONS // TODO(vmpstr): Revisit this when sizes of DrawImageRectOp change. using LargestPaintOp = typename std::conditional<(sizeof(DrawImageRectOp) > sizeof(DrawDRRectOp)), DrawImageRectOp, DrawDRRectOp>::type; class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { public: enum { kInitialBufferSize = 4096 }; // It's not necessarily the case that the op with the maximum alignment // requirements is also the biggest op, but for now that's true. static constexpr size_t PaintOpAlign = alignof(DrawDRRectOp); static inline size_t ComputeOpSkip(size_t sizeof_op) { return MathUtil::UncheckedRoundUp(sizeof_op, PaintOpBuffer::PaintOpAlign); } PaintOpBuffer(); PaintOpBuffer(PaintOpBuffer&& other); ~PaintOpBuffer() override; void operator=(PaintOpBuffer&& other); void Reset(); // Replays the paint op buffer into the canvas. void Playback(SkCanvas* canvas) const; void Playback(SkCanvas* canvas, const PlaybackParams& params) const; static sk_sp MakeFromMemory( const volatile void* input, size_t input_size, const PaintOp::DeserializeOptions& options); // Returns the size of the paint op buffer. That is, the number of ops // contained in it. size_t size() const { return op_count_; } // Returns the number of bytes used by the paint op buffer. size_t bytes_used() const { return sizeof(*this) + reserved_ + subrecord_bytes_used_; } // Returns the total number of ops including sub-records. size_t total_op_count() const { return op_count_ + subrecord_op_count_; } size_t next_op_offset() const { return used_; } int numSlowPaths() const { return num_slow_paths_; } bool HasNonAAPaint() const { return has_non_aa_paint_; } bool HasDiscardableImages() const { return has_discardable_images_; } bool operator==(const PaintOpBuffer& other) const; bool operator!=(const PaintOpBuffer& other) const { return !(*this == other); } // Resize the PaintOpBuffer to exactly fit the current amount of used space. void ShrinkToFit(); const PaintOp* GetFirstOp() const { return reinterpret_cast(data_.get()); } template void push(Args&&... args) { static_assert(std::is_convertible::value, "T not a PaintOp."); static_assert(alignof(T) <= PaintOpAlign, ""); size_t skip = ComputeOpSkip(sizeof(T)); T* op = reinterpret_cast(AllocatePaintOp(skip)); new (op) T{std::forward(args)...}; DCHECK_EQ(op->type, static_cast(T::kType)); op->skip = skip; AnalyzeAddedOp(op); } void UpdateSaveLayerBounds(size_t offset, const SkRect& bounds) { CHECK_LT(offset, used_); CHECK_LE(offset + sizeof(PaintOp), used_); auto* op = reinterpret_cast(data_.get() + offset); switch (op->GetType()) { case SaveLayerOp::kType: CHECK_LE(offset + sizeof(SaveLayerOp), used_); static_cast(op)->bounds = bounds; break; case SaveLayerAlphaOp::kType: CHECK_LE(offset + sizeof(SaveLayerAlphaOp), used_); static_cast(op)->bounds = bounds; break; default: NOTREACHED(); } } template void AnalyzeAddedOp(const T* op) { static_assert(!std::is_same::value, "AnalyzeAddedOp needs a subtype of PaintOp"); num_slow_paths_ += op->CountSlowPathsFromFlags(); num_slow_paths_ += op->CountSlowPaths(); has_non_aa_paint_ |= op->HasNonAAPaint(); has_discardable_images_ |= op->HasDiscardableImages(); has_discardable_images_ |= op->HasDiscardableImagesFromFlags(); subrecord_bytes_used_ += op->AdditionalBytesUsed(); subrecord_op_count_ += op->AdditionalOpCount(); } template const T* GetOpAtForTesting(size_t index) const { size_t i = 0; for (PaintOpBuffer::Iterator it(this); it && i <= index; ++it, ++i) { if (i == index && (*it)->GetType() == T::kType) return static_cast(*it); } return nullptr; } class CC_PAINT_EXPORT Iterator { public: explicit Iterator(const PaintOpBuffer* buffer) : Iterator(buffer, buffer->data_.get(), 0u) {} PaintOp* operator->() const { return reinterpret_cast(ptr_); } PaintOp* operator*() const { return operator->(); } Iterator begin() { return Iterator(buffer_); } Iterator end() { return Iterator(buffer_, buffer_->data_.get() + buffer_->used_, buffer_->used_); } bool operator!=(const Iterator& other) { // Not valid to compare iterators on different buffers. DCHECK_EQ(other.buffer_, buffer_); return other.op_offset_ != op_offset_; } Iterator& operator++() { DCHECK(*this); const PaintOp* op = **this; ptr_ += op->skip; op_offset_ += op->skip; CHECK_LE(op_offset_, buffer_->used_); return *this; } operator bool() const { return op_offset_ < buffer_->used_; } private: Iterator(const PaintOpBuffer* buffer, char* ptr, size_t op_offset) : buffer_(buffer), ptr_(ptr), op_offset_(op_offset) {} const PaintOpBuffer* buffer_ = nullptr; char* ptr_ = nullptr; size_t op_offset_ = 0; }; class CC_PAINT_EXPORT OffsetIterator { public: // Offsets and paint op buffer must come from the same DisplayItemList. OffsetIterator(const PaintOpBuffer* buffer, const std::vector* offsets) : buffer_(buffer), ptr_(buffer_->data_.get()), offsets_(offsets) { if (!offsets || offsets->empty()) { *this = end(); return; } op_offset_ = (*offsets)[0]; ptr_ += op_offset_; } PaintOp* operator->() const { return reinterpret_cast(ptr_); } PaintOp* operator*() const { return operator->(); } OffsetIterator begin() { return OffsetIterator(buffer_, offsets_); } OffsetIterator end() { return OffsetIterator(buffer_, buffer_->data_.get() + buffer_->used_, buffer_->used_, offsets_); } bool operator!=(const OffsetIterator& other) { // Not valid to compare iterators on different buffers. DCHECK_EQ(other.buffer_, buffer_); return other.op_offset_ != op_offset_; } OffsetIterator& operator++() { if (++offsets_index_ >= offsets_->size()) { *this = end(); return *this; } size_t target_offset = (*offsets_)[offsets_index_]; // Sanity checks. CHECK_GE(target_offset, op_offset_); // Debugging crbug.com/738182. base::debug::Alias(&target_offset); CHECK_LT(target_offset, buffer_->used_); // Advance the iterator to the target offset. ptr_ += (target_offset - op_offset_); op_offset_ = target_offset; DCHECK(!*this || (*this)->type <= static_cast(PaintOpType::LastPaintOpType)); return *this; } operator bool() const { return op_offset_ < buffer_->used_; } private: OffsetIterator(const PaintOpBuffer* buffer, char* ptr, size_t op_offset, const std::vector* offsets) : buffer_(buffer), ptr_(ptr), offsets_(offsets), op_offset_(op_offset) {} const PaintOpBuffer* buffer_ = nullptr; char* ptr_ = nullptr; const std::vector* offsets_; size_t op_offset_ = 0; size_t offsets_index_ = 0; }; class CC_PAINT_EXPORT CompositeIterator { public: // Offsets and paint op buffer must come from the same DisplayItemList. CompositeIterator(const PaintOpBuffer* buffer, const std::vector* offsets); CompositeIterator(const CompositeIterator& other); CompositeIterator(CompositeIterator&& other); PaintOp* operator->() const { return using_offsets_ ? **offset_iter_ : **iter_; } PaintOp* operator*() const { return using_offsets_ ? **offset_iter_ : **iter_; } bool operator==(const CompositeIterator& other) { if (using_offsets_ != other.using_offsets_) return false; return using_offsets_ ? (*offset_iter_ == *other.offset_iter_) : (*iter_ == *other.iter_); } bool operator!=(const CompositeIterator& other) { return !(*this == other); } CompositeIterator& operator++() { if (using_offsets_) ++*offset_iter_; else ++*iter_; return *this; } operator bool() const { return using_offsets_ ? !!*offset_iter_ : !!*iter_; } private: bool using_offsets_ = false; base::Optional offset_iter_; base::Optional iter_; }; class PlaybackFoldingIterator { public: PlaybackFoldingIterator(const PaintOpBuffer* buffer, const std::vector* offsets); ~PlaybackFoldingIterator(); const PaintOp* operator->() const { return current_op_; } const PaintOp* operator*() const { return current_op_; } PlaybackFoldingIterator& operator++() { FindNextOp(); return *this; } operator bool() const { return !!current_op_; } // Guaranteed to be 255 for all ops without flags. uint8_t alpha() const { return current_alpha_; } private: void FindNextOp(); const PaintOp* NextUnfoldedOp(); PaintOpBuffer::CompositeIterator iter_; // FIFO queue of paint ops that have been peeked at. base::StackVector stack_; DrawColorOp folded_draw_color_; const PaintOp* current_op_ = nullptr; uint8_t current_alpha_ = 255; }; private: friend class DisplayItemList; friend class PaintOpBufferOffsetsTest; friend class SolidColorAnalyzer; // Replays the paint op buffer into the canvas. If |indices| is specified, it // contains indices in an increasing order and only the indices specified in // the vector will be replayed. void Playback(SkCanvas* canvas, const PlaybackParams& params, const std::vector* indices) const; void ReallocBuffer(size_t new_size); // Returns the allocated op. void* AllocatePaintOp(size_t skip); std::unique_ptr data_; size_t used_ = 0; size_t reserved_ = 0; size_t op_count_ = 0; // Record paths for veto-to-msaa for gpu raster. int num_slow_paths_ = 0; // Record additional bytes used by referenced sub-records and display lists. size_t subrecord_bytes_used_ = 0; // Record total op count of referenced sub-record and display lists. size_t subrecord_op_count_ = 0; bool has_non_aa_paint_ : 1; bool has_discardable_images_ : 1; DISALLOW_COPY_AND_ASSIGN(PaintOpBuffer); }; } // namespace cc #endif // CC_PAINT_PAINT_OP_BUFFER_H_