| // 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_op_buffer.h" |
| |
| #include "cc/paint/decoded_draw_image.h" |
| #include "cc/paint/display_item_list.h" |
| #include "cc/paint/image_provider.h" |
| #include "cc/paint/paint_image_builder.h" |
| #include "cc/paint/paint_op_reader.h" |
| #include "cc/paint/paint_op_writer.h" |
| #include "cc/paint/paint_record.h" |
| #include "cc/paint/scoped_raster_flags.h" |
| #include "third_party/skia/include/core/SkAnnotation.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkRegion.h" |
| #include "third_party/skia/include/core/SkSerialProcs.h" |
| #include "third_party/skia/include/gpu/GrContext.h" |
| |
| namespace cc { |
| namespace { |
| DrawImage CreateDrawImage(const PaintImage& image, |
| const PaintFlags* flags, |
| const SkMatrix& matrix) { |
| if (!image) |
| return DrawImage(); |
| return DrawImage(image, SkIRect::MakeWH(image.width(), image.height()), |
| flags ? flags->getFilterQuality() : kLow_SkFilterQuality, |
| matrix); |
| } |
| |
| bool IsScaleAdjustmentIdentity(const SkSize& scale_adjustment) { |
| return std::abs(scale_adjustment.width() - 1.f) < FLT_EPSILON && |
| std::abs(scale_adjustment.height() - 1.f) < FLT_EPSILON; |
| } |
| |
| SkRect AdjustSrcRectForScale(SkRect original, SkSize scale_adjustment) { |
| if (IsScaleAdjustmentIdentity(scale_adjustment)) |
| return original; |
| |
| float x_scale = scale_adjustment.width(); |
| float y_scale = scale_adjustment.height(); |
| return SkRect::MakeXYWH(original.x() * x_scale, original.y() * y_scale, |
| original.width() * x_scale, |
| original.height() * y_scale); |
| } |
| } // namespace |
| |
| #define TYPES(M) \ |
| M(AnnotateOp) \ |
| M(ClipPathOp) \ |
| M(ClipRectOp) \ |
| M(ClipRRectOp) \ |
| M(ConcatOp) \ |
| M(CustomDataOp) \ |
| M(DrawColorOp) \ |
| M(DrawDRRectOp) \ |
| M(DrawImageOp) \ |
| M(DrawImageRectOp) \ |
| M(DrawIRectOp) \ |
| M(DrawLineOp) \ |
| M(DrawOvalOp) \ |
| M(DrawPathOp) \ |
| M(DrawRecordOp) \ |
| M(DrawRectOp) \ |
| M(DrawRRectOp) \ |
| M(DrawSkottieOp) \ |
| M(DrawTextBlobOp) \ |
| M(NoopOp) \ |
| M(RestoreOp) \ |
| M(RotateOp) \ |
| M(SaveOp) \ |
| M(SaveLayerOp) \ |
| M(SaveLayerAlphaOp) \ |
| M(ScaleOp) \ |
| M(SetMatrixOp) \ |
| M(TranslateOp) |
| |
| static constexpr size_t kNumOpTypes = |
| static_cast<size_t>(PaintOpType::LastPaintOpType) + 1; |
| |
| // Verify that every op is in the TYPES macro. |
| #define M(T) +1 |
| static_assert(kNumOpTypes == TYPES(M), "Missing op in list"); |
| #undef M |
| |
| #define M(T) sizeof(T), |
| static const size_t g_type_to_size[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| template <typename T, bool HasFlags> |
| struct Rasterizer { |
| static void RasterWithFlags(const T* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| static_assert( |
| !T::kHasPaintFlags, |
| "This function should not be used for a PaintOp that has PaintFlags"); |
| DCHECK(op->IsValid()); |
| NOTREACHED(); |
| } |
| static void Raster(const T* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| static_assert( |
| !T::kHasPaintFlags, |
| "This function should not be used for a PaintOp that has PaintFlags"); |
| DCHECK(op->IsValid()); |
| T::Raster(op, canvas, params); |
| } |
| }; |
| |
| template <typename T> |
| struct Rasterizer<T, true> { |
| static void RasterWithFlags(const T* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| static_assert(T::kHasPaintFlags, |
| "This function expects the PaintOp to have PaintFlags"); |
| DCHECK(op->IsValid()); |
| T::RasterWithFlags(op, flags, canvas, params); |
| } |
| |
| static void Raster(const T* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| static_assert(T::kHasPaintFlags, |
| "This function expects the PaintOp to have PaintFlags"); |
| DCHECK(op->IsValid()); |
| T::RasterWithFlags(op, &op->flags, canvas, params); |
| } |
| }; |
| |
| using RasterFunction = void (*)(const PaintOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params); |
| #define M(T) \ |
| [](const PaintOp* op, SkCanvas* canvas, const PlaybackParams& params) { \ |
| Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op), \ |
| canvas, params); \ |
| }, |
| static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| using RasterWithFlagsFunction = void (*)(const PaintOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params); |
| #define M(T) \ |
| [](const PaintOp* op, const PaintFlags* flags, SkCanvas* canvas, \ |
| const PlaybackParams& params) { \ |
| Rasterizer<T, T::kHasPaintFlags>::RasterWithFlags( \ |
| static_cast<const T*>(op), flags, canvas, params); \ |
| }, |
| static const RasterWithFlagsFunction |
| g_raster_with_flags_functions[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| using SerializeFunction = size_t (*)(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const PaintOp::SerializeOptions& options); |
| #define M(T) &T::Serialize, |
| static const SerializeFunction g_serialize_functions[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| using DeserializeFunction = |
| PaintOp* (*)(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const PaintOp::DeserializeOptions& options); |
| |
| #define M(T) &T::Deserialize, |
| static const DeserializeFunction g_deserialize_functions[kNumOpTypes] = { |
| TYPES(M)}; |
| #undef M |
| |
| using EqualsFunction = bool (*)(const PaintOp* left, const PaintOp* right); |
| #define M(T) &T::AreEqual, |
| static const EqualsFunction g_equals_operator[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| // Most state ops (matrix, clip, save, restore) have a trivial destructor. |
| // TODO(enne): evaluate if we need the nullptr optimization or if |
| // we even need to differentiate trivial destructors here. |
| using VoidFunction = void (*)(PaintOp* op); |
| #define M(T) \ |
| !std::is_trivially_destructible<T>::value \ |
| ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \ |
| : static_cast<VoidFunction>(nullptr), |
| static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| #define M(T) T::kIsDrawOp, |
| static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| #define M(T) T::kHasPaintFlags, |
| static bool g_has_paint_flags[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| #define M(T) \ |
| static_assert(sizeof(T) <= sizeof(LargestPaintOp), \ |
| #T " must be no bigger than LargestPaintOp"); |
| TYPES(M) |
| #undef M |
| |
| #define M(T) \ |
| static_assert(alignof(T) <= PaintOpBuffer::PaintOpAlign, \ |
| #T " must have alignment no bigger than PaintOpAlign"); |
| TYPES(M) |
| #undef M |
| |
| using AnalyzeOpFunc = void (*)(PaintOpBuffer*, const PaintOp*); |
| #define M(T) \ |
| [](PaintOpBuffer* buffer, const PaintOp* op) { \ |
| buffer->AnalyzeAddedOp(static_cast<const T*>(op)); \ |
| }, |
| static const AnalyzeOpFunc g_analyze_op_functions[kNumOpTypes] = {TYPES(M)}; |
| #undef M |
| |
| #undef TYPES |
| |
| const SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0}; |
| const size_t PaintOp::kMaxSkip; |
| |
| std::string PaintOpTypeToString(PaintOpType type) { |
| switch (type) { |
| case PaintOpType::Annotate: |
| return "Annotate"; |
| case PaintOpType::ClipPath: |
| return "ClipPath"; |
| case PaintOpType::ClipRect: |
| return "ClipRect"; |
| case PaintOpType::ClipRRect: |
| return "ClipRRect"; |
| case PaintOpType::Concat: |
| return "Concat"; |
| case PaintOpType::CustomData: |
| return "CustomData"; |
| case PaintOpType::DrawColor: |
| return "DrawColor"; |
| case PaintOpType::DrawDRRect: |
| return "DrawDRRect"; |
| case PaintOpType::DrawImage: |
| return "DrawImage"; |
| case PaintOpType::DrawImageRect: |
| return "DrawImageRect"; |
| case PaintOpType::DrawIRect: |
| return "DrawIRect"; |
| case PaintOpType::DrawLine: |
| return "DrawLine"; |
| case PaintOpType::DrawOval: |
| return "DrawOval"; |
| case PaintOpType::DrawPath: |
| return "DrawPath"; |
| case PaintOpType::DrawRecord: |
| return "DrawRecord"; |
| case PaintOpType::DrawRect: |
| return "DrawRect"; |
| case PaintOpType::DrawRRect: |
| return "DrawRRect"; |
| case PaintOpType::DrawSkottie: |
| return "DrawSkottie"; |
| case PaintOpType::DrawTextBlob: |
| return "DrawTextBlob"; |
| case PaintOpType::Noop: |
| return "Noop"; |
| case PaintOpType::Restore: |
| return "Restore"; |
| case PaintOpType::Rotate: |
| return "Rotate"; |
| case PaintOpType::Save: |
| return "Save"; |
| case PaintOpType::SaveLayer: |
| return "SaveLayer"; |
| case PaintOpType::SaveLayerAlpha: |
| return "SaveLayerAlpha"; |
| case PaintOpType::Scale: |
| return "Scale"; |
| case PaintOpType::SetMatrix: |
| return "SetMatrix"; |
| case PaintOpType::Translate: |
| return "Translate"; |
| } |
| return "UNKNOWN"; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, PaintOpType type) { |
| return os << PaintOpTypeToString(type); |
| } |
| |
| template <typename T> |
| size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) { |
| if (sizeof(T) > size) |
| return 0; |
| memcpy(memory, op, sizeof(T)); |
| return sizeof(T); |
| } |
| |
| PlaybackParams::PlaybackParams(ImageProvider* image_provider) |
| : image_provider(image_provider), |
| original_ctm(SkMatrix::I()), |
| custom_callback(CustomDataRasterCallback()), |
| did_draw_op_callback(DidDrawOpCallback()) {} |
| |
| PlaybackParams::PlaybackParams(ImageProvider* image_provider, |
| const SkMatrix& original_ctm, |
| CustomDataRasterCallback custom_callback, |
| DidDrawOpCallback did_draw_op_callback) |
| : image_provider(image_provider), |
| original_ctm(original_ctm), |
| custom_callback(custom_callback), |
| did_draw_op_callback(did_draw_op_callback) {} |
| |
| PlaybackParams::~PlaybackParams() {} |
| |
| PlaybackParams::PlaybackParams(const PlaybackParams& other) = default; |
| PlaybackParams& PlaybackParams::operator=(const PlaybackParams& other) = |
| default; |
| |
| PaintOp::SerializeOptions::SerializeOptions( |
| ImageProvider* image_provider, |
| TransferCacheSerializeHelper* transfer_cache, |
| ClientPaintCache* paint_cache, |
| SkCanvas* canvas, |
| SkStrikeServer* strike_server, |
| sk_sp<SkColorSpace> color_space, |
| bool can_use_lcd_text, |
| bool context_supports_distance_field_text, |
| int max_texture_size, |
| size_t max_texture_bytes, |
| const SkMatrix& original_ctm) |
| : image_provider(image_provider), |
| transfer_cache(transfer_cache), |
| paint_cache(paint_cache), |
| canvas(canvas), |
| strike_server(strike_server), |
| color_space(std::move(color_space)), |
| can_use_lcd_text(can_use_lcd_text), |
| context_supports_distance_field_text( |
| context_supports_distance_field_text), |
| max_texture_size(max_texture_size), |
| max_texture_bytes(max_texture_bytes), |
| original_ctm(original_ctm) {} |
| |
| PaintOp::SerializeOptions::SerializeOptions(const SerializeOptions&) = default; |
| PaintOp::SerializeOptions& PaintOp::SerializeOptions::operator=( |
| const SerializeOptions&) = default; |
| PaintOp::SerializeOptions::~SerializeOptions() = default; |
| |
| PaintOp::DeserializeOptions::DeserializeOptions( |
| TransferCacheDeserializeHelper* transfer_cache, |
| ServicePaintCache* paint_cache, |
| SkStrikeClient* strike_client, |
| std::vector<uint8_t>* scratch_buffer) |
| : transfer_cache(transfer_cache), |
| paint_cache(paint_cache), |
| strike_client(strike_client), |
| scratch_buffer(scratch_buffer) { |
| DCHECK(scratch_buffer); |
| } |
| |
| size_t AnnotateOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const AnnotateOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| helper.Write(op->annotation_type); |
| helper.Write(op->rect); |
| helper.Write(op->data); |
| return helper.size(); |
| } |
| |
| size_t ClipPathOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const ClipPathOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| helper.Write(op->path); |
| helper.Write(op->op); |
| helper.Write(op->antialias); |
| return helper.size(); |
| } |
| |
| size_t ClipRectOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<ClipRectOp>(op, memory, size); |
| } |
| |
| size_t ClipRRectOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<ClipRRectOp>(op, memory, size); |
| } |
| |
| size_t ConcatOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<ConcatOp>(op, memory, size); |
| } |
| |
| size_t CustomDataOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<CustomDataOp>(op, memory, size); |
| } |
| |
| size_t DrawColorOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<DrawColorOp>(op, memory, size); |
| } |
| |
| size_t DrawDRRectOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawDRRectOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->outer); |
| helper.Write(op->inner); |
| return helper.size(); |
| } |
| |
| size_t DrawImageOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawImageOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| |
| SkSize scale_adjustment = SkSize::Make(1.f, 1.f); |
| helper.Write(CreateDrawImage(op->image, serialized_flags, |
| options.canvas->getTotalMatrix()), |
| &scale_adjustment); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Write(scale_adjustment.width()); |
| helper.Write(scale_adjustment.height()); |
| |
| helper.Write(op->left); |
| helper.Write(op->top); |
| return helper.size(); |
| } |
| |
| size_t DrawImageRectOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawImageRectOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| |
| // This adjustment mirrors DiscardableImageMap::GatherDiscardableImage logic. |
| SkMatrix matrix = options.canvas->getTotalMatrix(); |
| matrix.postConcat( |
| SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit)); |
| // Note that we don't request subsets here since the GpuImageCache has no |
| // optimizations for using subsets. |
| SkSize scale_adjustment = SkSize::Make(1.f, 1.f); |
| helper.Write(CreateDrawImage(op->image, serialized_flags, matrix), |
| &scale_adjustment); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Write(scale_adjustment.width()); |
| helper.Write(scale_adjustment.height()); |
| |
| helper.Write(op->src); |
| helper.Write(op->dst); |
| helper.Write(op->constraint); |
| return helper.size(); |
| } |
| |
| size_t DrawIRectOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawIRectOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->rect); |
| return helper.size(); |
| } |
| |
| size_t DrawLineOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawLineOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Write(op->x0); |
| helper.Write(op->y0); |
| helper.Write(op->x1); |
| helper.Write(op->y1); |
| return helper.size(); |
| } |
| |
| size_t DrawOvalOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawOvalOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->oval); |
| return helper.size(); |
| } |
| |
| size_t DrawPathOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawPathOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->path); |
| return helper.size(); |
| } |
| |
| size_t DrawRecordOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| // TODO(enne): these must be flattened. Serializing this will not do |
| // anything. |
| NOTREACHED(); |
| return 0u; |
| } |
| |
| size_t DrawRectOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawRectOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->rect); |
| return helper.size(); |
| } |
| |
| size_t DrawRRectOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawRRectOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->rrect); |
| return helper.size(); |
| } |
| |
| size_t DrawSkottieOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| // TODO(malaykeshav): these must be flattened. Serializing this will not do |
| // anything. See https://crbug.com/894635 |
| NOTREACHED(); |
| return 0u; |
| } |
| |
| size_t DrawTextBlobOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const DrawTextBlobOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Write(op->x); |
| helper.Write(op->y); |
| helper.Write(op->blob); |
| return helper.size(); |
| } |
| |
| size_t NoopOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<NoopOp>(op, memory, size); |
| } |
| |
| size_t RestoreOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<RestoreOp>(op, memory, size); |
| } |
| |
| size_t RotateOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<RotateOp>(op, memory, size); |
| } |
| |
| size_t SaveOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<SaveOp>(op, memory, size); |
| } |
| |
| size_t SaveLayerOp::Serialize(const PaintOp* base_op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| auto* op = static_cast<const SaveLayerOp*>(base_op); |
| PaintOpWriter helper(memory, size, options); |
| const auto* serialized_flags = options.flags_to_serialize; |
| if (!serialized_flags) |
| serialized_flags = &op->flags; |
| helper.Write(*serialized_flags); |
| helper.Write(op->bounds); |
| return helper.size(); |
| } |
| |
| size_t SaveLayerAlphaOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<SaveLayerAlphaOp>(op, memory, size); |
| } |
| |
| size_t ScaleOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<ScaleOp>(op, memory, size); |
| } |
| |
| size_t SetMatrixOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| if (options.original_ctm.isIdentity()) |
| return SimpleSerialize<SetMatrixOp>(op, memory, size); |
| |
| SetMatrixOp transformed(*static_cast<const SetMatrixOp*>(op)); |
| transformed.matrix.postConcat(options.original_ctm); |
| return SimpleSerialize<SetMatrixOp>(&transformed, memory, size); |
| } |
| |
| size_t TranslateOp::Serialize(const PaintOp* op, |
| void* memory, |
| size_t size, |
| const SerializeOptions& options) { |
| return SimpleSerialize<TranslateOp>(op, memory, size); |
| } |
| |
| template <typename T> |
| void UpdateTypeAndSkip(T* op) { |
| op->type = static_cast<uint8_t>(T::kType); |
| op->skip = PaintOpBuffer::ComputeOpSkip(sizeof(T)); |
| } |
| |
| template <typename T> |
| T* SimpleDeserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size) { |
| if (input_size < sizeof(T)) |
| return nullptr; |
| memcpy(output, const_cast<void*>(input), sizeof(T)); |
| |
| T* op = reinterpret_cast<T*>(output); |
| if (!op->IsValid()) |
| return nullptr; |
| // Type and skip were already read once, so could have been changed. |
| // Don't trust them and clobber them with something valid. |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* AnnotateOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(AnnotateOp)); |
| AnnotateOp* op = new (output) AnnotateOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->annotation_type); |
| helper.Read(&op->rect); |
| helper.Read(&op->data); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~AnnotateOp(); |
| return nullptr; |
| } |
| |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* ClipPathOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(ClipPathOp)); |
| ClipPathOp* op = new (output) ClipPathOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->path); |
| helper.Read(&op->op); |
| helper.Read(&op->antialias); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~ClipPathOp(); |
| return nullptr; |
| } |
| |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* ClipRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(ClipRectOp)); |
| return SimpleDeserialize<ClipRectOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* ClipRRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(ClipRRectOp)); |
| return SimpleDeserialize<ClipRRectOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* ConcatOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(ConcatOp)); |
| auto* op = |
| SimpleDeserialize<ConcatOp>(input, input_size, output, output_size); |
| if (op) |
| PaintOpReader::FixupMatrixPostSerialization(&op->matrix); |
| return op; |
| } |
| |
| PaintOp* CustomDataOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(CustomDataOp)); |
| return SimpleDeserialize<CustomDataOp>(input, input_size, output, |
| output_size); |
| } |
| |
| PaintOp* DrawColorOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawColorOp)); |
| return SimpleDeserialize<DrawColorOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* DrawDRRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawDRRectOp)); |
| DrawDRRectOp* op = new (output) DrawDRRectOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->outer); |
| helper.Read(&op->inner); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawDRRectOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawImageOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawImageOp)); |
| DrawImageOp* op = new (output) DrawImageOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| |
| helper.Read(&op->image); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Read(&op->scale_adjustment.fWidth); |
| helper.Read(&op->scale_adjustment.fHeight); |
| |
| helper.Read(&op->left); |
| helper.Read(&op->top); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawImageOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawImageRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawImageRectOp)); |
| DrawImageRectOp* op = new (output) DrawImageRectOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| |
| helper.Read(&op->image); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Read(&op->scale_adjustment.fWidth); |
| helper.Read(&op->scale_adjustment.fHeight); |
| |
| helper.Read(&op->src); |
| helper.Read(&op->dst); |
| helper.Read(&op->constraint); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawImageRectOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawIRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawIRectOp)); |
| DrawIRectOp* op = new (output) DrawIRectOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->rect); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawIRectOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawLineOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawLineOp)); |
| DrawLineOp* op = new (output) DrawLineOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Read(&op->x0); |
| helper.Read(&op->y0); |
| helper.Read(&op->x1); |
| helper.Read(&op->y1); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawLineOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawOvalOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawOvalOp)); |
| DrawOvalOp* op = new (output) DrawOvalOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->oval); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawOvalOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawPathOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawPathOp)); |
| DrawPathOp* op = new (output) DrawPathOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->path); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawPathOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawRecordOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| // TODO(enne): these must be flattened and not sent directly. |
| // TODO(enne): could also consider caching these service side. |
| return nullptr; |
| } |
| |
| PaintOp* DrawRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawRectOp)); |
| DrawRectOp* op = new (output) DrawRectOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->rect); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawRectOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawRRectOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawRRectOp)); |
| DrawRRectOp* op = new (output) DrawRRectOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->rrect); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawRRectOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* DrawSkottieOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| // TODO(malaykeshav): these must be flattened and not sent directly. |
| // See https://crbug.com/894635 |
| return nullptr; |
| } |
| |
| PaintOp* DrawTextBlobOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(DrawTextBlobOp) - sizeof(NodeId)); |
| DrawTextBlobOp* op = new (output) DrawTextBlobOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.AlignMemory(alignof(SkScalar)); |
| helper.Read(&op->x); |
| helper.Read(&op->y); |
| helper.Read(&op->blob); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~DrawTextBlobOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* NoopOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(NoopOp)); |
| return SimpleDeserialize<NoopOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* RestoreOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(RestoreOp)); |
| return SimpleDeserialize<RestoreOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* RotateOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(RotateOp)); |
| return SimpleDeserialize<RotateOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* SaveOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(SaveOp)); |
| return SimpleDeserialize<SaveOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* SaveLayerOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(SaveLayerOp)); |
| SaveLayerOp* op = new (output) SaveLayerOp; |
| |
| PaintOpReader helper(input, input_size, options); |
| helper.Read(&op->flags); |
| helper.Read(&op->bounds); |
| if (!helper.valid() || !op->IsValid()) { |
| op->~SaveLayerOp(); |
| return nullptr; |
| } |
| UpdateTypeAndSkip(op); |
| return op; |
| } |
| |
| PaintOp* SaveLayerAlphaOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(SaveLayerAlphaOp)); |
| return SimpleDeserialize<SaveLayerAlphaOp>(input, input_size, output, |
| output_size); |
| } |
| |
| PaintOp* ScaleOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(ScaleOp)); |
| |
| return SimpleDeserialize<ScaleOp>(input, input_size, output, output_size); |
| } |
| |
| PaintOp* SetMatrixOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(SetMatrixOp)); |
| auto* op = |
| SimpleDeserialize<SetMatrixOp>(input, input_size, output, output_size); |
| if (op) |
| PaintOpReader::FixupMatrixPostSerialization(&op->matrix); |
| return op; |
| } |
| |
| PaintOp* TranslateOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(TranslateOp)); |
| return SimpleDeserialize<TranslateOp>(input, input_size, output, output_size); |
| } |
| |
| void AnnotateOp::Raster(const AnnotateOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| switch (op->annotation_type) { |
| case PaintCanvas::AnnotationType::URL: |
| SkAnnotateRectWithURL(canvas, op->rect, op->data.get()); |
| break; |
| case PaintCanvas::AnnotationType::LINK_TO_DESTINATION: |
| SkAnnotateLinkToDestination(canvas, op->rect, op->data.get()); |
| break; |
| case PaintCanvas::AnnotationType::NAMED_DESTINATION: { |
| SkPoint point = SkPoint::Make(op->rect.x(), op->rect.y()); |
| SkAnnotateNamedDestination(canvas, point, op->data.get()); |
| break; |
| } |
| } |
| } |
| |
| void ClipPathOp::Raster(const ClipPathOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->clipPath(op->path, op->op, op->antialias); |
| } |
| |
| void ClipRectOp::Raster(const ClipRectOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->clipRect(op->rect, op->op, op->antialias); |
| } |
| |
| void ClipRRectOp::Raster(const ClipRRectOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->clipRRect(op->rrect, op->op, op->antialias); |
| } |
| |
| void ConcatOp::Raster(const ConcatOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->concat(op->matrix); |
| } |
| |
| void CustomDataOp::Raster(const CustomDataOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| if (params.custom_callback) |
| params.custom_callback.Run(canvas, op->id); |
| } |
| |
| void DrawColorOp::Raster(const DrawColorOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->drawColor(op->color, op->mode); |
| } |
| |
| void DrawDRRectOp::RasterWithFlags(const DrawDRRectOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawDRRect(op->outer, op->inner, p); |
| }); |
| } |
| |
| void DrawImageOp::RasterWithFlags(const DrawImageOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| DCHECK(!op->image.IsPaintWorklet()); |
| SkPaint paint = flags ? flags->ToSkPaint() : SkPaint(); |
| |
| if (!params.image_provider) { |
| const bool needs_scale = !IsScaleAdjustmentIdentity(op->scale_adjustment); |
| SkAutoCanvasRestore save_restore(canvas, needs_scale); |
| if (needs_scale) { |
| canvas->scale(1.f / op->scale_adjustment.width(), |
| 1.f / op->scale_adjustment.height()); |
| } |
| canvas->drawImage(op->image.GetSkImage().get(), op->left, op->top, &paint); |
| return; |
| } |
| |
| DrawImage draw_image( |
| op->image, SkIRect::MakeWH(op->image.width(), op->image.height()), |
| flags ? flags->getFilterQuality() : kNone_SkFilterQuality, |
| canvas->getTotalMatrix()); |
| auto scoped_result = params.image_provider->GetRasterContent(draw_image); |
| if (!scoped_result) |
| return; |
| |
| const auto& decoded_image = scoped_result.decoded_image(); |
| DCHECK(decoded_image.image()); |
| |
| DCHECK_EQ(0, static_cast<int>(decoded_image.src_rect_offset().width())); |
| DCHECK_EQ(0, static_cast<int>(decoded_image.src_rect_offset().height())); |
| SkSize scale_adjustment = SkSize::Make( |
| op->scale_adjustment.width() * decoded_image.scale_adjustment().width(), |
| op->scale_adjustment.height() * |
| decoded_image.scale_adjustment().height()); |
| const bool needs_scale = !IsScaleAdjustmentIdentity(scale_adjustment); |
| SkAutoCanvasRestore save_restore(canvas, needs_scale); |
| if (needs_scale) { |
| canvas->scale(1.f / scale_adjustment.width(), |
| 1.f / scale_adjustment.height()); |
| } |
| paint.setFilterQuality(decoded_image.filter_quality()); |
| canvas->drawImage(decoded_image.image().get(), op->left, op->top, &paint); |
| } |
| |
| void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| SkPaint paint = flags ? flags->ToSkPaint() : SkPaint(); |
| // TODO(crbug.com/931704): make sure to support the case where paint worklet |
| // generated images are used in other raster work such as canvas2d. |
| if (op->image.IsPaintWorklet()) { |
| DCHECK(params.image_provider); |
| ImageProvider::ScopedResult result = |
| params.image_provider->GetRasterContent(DrawImage(op->image)); |
| |
| DCHECK(IsScaleAdjustmentIdentity(op->scale_adjustment)); |
| SkAutoCanvasRestore save_restore(canvas, true); |
| canvas->concat( |
| SkMatrix::MakeRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit)); |
| canvas->clipRect(op->src); |
| canvas->saveLayer(&op->src, &paint); |
| // Compositor thread animations can cause PaintWorklet jobs to be dispatched |
| // to the worklet thread even after main has torn down the worklet (e.g. |
| // because a navigation is happening). In that case the PaintWorklet jobs |
| // will fail and there will be no result to raster here. This state is |
| // transient as the next main frame commit will remove the PaintWorklets. |
| if (result && result.paint_record()) |
| result.paint_record()->Playback(canvas, params); |
| return; |
| } |
| |
| // TODO(enne): Probably PaintCanvas should just use the skia enum directly. |
| SkCanvas::SrcRectConstraint skconstraint = |
| static_cast<SkCanvas::SrcRectConstraint>(op->constraint); |
| |
| if (!params.image_provider) { |
| SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment); |
| canvas->drawImageRect(op->image.GetSkImage().get(), adjusted_src, op->dst, |
| &paint, skconstraint); |
| return; |
| } |
| |
| SkMatrix matrix; |
| matrix.setRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit); |
| matrix.postConcat(canvas->getTotalMatrix()); |
| |
| SkIRect int_src_rect; |
| op->src.roundOut(&int_src_rect); |
| |
| DrawImage draw_image( |
| op->image, int_src_rect, |
| flags ? flags->getFilterQuality() : kNone_SkFilterQuality, matrix); |
| auto scoped_result = params.image_provider->GetRasterContent(draw_image); |
| if (!scoped_result) |
| return; |
| |
| const auto& decoded_image = scoped_result.decoded_image(); |
| DCHECK(decoded_image.image()); |
| |
| SkSize scale_adjustment = SkSize::Make( |
| op->scale_adjustment.width() * decoded_image.scale_adjustment().width(), |
| op->scale_adjustment.height() * |
| decoded_image.scale_adjustment().height()); |
| SkRect adjusted_src = |
| op->src.makeOffset(decoded_image.src_rect_offset().width(), |
| decoded_image.src_rect_offset().height()); |
| adjusted_src = AdjustSrcRectForScale(adjusted_src, scale_adjustment); |
| paint.setFilterQuality(decoded_image.filter_quality()); |
| canvas->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst, |
| &paint, skconstraint); |
| } |
| |
| void DrawIRectOp::RasterWithFlags(const DrawIRectOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawIRect(op->rect, p); |
| }); |
| } |
| |
| void DrawLineOp::RasterWithFlags(const DrawLineOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| SkPaint paint = flags->ToSkPaint(); |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawLine(op->x0, op->y0, op->x1, op->y1, p); |
| }); |
| } |
| |
| void DrawOvalOp::RasterWithFlags(const DrawOvalOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawOval(op->oval, p); |
| }); |
| } |
| |
| void DrawPathOp::RasterWithFlags(const DrawPathOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawPath(op->path, p); |
| }); |
| } |
| |
| void DrawRecordOp::Raster(const DrawRecordOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| // Don't use drawPicture here, as it adds an implicit clip. |
| // TODO(enne): Temporary CHECK debugging for http://crbug.com/823835 |
| CHECK(op->record); |
| op->record->Playback(canvas, params); |
| } |
| |
| void DrawRectOp::RasterWithFlags(const DrawRectOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawRect(op->rect, p); |
| }); |
| } |
| |
| void DrawRRectOp::RasterWithFlags(const DrawRRectOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawRRect(op->rrect, p); |
| }); |
| } |
| |
| void DrawSkottieOp::Raster(const DrawSkottieOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| op->skottie->Draw(canvas, op->t, op->dst); |
| } |
| |
| void DrawTextBlobOp::RasterWithFlags(const DrawTextBlobOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { |
| c->drawTextBlob(op->blob.get(), op->x, op->y, p); |
| }); |
| } |
| |
| void RestoreOp::Raster(const RestoreOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->restore(); |
| } |
| |
| void RotateOp::Raster(const RotateOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->rotate(op->degrees); |
| } |
| |
| void SaveOp::Raster(const SaveOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->save(); |
| } |
| |
| void SaveLayerOp::RasterWithFlags(const SaveLayerOp* op, |
| const PaintFlags* flags, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| // See PaintOp::kUnsetRect |
| SkPaint paint = flags->ToSkPaint(); |
| bool unset = op->bounds.left() == SK_ScalarInfinity; |
| canvas->saveLayer(unset ? nullptr : &op->bounds, &paint); |
| } |
| |
| void SaveLayerAlphaOp::Raster(const SaveLayerAlphaOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| // See PaintOp::kUnsetRect |
| bool unset = op->bounds.left() == SK_ScalarInfinity; |
| canvas->saveLayerAlpha(unset ? nullptr : &op->bounds, op->alpha); |
| } |
| |
| void ScaleOp::Raster(const ScaleOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->scale(op->sx, op->sy); |
| } |
| |
| void SetMatrixOp::Raster(const SetMatrixOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->setMatrix(SkMatrix::Concat(params.original_ctm, op->matrix)); |
| } |
| |
| void TranslateOp::Raster(const TranslateOp* op, |
| SkCanvas* canvas, |
| const PlaybackParams& params) { |
| canvas->translate(op->dx, op->dy); |
| } |
| |
| // static |
| bool PaintOp::AreSkPointsEqual(const SkPoint& left, const SkPoint& right) { |
| if (!AreEqualEvenIfNaN(left.fX, right.fX)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fY, right.fY)) |
| return false; |
| return true; |
| } |
| |
| // static |
| bool PaintOp::AreSkPoint3sEqual(const SkPoint3& left, const SkPoint3& right) { |
| if (!AreEqualEvenIfNaN(left.fX, right.fX)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fY, right.fY)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fZ, right.fZ)) |
| return false; |
| return true; |
| } |
| |
| // static |
| bool PaintOp::AreSkRectsEqual(const SkRect& left, const SkRect& right) { |
| if (!AreEqualEvenIfNaN(left.fLeft, right.fLeft)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fTop, right.fTop)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fRight, right.fRight)) |
| return false; |
| if (!AreEqualEvenIfNaN(left.fBottom, right.fBottom)) |
| return false; |
| return true; |
| } |
| |
| // static |
| bool PaintOp::AreSkRRectsEqual(const SkRRect& left, const SkRRect& right) { |
| char left_buffer[SkRRect::kSizeInMemory]; |
| left.writeToMemory(left_buffer); |
| char right_buffer[SkRRect::kSizeInMemory]; |
| right.writeToMemory(right_buffer); |
| return !memcmp(left_buffer, right_buffer, SkRRect::kSizeInMemory); |
| } |
| |
| // static |
| bool PaintOp::AreSkMatricesEqual(const SkMatrix& left, const SkMatrix& right) { |
| for (int i = 0; i < 9; ++i) { |
| if (!AreEqualEvenIfNaN(left.get(i), right.get(i))) |
| return false; |
| } |
| |
| // If a serialized matrix says it is identity, then the original must have |
| // those values, as the serialization process clobbers the matrix values. |
| if (left.isIdentity()) { |
| if (SkMatrix::I() != left) |
| return false; |
| if (SkMatrix::I() != right) |
| return false; |
| } |
| |
| if (left.getType() != right.getType()) |
| return false; |
| |
| return true; |
| } |
| |
| // static |
| bool PaintOp::AreSkFlattenablesEqual(SkFlattenable* left, |
| SkFlattenable* right) { |
| if (!right || !left) |
| return !right && !left; |
| |
| sk_sp<SkData> left_data = left->serialize(); |
| sk_sp<SkData> right_data = right->serialize(); |
| if (left_data->size() != right_data->size()) |
| return false; |
| if (!left_data->equals(right_data.get())) |
| return false; |
| return true; |
| } |
| |
| bool AnnotateOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const AnnotateOp*>(base_left); |
| auto* right = static_cast<const AnnotateOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->annotation_type != right->annotation_type) |
| return false; |
| if (!AreSkRectsEqual(left->rect, right->rect)) |
| return false; |
| if (!left->data != !right->data) |
| return false; |
| if (left->data) { |
| if (left->data->size() != right->data->size()) |
| return false; |
| if (0 != |
| memcmp(left->data->data(), right->data->data(), right->data->size())) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ClipPathOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const ClipPathOp*>(base_left); |
| auto* right = static_cast<const ClipPathOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->path != right->path) |
| return false; |
| if (left->op != right->op) |
| return false; |
| if (left->antialias != right->antialias) |
| return false; |
| return true; |
| } |
| |
| bool ClipRectOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const ClipRectOp*>(base_left); |
| auto* right = static_cast<const ClipRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreSkRectsEqual(left->rect, right->rect)) |
| return false; |
| if (left->op != right->op) |
| return false; |
| if (left->antialias != right->antialias) |
| return false; |
| return true; |
| } |
| |
| bool ClipRRectOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const ClipRRectOp*>(base_left); |
| auto* right = static_cast<const ClipRRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreSkRRectsEqual(left->rrect, right->rrect)) |
| return false; |
| if (left->op != right->op) |
| return false; |
| if (left->antialias != right->antialias) |
| return false; |
| return true; |
| } |
| |
| bool ConcatOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const ConcatOp*>(base_left); |
| auto* right = static_cast<const ConcatOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| return AreSkMatricesEqual(left->matrix, right->matrix); |
| } |
| |
| bool CustomDataOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const CustomDataOp*>(base_left); |
| auto* right = static_cast<const CustomDataOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| return left->id == right->id; |
| } |
| |
| bool DrawColorOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawColorOp*>(base_left); |
| auto* right = static_cast<const DrawColorOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| return left->color == right->color; |
| } |
| |
| bool DrawDRRectOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawDRRectOp*>(base_left); |
| auto* right = static_cast<const DrawDRRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreSkRRectsEqual(left->outer, right->outer)) |
| return false; |
| if (!AreSkRRectsEqual(left->inner, right->inner)) |
| return false; |
| return true; |
| } |
| |
| bool DrawImageOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawImageOp*>(base_left); |
| auto* right = static_cast<const DrawImageOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| // TODO(enne): Test PaintImage equality once implemented |
| if (!AreEqualEvenIfNaN(left->left, right->left)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->top, right->top)) |
| return false; |
| |
| // scale_adjustment intentionally omitted because it is added during |
| // serialization based on raster scale. |
| return true; |
| } |
| |
| bool DrawImageRectOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawImageRectOp*>(base_left); |
| auto* right = static_cast<const DrawImageRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| // TODO(enne): Test PaintImage equality once implemented |
| if (!AreSkRectsEqual(left->src, right->src)) |
| return false; |
| if (!AreSkRectsEqual(left->dst, right->dst)) |
| return false; |
| |
| // scale_adjustment intentionally omitted because it is added during |
| // serialization based on raster scale. |
| return true; |
| } |
| |
| bool DrawIRectOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawIRectOp*>(base_left); |
| auto* right = static_cast<const DrawIRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (left->rect != right->rect) |
| return false; |
| return true; |
| } |
| |
| bool DrawLineOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const DrawLineOp*>(base_left); |
| auto* right = static_cast<const DrawLineOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreEqualEvenIfNaN(left->x0, right->x0)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->y0, right->y0)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->x1, right->x1)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->y1, right->y1)) |
| return false; |
| return true; |
| } |
| |
| bool DrawOvalOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const DrawOvalOp*>(base_left); |
| auto* right = static_cast<const DrawOvalOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreSkRectsEqual(left->oval, right->oval)) |
| return false; |
| return true; |
| } |
| |
| bool DrawPathOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const DrawPathOp*>(base_left); |
| auto* right = static_cast<const DrawPathOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (left->path != right->path) |
| return false; |
| return true; |
| } |
| |
| bool DrawRecordOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawRecordOp*>(base_left); |
| auto* right = static_cast<const DrawRecordOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!left->record != !right->record) |
| return false; |
| if (*left->record != *right->record) |
| return false; |
| return true; |
| } |
| |
| bool DrawRectOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const DrawRectOp*>(base_left); |
| auto* right = static_cast<const DrawRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreSkRectsEqual(left->rect, right->rect)) |
| return false; |
| return true; |
| } |
| |
| bool DrawRRectOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawRRectOp*>(base_left); |
| auto* right = static_cast<const DrawRRectOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreSkRRectsEqual(left->rrect, right->rrect)) |
| return false; |
| return true; |
| } |
| |
| bool DrawSkottieOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawSkottieOp*>(base_left); |
| auto* right = static_cast<const DrawSkottieOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| // TODO(malaykeshav): Verify the skottie objects of each PaintOb are equal |
| // based on the serialized bytes. |
| if (left->t != right->t) |
| return false; |
| if (!AreSkRectsEqual(left->dst, right->dst)) |
| return false; |
| return true; |
| } |
| |
| bool DrawTextBlobOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const DrawTextBlobOp*>(base_left); |
| auto* right = static_cast<const DrawTextBlobOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreEqualEvenIfNaN(left->x, right->x)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->y, right->y)) |
| return false; |
| if (left->node_id != right->node_id) |
| return false; |
| |
| SkSerialProcs default_procs; |
| return left->blob->serialize(default_procs) |
| ->equals(right->blob->serialize(default_procs).get()); |
| } |
| |
| bool NoopOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| return true; |
| } |
| |
| bool RestoreOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| return true; |
| } |
| |
| bool RotateOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const RotateOp*>(base_left); |
| auto* right = static_cast<const RotateOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreEqualEvenIfNaN(left->degrees, right->degrees)) |
| return false; |
| return true; |
| } |
| |
| bool SaveOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| return true; |
| } |
| |
| bool SaveLayerOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const SaveLayerOp*>(base_left); |
| auto* right = static_cast<const SaveLayerOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (left->flags != right->flags) |
| return false; |
| if (!AreSkRectsEqual(left->bounds, right->bounds)) |
| return false; |
| return true; |
| } |
| |
| bool SaveLayerAlphaOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const SaveLayerAlphaOp*>(base_left); |
| auto* right = static_cast<const SaveLayerAlphaOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreSkRectsEqual(left->bounds, right->bounds)) |
| return false; |
| if (left->alpha != right->alpha) |
| return false; |
| return true; |
| } |
| |
| bool ScaleOp::AreEqual(const PaintOp* base_left, const PaintOp* base_right) { |
| auto* left = static_cast<const ScaleOp*>(base_left); |
| auto* right = static_cast<const ScaleOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreEqualEvenIfNaN(left->sx, right->sx)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->sy, right->sy)) |
| return false; |
| return true; |
| } |
| |
| bool SetMatrixOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const SetMatrixOp*>(base_left); |
| auto* right = static_cast<const SetMatrixOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreSkMatricesEqual(left->matrix, right->matrix)) |
| return false; |
| return true; |
| } |
| |
| bool TranslateOp::AreEqual(const PaintOp* base_left, |
| const PaintOp* base_right) { |
| auto* left = static_cast<const TranslateOp*>(base_left); |
| auto* right = static_cast<const TranslateOp*>(base_right); |
| DCHECK(left->IsValid()); |
| DCHECK(right->IsValid()); |
| if (!AreEqualEvenIfNaN(left->dx, right->dx)) |
| return false; |
| if (!AreEqualEvenIfNaN(left->dy, right->dy)) |
| return false; |
| return true; |
| } |
| |
| bool PaintOp::IsDrawOp() const { |
| return g_is_draw_op[type]; |
| } |
| |
| bool PaintOp::IsPaintOpWithFlags() const { |
| return g_has_paint_flags[type]; |
| } |
| |
| bool PaintOp::operator==(const PaintOp& other) const { |
| if (GetType() != other.GetType()) |
| return false; |
| return g_equals_operator[type](this, &other); |
| } |
| |
| // static |
| bool PaintOp::TypeHasFlags(PaintOpType type) { |
| return g_has_paint_flags[static_cast<uint8_t>(type)]; |
| } |
| |
| void PaintOp::Raster(SkCanvas* canvas, const PlaybackParams& params) const { |
| g_raster_functions[type](this, canvas, params); |
| } |
| |
| size_t PaintOp::Serialize(void* memory, |
| size_t size, |
| const SerializeOptions& options) const { |
| DCHECK(options.transfer_cache); |
| DCHECK(options.canvas); |
| |
| // Need at least enough room for a skip/type header. |
| if (size < 4) |
| return 0u; |
| |
| DCHECK_EQ(0u, |
| reinterpret_cast<uintptr_t>(memory) % PaintOpBuffer::PaintOpAlign); |
| |
| size_t written = g_serialize_functions[type](this, memory, size, options); |
| DCHECK_LE(written, size); |
| if (written < 4) |
| return 0u; |
| |
| size_t aligned_written = ((written + PaintOpBuffer::PaintOpAlign - 1) & |
| ~(PaintOpBuffer::PaintOpAlign - 1)); |
| if (aligned_written >= kMaxSkip) |
| return 0u; |
| if (aligned_written > size) |
| return 0u; |
| |
| // Update skip and type now that the size is known. |
| uint32_t bytes_to_skip = static_cast<uint32_t>(aligned_written); |
| static_cast<uint32_t*>(memory)[0] = type | bytes_to_skip << 8; |
| return bytes_to_skip; |
| } |
| |
| PaintOp* PaintOp::Deserialize(const volatile void* input, |
| size_t input_size, |
| void* output, |
| size_t output_size, |
| size_t* read_bytes, |
| const DeserializeOptions& options) { |
| DCHECK_GE(output_size, sizeof(LargestPaintOp)); |
| |
| uint8_t type; |
| uint32_t skip; |
| if (!PaintOpReader::ReadAndValidateOpHeader(input, input_size, &type, &skip)) |
| return nullptr; |
| |
| *read_bytes = skip; |
| return g_deserialize_functions[type](input, skip, output, output_size, |
| options); |
| } |
| |
| // static |
| bool PaintOp::GetBounds(const PaintOp* op, SkRect* rect) { |
| DCHECK(op->IsDrawOp()); |
| |
| switch (op->GetType()) { |
| case PaintOpType::DrawColor: |
| return false; |
| case PaintOpType::DrawDRRect: { |
| auto* rect_op = static_cast<const DrawDRRectOp*>(op); |
| *rect = rect_op->outer.getBounds(); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawImage: { |
| auto* image_op = static_cast<const DrawImageOp*>(op); |
| *rect = |
| SkRect::MakeXYWH(image_op->left, image_op->top, |
| image_op->image.width(), image_op->image.height()); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawImageRect: { |
| auto* image_rect_op = static_cast<const DrawImageRectOp*>(op); |
| *rect = image_rect_op->dst; |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawIRect: { |
| auto* rect_op = static_cast<const DrawIRectOp*>(op); |
| *rect = SkRect::Make(rect_op->rect); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawLine: { |
| auto* line_op = static_cast<const DrawLineOp*>(op); |
| rect->setLTRB(line_op->x0, line_op->y0, line_op->x1, line_op->y1); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawOval: { |
| auto* oval_op = static_cast<const DrawOvalOp*>(op); |
| *rect = oval_op->oval; |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawPath: { |
| auto* path_op = static_cast<const DrawPathOp*>(op); |
| *rect = path_op->path.getBounds(); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawRect: { |
| auto* rect_op = static_cast<const DrawRectOp*>(op); |
| *rect = rect_op->rect; |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawRRect: { |
| auto* rect_op = static_cast<const DrawRRectOp*>(op); |
| *rect = rect_op->rrect.rect(); |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawRecord: |
| return false; |
| case PaintOpType::DrawSkottie: { |
| auto* skottie_op = static_cast<const DrawSkottieOp*>(op); |
| *rect = skottie_op->dst; |
| rect->sort(); |
| return true; |
| } |
| case PaintOpType::DrawTextBlob: { |
| auto* text_op = static_cast<const DrawTextBlobOp*>(op); |
| *rect = text_op->blob->bounds().makeOffset(text_op->x, text_op->y); |
| rect->sort(); |
| return true; |
| } |
| default: |
| NOTREACHED(); |
| } |
| return false; |
| } |
| |
| // static |
| bool PaintOp::QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) { |
| if (!op->IsDrawOp()) |
| return false; |
| |
| SkRect rect; |
| if (!PaintOp::GetBounds(op, &rect)) |
| return false; |
| |
| if (op->IsPaintOpWithFlags()) { |
| SkPaint paint = static_cast<const PaintOpWithFlags*>(op)->flags.ToSkPaint(); |
| if (!paint.canComputeFastBounds()) |
| return false; |
| paint.computeFastBounds(rect, &rect); |
| } |
| |
| return canvas->quickReject(rect); |
| } |
| |
| // static |
| bool PaintOp::OpHasDiscardableImages(const PaintOp* op) { |
| if (op->IsPaintOpWithFlags() && static_cast<const PaintOpWithFlags*>(op) |
| ->HasDiscardableImagesFromFlags()) { |
| return true; |
| } |
| |
| if (op->GetType() == PaintOpType::DrawImage && |
| static_cast<const DrawImageOp*>(op)->HasDiscardableImages()) { |
| return true; |
| } else if (op->GetType() == PaintOpType::DrawImageRect && |
| static_cast<const DrawImageRectOp*>(op)->HasDiscardableImages()) { |
| return true; |
| } else if (op->GetType() == PaintOpType::DrawRecord && |
| static_cast<const DrawRecordOp*>(op)->HasDiscardableImages()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void PaintOp::DestroyThis() { |
| auto func = g_destructor_functions[type]; |
| if (func) |
| func(this); |
| } |
| |
| bool PaintOpWithFlags::HasDiscardableImagesFromFlags() const { |
| return flags.HasDiscardableImages(); |
| } |
| |
| void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas, |
| const PaintFlags* raster_flags, |
| const PlaybackParams& params) const { |
| g_raster_with_flags_functions[type](this, raster_flags, canvas, params); |
| } |
| |
| int ClipPathOp::CountSlowPaths() const { |
| return antialias && !path.isConvex() ? 1 : 0; |
| } |
| |
| int DrawLineOp::CountSlowPaths() const { |
| if (const SkPathEffect* effect = flags.getPathEffect().get()) { |
| SkPathEffect::DashInfo info; |
| SkPathEffect::DashType dashType = effect->asADash(&info); |
| if (flags.getStrokeCap() != PaintFlags::kRound_Cap && |
| dashType == SkPathEffect::kDash_DashType && info.fCount == 2) { |
| // The PaintFlags will count this as 1, so uncount that here as |
| // this kind of line is special cased and not slow. |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int DrawPathOp::CountSlowPaths() const { |
| // This logic is copied from SkPathCounter instead of attempting to expose |
| // that from Skia. |
| if (!flags.isAntiAlias() || path.isConvex()) |
| return 0; |
| |
| PaintFlags::Style paintStyle = flags.getStyle(); |
| const SkRect& pathBounds = path.getBounds(); |
| if (paintStyle == PaintFlags::kStroke_Style && flags.getStrokeWidth() == 0) { |
| // AA hairline concave path is not slow. |
| return 0; |
| } else if (paintStyle == PaintFlags::kFill_Style && |
| pathBounds.width() < 64.f && pathBounds.height() < 64.f && |
| !path.isVolatile()) { |
| // AADF eligible concave path is not slow. |
| return 0; |
| } else { |
| return 1; |
| } |
| } |
| |
| int DrawRecordOp::CountSlowPaths() const { |
| return record->numSlowPaths(); |
| } |
| |
| bool DrawRecordOp::HasNonAAPaint() const { |
| return record->HasNonAAPaint(); |
| } |
| |
| AnnotateOp::AnnotateOp() : PaintOp(kType) {} |
| |
| AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type, |
| const SkRect& rect, |
| sk_sp<SkData> data) |
| : PaintOp(kType), |
| annotation_type(annotation_type), |
| rect(rect), |
| data(std::move(data)) {} |
| |
| AnnotateOp::~AnnotateOp() = default; |
| |
| DrawImageOp::DrawImageOp() : PaintOpWithFlags(kType) {} |
| |
| DrawImageOp::DrawImageOp(const PaintImage& image, |
| SkScalar left, |
| SkScalar top, |
| const PaintFlags* flags) |
| : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()), |
| image(image), |
| left(left), |
| top(top) {} |
| |
| bool DrawImageOp::HasDiscardableImages() const { |
| return image && !image.IsTextureBacked(); |
| } |
| |
| DrawImageOp::~DrawImageOp() = default; |
| |
| DrawImageRectOp::DrawImageRectOp() : PaintOpWithFlags(kType) {} |
| |
| DrawImageRectOp::DrawImageRectOp(const PaintImage& image, |
| const SkRect& src, |
| const SkRect& dst, |
| const PaintFlags* flags, |
| PaintCanvas::SrcRectConstraint constraint) |
| : PaintOpWithFlags(kType, flags ? *flags : PaintFlags()), |
| image(image), |
| src(src), |
| dst(dst), |
| constraint(constraint) {} |
| |
| bool DrawImageRectOp::HasDiscardableImages() const { |
| return image && !image.IsTextureBacked(); |
| } |
| |
| DrawImageRectOp::~DrawImageRectOp() = default; |
| |
| DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record) |
| : PaintOp(kType), record(std::move(record)) {} |
| |
| DrawRecordOp::~DrawRecordOp() = default; |
| |
| size_t DrawRecordOp::AdditionalBytesUsed() const { |
| return record->bytes_used(); |
| } |
| |
| size_t DrawRecordOp::AdditionalOpCount() const { |
| return record->total_op_count(); |
| } |
| |
| DrawSkottieOp::DrawSkottieOp(scoped_refptr<SkottieWrapper> skottie, |
| SkRect dst, |
| float t) |
| : PaintOp(kType), skottie(std::move(skottie)), dst(dst), t(t) {} |
| |
| DrawSkottieOp::DrawSkottieOp() : PaintOp(kType) {} |
| |
| DrawSkottieOp::~DrawSkottieOp() = default; |
| |
| bool DrawRecordOp::HasDiscardableImages() const { |
| return record->HasDiscardableImages(); |
| } |
| |
| bool DrawRecordOp::HasText() const { |
| return record->HasText(); |
| } |
| |
| DrawTextBlobOp::DrawTextBlobOp() : PaintOpWithFlags(kType) {} |
| |
| DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, |
| SkScalar x, |
| SkScalar y, |
| const PaintFlags& flags) |
| : PaintOpWithFlags(kType, flags), blob(std::move(blob)), x(x), y(y) {} |
| |
| DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, |
| SkScalar x, |
| SkScalar y, |
| NodeId node_id, |
| const PaintFlags& flags) |
| : PaintOpWithFlags(kType, flags), |
| blob(std::move(blob)), |
| x(x), |
| y(y), |
| node_id(node_id) {} |
| |
| DrawTextBlobOp::~DrawTextBlobOp() = default; |
| |
| PaintOpBuffer::CompositeIterator::CompositeIterator( |
| const PaintOpBuffer* buffer, |
| const std::vector<size_t>* offsets) |
| : using_offsets_(!!offsets) { |
| if (using_offsets_) |
| offset_iter_.emplace(buffer, offsets); |
| else |
| iter_.emplace(buffer); |
| } |
| |
| PaintOpBuffer::CompositeIterator::CompositeIterator( |
| const CompositeIterator& other) = default; |
| PaintOpBuffer::CompositeIterator::CompositeIterator(CompositeIterator&& other) = |
| default; |
| |
| PaintOpBuffer::PaintOpBuffer() |
| : has_non_aa_paint_(false), |
| has_discardable_images_(false), |
| has_text_(false) {} |
| |
| PaintOpBuffer::PaintOpBuffer(PaintOpBuffer&& other) { |
| *this = std::move(other); |
| } |
| |
| PaintOpBuffer::~PaintOpBuffer() { |
| Reset(); |
| } |
| |
| PaintOpBuffer& PaintOpBuffer::operator=(PaintOpBuffer&& other) { |
| data_ = std::move(other.data_); |
| used_ = other.used_; |
| reserved_ = other.reserved_; |
| op_count_ = other.op_count_; |
| num_slow_paths_ = other.num_slow_paths_; |
| subrecord_bytes_used_ = other.subrecord_bytes_used_; |
| subrecord_op_count_ = other.subrecord_op_count_; |
| has_non_aa_paint_ = other.has_non_aa_paint_; |
| has_discardable_images_ = other.has_discardable_images_; |
| has_text_ = other.has_text_; |
| |
| // Make sure the other pob can destruct safely. |
| other.used_ = 0; |
| other.op_count_ = 0; |
| other.reserved_ = 0; |
| return *this; |
| } |
| |
| void PaintOpBuffer::Reset() { |
| for (auto* op : Iterator(this)) |
| op->DestroyThis(); |
| |
| // Leave data_ allocated, reserved_ unchanged. ShrinkToFit will take care of |
| // that if called. |
| used_ = 0; |
| op_count_ = 0; |
| num_slow_paths_ = 0; |
| has_non_aa_paint_ = false; |
| subrecord_bytes_used_ = 0; |
| subrecord_op_count_ = 0; |
| has_discardable_images_ = false; |
| has_text_ = false; |
| } |
| |
| // When |op| is a nested PaintOpBuffer, this returns the PaintOp inside |
| // that buffer if the buffer contains a single drawing op, otherwise it |
| // returns null. This searches recursively if the PaintOpBuffer contains only |
| // another PaintOpBuffer. |
| static const PaintOp* GetNestedSingleDrawingOp(const PaintOp* op) { |
| if (!op->IsDrawOp()) |
| return nullptr; |
| while (op->GetType() == PaintOpType::DrawRecord) { |
| auto* draw_record_op = static_cast<const DrawRecordOp*>(op); |
| if (draw_record_op->record->size() > 1) { |
| // If there's more than one op, then we need to keep the |
| // SaveLayer. |
| return nullptr; |
| } |
| |
| // Recurse into the single-op DrawRecordOp and make sure it's a |
| // drawing op. |
| op = draw_record_op->record->GetFirstOp(); |
| if (!op->IsDrawOp()) |
| return nullptr; |
| } |
| |
| return op; |
| } |
| |
| void PaintOpBuffer::Playback(SkCanvas* canvas) const { |
| Playback(canvas, PlaybackParams(nullptr), nullptr); |
| } |
| |
| void PaintOpBuffer::Playback(SkCanvas* canvas, |
| const PlaybackParams& params) const { |
| Playback(canvas, params, nullptr); |
| } |
| |
| PaintOpBuffer::PlaybackFoldingIterator::PlaybackFoldingIterator( |
| const PaintOpBuffer* buffer, |
| const std::vector<size_t>* offsets) |
| : iter_(buffer, offsets), |
| folded_draw_color_(SK_ColorTRANSPARENT, SkBlendMode::kSrcOver) { |
| FindNextOp(); |
| } |
| |
| PaintOpBuffer::PlaybackFoldingIterator::~PlaybackFoldingIterator() = default; |
| |
| void PaintOpBuffer::PlaybackFoldingIterator::FindNextOp() { |
| current_alpha_ = 255u; |
| for (current_op_ = NextUnfoldedOp(); current_op_; |
| current_op_ = NextUnfoldedOp()) { |
| if (current_op_->GetType() != PaintOpType::SaveLayerAlpha) |
| break; |
| const PaintOp* second = NextUnfoldedOp(); |
| if (!second) |
| break; |
| |
| if (second->GetType() == PaintOpType::Restore) { |
| // Drop a SaveLayerAlpha/Restore combo. |
| continue; |
| } |
| |
| // Find a nested drawing PaintOp to replace |second| if possible, while |
| // holding onto the pointer to |second| in case we can't find a nested |
| // drawing op to replace it with. |
| const PaintOp* draw_op = GetNestedSingleDrawingOp(second); |
| |
| const PaintOp* third = nullptr; |
| if (draw_op) { |
| third = NextUnfoldedOp(); |
| if (third && third->GetType() == PaintOpType::Restore) { |
| auto* save_op = static_cast<const SaveLayerAlphaOp*>(current_op_); |
| if (draw_op->IsPaintOpWithFlags()) { |
| auto* flags_op = static_cast<const PaintOpWithFlags*>(draw_op); |
| if (flags_op->flags.SupportsFoldingAlpha()) { |
| current_alpha_ = save_op->alpha; |
| current_op_ = draw_op; |
| break; |
| } |
| } else if (draw_op->GetType() == PaintOpType::DrawColor && |
| static_cast<const DrawColorOp*>(draw_op)->mode == |
| SkBlendMode::kSrcOver) { |
| auto* draw_color_op = static_cast<const DrawColorOp*>(draw_op); |
| SkColor color = draw_color_op->color; |
| folded_draw_color_.color = SkColorSetARGB( |
| SkMulDiv255Round(save_op->alpha, SkColorGetA(color)), |
| SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); |
| current_op_ = &folded_draw_color_; |
| break; |
| } |
| } |
| } |
| |
| // If we get here, then we could not find a foldable sequence after |
| // this SaveLayerAlpha, so store any peeked at ops. |
| stack_->push_back(second); |
| if (third) |
| stack_->push_back(third); |
| break; |
| } |
| } |
| |
| const PaintOp* PaintOpBuffer::PlaybackFoldingIterator::NextUnfoldedOp() { |
| if (stack_->size()) { |
| const PaintOp* op = stack_->front(); |
| // Shift paintops forward. |
| stack_->erase(stack_->begin()); |
| return op; |
| } |
| if (!iter_) |
| return nullptr; |
| const PaintOp* op = *iter_; |
| ++iter_; |
| return op; |
| } |
| |
| void PaintOpBuffer::Playback(SkCanvas* canvas, |
| const PlaybackParams& params, |
| const std::vector<size_t>* offsets) const { |
| if (!op_count_) |
| return; |
| if (offsets && offsets->empty()) |
| return; |
| |
| // Prevent PaintOpBuffers from having side effects back into the canvas. |
| SkAutoCanvasRestore save_restore(canvas, true); |
| |
| // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the |
| // SetMatrix is local to that PaintRecord itself. Said differently, if you |
| // translate(x, y), then draw a paint record with a SetMatrix(identity), |
| // the translation should be preserved instead of clobbering the top level |
| // transform. This could probably be done more efficiently. |
| PlaybackParams new_params(params.image_provider, canvas->getTotalMatrix(), |
| params.custom_callback, |
| params.did_draw_op_callback); |
| for (PlaybackFoldingIterator iter(this, offsets); iter; ++iter) { |
| const PaintOp* op = *iter; |
| |
| // This is an optimization to replicate the behaviour in SkCanvas |
| // which rejects ops that draw outside the current clip. In the |
| // general case we defer this to the SkCanvas but if we will be |
| // using an ImageProvider for pre-decoding images, we can save |
| // performing an expensive decode that will never be rasterized. |
| const bool skip_op = new_params.image_provider && |
| PaintOp::OpHasDiscardableImages(op) && |
| PaintOp::QuickRejectDraw(op, canvas); |
| if (skip_op) |
| continue; |
| |
| if (op->IsPaintOpWithFlags()) { |
| const auto* flags_op = static_cast<const PaintOpWithFlags*>(op); |
| auto* context = canvas->getGrContext(); |
| const ScopedRasterFlags scoped_flags( |
| &flags_op->flags, new_params.image_provider, canvas->getTotalMatrix(), |
| context ? context->maxTextureSize() : 0, iter.alpha()); |
| if (const auto* raster_flags = scoped_flags.flags()) |
| flags_op->RasterWithFlags(canvas, raster_flags, new_params); |
| } else { |
| // TODO(enne): skip SaveLayer followed by restore with nothing in |
| // between, however SaveLayer with image filters on it (or maybe |
| // other PaintFlags options) are not a noop. Figure out what these |
| // are so we can skip them correctly. |
| DCHECK_EQ(iter.alpha(), 255); |
| op->Raster(canvas, new_params); |
| } |
| |
| if (!new_params.did_draw_op_callback.is_null()) |
| new_params.did_draw_op_callback.Run(); |
| } |
| } |
| |
| sk_sp<PaintOpBuffer> PaintOpBuffer::MakeFromMemory( |
| const volatile void* input, |
| size_t input_size, |
| const PaintOp::DeserializeOptions& options) { |
| auto buffer = sk_make_sp<PaintOpBuffer>(); |
| if (input_size == 0) |
| return buffer; |
| |
| size_t total_bytes_read = 0u; |
| while (total_bytes_read < input_size) { |
| const volatile void* next_op = |
| static_cast<const volatile char*>(input) + total_bytes_read; |
| |
| uint8_t type; |
| uint32_t skip; |
| if (!PaintOpReader::ReadAndValidateOpHeader( |
| next_op, input_size - total_bytes_read, &type, &skip)) { |
| return nullptr; |
| } |
| |
| size_t op_skip = ComputeOpSkip(g_type_to_size[type]); |
| const auto* op = g_deserialize_functions[type]( |
| next_op, skip, buffer->AllocatePaintOp(op_skip), op_skip, options); |
| if (!op) { |
| // The last allocated op has already been destroyed if it failed to |
| // deserialize. Update the buffer's op tracking to exclude it to avoid |
| // access during cleanup at destruction. |
| buffer->used_ -= op_skip; |
| buffer->op_count_--; |
| return nullptr; |
| } |
| |
| g_analyze_op_functions[type](buffer.get(), op); |
| total_bytes_read += skip; |
| } |
| |
| DCHECK_GT(buffer->size(), 0u); |
| return buffer; |
| } |
| |
| void PaintOpBuffer::ReallocBuffer(size_t new_size) { |
| DCHECK_GE(new_size, used_); |
| std::unique_ptr<char, base::AlignedFreeDeleter> new_data( |
| static_cast<char*>(base::AlignedAlloc(new_size, PaintOpAlign))); |
| if (data_) |
| memcpy(new_data.get(), data_.get(), used_); |
| data_ = std::move(new_data); |
| reserved_ = new_size; |
| } |
| |
| void* PaintOpBuffer::AllocatePaintOp(size_t skip) { |
| DCHECK_LT(skip, PaintOp::kMaxSkip); |
| if (used_ + skip > reserved_) { |
| // Start reserved_ at kInitialBufferSize and then double. |
| // ShrinkToFit can make this smaller afterwards. |
| size_t new_size = reserved_ ? reserved_ : kInitialBufferSize; |
| while (used_ + skip > new_size) |
| new_size *= 2; |
| ReallocBuffer(new_size); |
| } |
| DCHECK_LE(used_ + skip, reserved_); |
| |
| void* op = data_.get() + used_; |
| used_ += skip; |
| op_count_++; |
| return op; |
| } |
| |
| void PaintOpBuffer::ShrinkToFit() { |
| if (used_ == reserved_) |
| return; |
| if (!used_) { |
| reserved_ = 0; |
| data_.reset(); |
| } else { |
| ReallocBuffer(used_); |
| } |
| } |
| |
| bool PaintOpBuffer::operator==(const PaintOpBuffer& other) const { |
| if (op_count_ != other.op_count_) |
| return false; |
| if (num_slow_paths_ != other.num_slow_paths_) |
| return false; |
| if (subrecord_bytes_used_ != other.subrecord_bytes_used_) |
| return false; |
| if (subrecord_op_count_ != other.subrecord_op_count_) |
| return false; |
| if (has_non_aa_paint_ != other.has_non_aa_paint_) |
| return false; |
| if (has_discardable_images_ != other.has_discardable_images_) |
| return false; |
| if (has_text_ != other.has_text_) |
| return false; |
| |
| auto left_iter = Iterator(this); |
| auto right_iter = Iterator(&other); |
| |
| for (; left_iter != left_iter.end(); ++left_iter, ++right_iter) { |
| if (**left_iter != **right_iter) |
| return false; |
| } |
| |
| DCHECK(left_iter == left_iter.end()); |
| DCHECK(right_iter == right_iter.end()); |
| return true; |
| } |
| |
| } // namespace cc |