chromium/cc/paint/paint_op_buffer_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "cc/paint/paint_op_buffer.h"

#include <algorithm>
#include <array>
#include <string>
#include <vector>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "cc/paint/decoded_draw_image.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/draw_looper.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/image_transfer_cache_entry.h"
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_buffer_iterator.h"
#include "cc/paint/paint_op_buffer_serializer.h"
#include "cc/paint/paint_op_reader.h"
#include "cc/paint/paint_op_writer.h"
#include "cc/paint/refcounted_buffer.h"
#include "cc/paint/shader_transfer_cache_entry.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"
#include "cc/paint/skottie_wrapper.h"
#include "cc/paint/transfer_cache_entry.h"
#include "cc/test/lottie_test_data.h"
#include "cc/test/paint_op_matchers.h"
#include "cc/test/skia_common.h"
#include "cc/test/test_options_provider.h"
#include "cc/test/test_paint_worklet_input.h"
#include "cc/test/test_skcanvas.h"
#include "cc/test/transfer_cache_test_helper.h"
#include "skia/buildflags.h"
#include "skia/ext/font_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkBlendMode.h"
#include "third_party/skia/include/core/SkBlurTypes.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkClipOp.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSamplingOptions.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkTileMode.h"
#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
#include "third_party/skia/include/private/chromium/SkChromeRemoteGlyphCache.h"
#include "ui/gfx/geometry/test/geometry_util.h"

namespace cc {
namespace {

_;
AllOf;
AtLeast;
Contains;
Each;
ElementsAre;
Eq;
FloatEq;
InSequence;
Key;
Le;
Matcher;
Mock;
NiceMock;
NotNull;
Pointee;
ResultOf;
StrictMock;

// An arbitrary size guaranteed to fit the size of any serialized op in this
// unit test.  This can also be used for deserialized op size safely in this
// unit test suite as generally deserialized ops are smaller.
static constexpr size_t kSerializedBytesPerOp =;

static constexpr size_t kDefaultSerializedBufferSize =;
std::unique_ptr<char, base::AlignedFreeDeleter> AllocateSerializedBuffer(
    size_t size = kDefaultSerializedBufferSize) {}

bool ReadAndValidateOpHeader(const void* memory,
                             size_t size,
                             uint8_t* op_type,
                             size_t* serialized_size) {}

}  // namespace

class PaintOpSerializationTestUtils {};

TEST(PaintOpBufferTest, Empty) {}

class PaintOpAppendTest : public ::testing::Test {};

TEST_F(PaintOpAppendTest, SimpleAppend) {}

TEST_F(PaintOpAppendTest, MoveThenDestruct) {}

TEST_F(PaintOpAppendTest, MoveThenDestructOperatorEq) {}

TEST_F(PaintOpAppendTest, MoveThenReappend) {}

TEST_F(PaintOpAppendTest, MoveThenReappendOperatorEq) {}

// Verify that a kSaveLayerAlpha / Draw / kRestore can be optimized to just
// a draw with opacity.
TEST(PaintOpBufferTest, SaveDrawRestore) {}

// Verify that we don't optimize kSaveLayerAlpha / kDrawTextBlob / kRestore.
TEST(PaintOpBufferTest, SaveDrawTextBlobRestore) {}

// Verify that we don't optimize kSaveLayerAlpha / kSaveLayerFilters / kRestore.
TEST(PaintOpBufferTest, SaveSaveLayerFiltersRestore) {}

// The same as SaveDrawRestore, but test that the optimization doesn't apply
// when the drawing op's flags are not compatible with being folded into the
// save layer with opacity.
TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) {}

// Same as above, but the save layer itself appears to be a noop.
// See: http://crbug.com/748485.  If the inner draw op itself
// doesn't support folding, then the external save can't be skipped.
TEST(PaintOpBufferTest, SaveDrawRestore_BadFlags255Alpha) {}

// The same as SaveDrawRestore, but test that the optimization doesn't apply
// when there are more than one ops between the save and restore.
TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) {}

// Verify that the save draw restore code works with a single op
// that's not a draw op, and the optimization does not kick in.
TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpNotADrawOp) {}

// Test that the save/draw/restore optimization applies if the single op
// is a kDrawRecord that itself has a single draw op.
TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleOp) {}

// The same as the above SingleOpRecord test, but the single op is not
// a draw op.  So, there's no way to fold in the save layer optimization.
// Verify that the optimization doesn't apply and that this doesn't crash.
// See: http://crbug.com/712093.
TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) {}

TEST(PaintOpBufferTest, SaveLayerRestore_DrawColor) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_EmptyBuffer) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_NoImageOp) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImage) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImage) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImageRect) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {}

TEST(PaintOpBufferTest, DiscardableImagesTracking_OpWithFlags) {}

TEST(PaintOpBufferTest, SlowPaths) {}

TEST(PaintOpBufferTest, NonAAPaint) {}

class PaintOpBufferOffsetsTest : public ::testing::Test {};

TEST_F(PaintOpBufferOffsetsTest, EmptyClipRectShouldRejectAnOp) {}

TEST_F(PaintOpBufferOffsetsTest, ContiguousIndices) {}

TEST_F(PaintOpBufferOffsetsTest, NonContiguousIndices) {}

TEST_F(PaintOpBufferOffsetsTest, FirstTwoIndices) {}

TEST_F(PaintOpBufferOffsetsTest, MiddleIndex) {}

TEST_F(PaintOpBufferOffsetsTest, LastTwoElements) {}

TEST_F(PaintOpBufferOffsetsTest, ContiguousIndicesWithSaveLayerAlphaRestore) {}

TEST_F(PaintOpBufferOffsetsTest,
       NonContiguousIndicesWithSaveLayerAlphaRestore) {}

TEST_F(PaintOpBufferOffsetsTest,
       ContiguousIndicesWithSaveLayerAlphaDrawRestore) {}

TEST_F(PaintOpBufferOffsetsTest,
       NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {}

TEST(PaintOpBufferTest, SaveLayerAlphaDrawRestoreWithBadBlendMode) {}

TEST(PaintOpBufferTest, UnmatchedSaveRestoreNoSideEffects) {}

std::vector<float> test_floats =;

std::vector<float> test_angles =;

std::vector<uint8_t> test_uint8s =;

static SkRect make_largest_skrect() {}

static SkIRect make_largest_skirect() {}

std::vector<SkRect> test_rects =;

std::vector<SkRRect> test_rrects =rrect =;

std::vector<SkIRect> test_irects =;

std::vector<uint32_t> test_ids =;

std::vector<SkM44> test_matrices =;

std::vector<SkPath> test_paths =path;
      path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
      path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
      path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
      path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
      return path;
    }(),
    [] {}(),
    SkPath(),
};

std::vector<PaintFlags> test_flags =flags;
      flags.setColor(SK_ColorMAGENTA);
      flags.setStrokeWidth(4.2f);
      flags.setStrokeMiter(5.91f);
      flags.setBlendMode(SkBlendMode::kDst);
      flags.setStrokeCap(PaintFlags::kSquare_Cap);
      flags.setStrokeJoin(PaintFlags::kBevel_Join);
      flags.setStyle(PaintFlags::kStroke_Style);
      flags.setFilterQuality(PaintFlags::FilterQuality::kMedium);
      flags.setShader(PaintShader::MakeColor({}));
      return flags;
    }(),
    [] {}(),
    [] {}(),
    [] {}(),
    [] {}(),
    [] {}(),
    PaintFlags(),
    PaintFlags(),
};

std::vector<SkColor4f> test_colors =;

std::vector<std::string> test_strings =;

std::vector<std::vector<SkPoint>> test_point_arrays =;

// TODO(enne): In practice, probably all paint images need to be uploaded
// ahead of time and not be bitmaps. These paint images should be fake
// gpu resource paint images.
std::vector<PaintImage> test_images =;

#if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)

bool kIsSkottieSupported =;
#else
bool kIsSkottieSupported = false;
#endif

// Writes as many ops in |buffer| as can fit in |output_size| to |output|.
// Records the numbers of bytes written for each op.
class SimpleSerializer {};

class DeserializerIterator {};

void PushAnnotateOps(PaintOpBuffer* buffer) {}

void PushClipPathOps(PaintOpBuffer* buffer) {}

void PushClipRectOps(PaintOpBuffer* buffer) {}

void PushClipRRectOps(PaintOpBuffer* buffer) {}

void PushConcatOps(PaintOpBuffer* buffer) {}

void PushCustomDataOps(PaintOpBuffer* buffer) {}

void PushDrawColorOps(PaintOpBuffer* buffer) {}

void PushDrawDRRectOps(PaintOpBuffer* buffer) {}

void PushDrawImageOps(PaintOpBuffer* buffer) {}

void PushDrawImageRectOps(PaintOpBuffer* buffer) {}

void PushDrawIRectOps(PaintOpBuffer* buffer) {}

void PushDrawLineOps(PaintOpBuffer* buffer) {}

void PushDrawLineLiteOps(PaintOpBuffer* buffer) {}

void PushDrawArcOps(PaintOpBuffer* buffer) {}

void PushDrawArcLiteOps(PaintOpBuffer* buffer) {}

void PushDrawOvalOps(PaintOpBuffer* buffer) {}

void PushDrawPathOps(PaintOpBuffer* buffer) {}

void PushDrawRectOps(PaintOpBuffer* buffer) {}

void PushDrawRRectOps(PaintOpBuffer* buffer) {}

void PushDrawVerticesOps(PaintOpBuffer* buffer) {}

SkottieFrameDataMap GetTestImagesForSkottie(SkottieWrapper& skottie,
                                            const SkRect& skottie_rect,
                                            PaintFlags::FilterQuality quality,
                                            float t) {}

SkottieFrameDataMap GetNullImagesForSkottie(SkottieWrapper& skottie, float t) {}

void PushDrawSkottieOps(PaintOpBuffer* buffer) {}

void PushDrawTextBlobOps(PaintOpBuffer* buffer) {}

void PushNoopOps(PaintOpBuffer* buffer) {}

void PushRestoreOps(PaintOpBuffer* buffer) {}

void PushRotateOps(PaintOpBuffer* buffer) {}

void PushSaveOps(PaintOpBuffer* buffer) {}

void PushSaveLayerOps(PaintOpBuffer* buffer) {}

void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {}

void PushSaveLayerFiltersOps(PaintOpBuffer* buffer) {}

void PushScaleOps(PaintOpBuffer* buffer) {}

void PushSetMatrixOps(PaintOpBuffer* buffer) {}

void PushTranslateOps(PaintOpBuffer* buffer) {}

void PushSetNodeIdOps(PaintOpBuffer* buffer) {}

class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {};

sk_sp<PaintOpBuffer> SerializeAndDeserialize(
    const PaintOpBuffer& buffer,
    const PaintOpBufferSerializer::Preamble* preamble = nullptr) {}

INSTANTIATE_TEST_SUITE_P();

// Test serializing and then deserializing all test ops.  They should all
// write successfully and be identical to the original ops in the buffer.
TEST_P(PaintOpSerializationTest, SmokeTest) {}

// Verify for all test ops that serializing into a smaller size aborts
// correctly and doesn't write anything.
TEST_P(PaintOpSerializationTest, SerializationFailures) {}

// Verify that deserializing test ops from too small buffers aborts
// correctly, in case the deserialized data is lying about how big it is.
TEST_P(PaintOpSerializationTest, DeserializationFailures) {}

TEST_P(PaintOpSerializationTest, UsesOverridenFlags) {}

TEST(PaintOpSerializationTest, CompleteBufferSerialization) {}

TEST(PaintOpSerializationTest, Preamble) {}

TEST(PaintOpSerializationTest,
     ConvertToDrawSlugWhenSerializationAndRasterization) {}

TEST(PaintOpSerializationTest, SerializesNestedRecords) {}

TEST(PaintOpBufferTest, ClipsImagesDuringSerialization) {}

TEST(PaintOpBufferSerializationTest, AlphaFoldingDuringSerialization) {}

// Test generic PaintOp deserializing failure cases.
TEST(PaintOpBufferTest, PaintOpDeserialize) {}

// Test that deserializing invalid paint ops fails silently. Skia release
// asserts on invalid values in several places so these are not safe to pass
// them to the SkCanvas API.
TEST(PaintOpBufferTest, ValidateRects) {}

TEST(PaintOpBufferTest, ValidateSkClip) {}

TEST(PaintOpBufferTest, ValidateSkBlendMode) {}

TEST(PaintOpBufferTest, BoundingRect_DrawImageOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawImageRectOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawIRectOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawOvalOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawPathOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawRectOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawArcOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawRRectOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawLineOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawDRRectOp) {}

TEST(PaintOpBufferTest, BoundingRect_DrawTextBlobOp) {}

class MockImageProvider : public ImageProvider {};

TEST(PaintOpBufferTest, SkipsOpsOutsideClip) {}

TEST(PaintOpBufferTest, SkipsOpsWithFailedDecodes) {}

MATCHER(NonLazyImage, "") {}

MATCHER_P(MatchesPaintImage, paint_image, "") {}

MATCHER_P2(MatchesRect, rect, scale, "") {}

MATCHER_P(MatchesQuality, quality, "") {}

MATCHER_P2(MatchesShader, flags, scale, "") {}

TEST(PaintOpBufferTest, RasterPaintWorkletImageRectBasicCase) {}

TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) {}

TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) {}

TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) {}

TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {}

TEST(PaintOpBufferTest, DrawImageRectOpWithLooperNoImageProvider) {}

TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) {}

TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) {}

class PaintFilterSerializationTest : public ::testing::TestWithParam<bool> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(PaintFilterSerializationTest, Basic) {}

TEST(PaintOpBufferTest, RecordPaintFilterDeserializationInvalidPaintOp) {}

TEST(PaintOpBufferTest, PaintRecordShaderSerialization) {}

#if BUILDFLAG(SKIA_SUPPORT_SKOTTIE)
TEST(PaintOpBufferTest, BoundingRect_DrawSkottieOp) {}

// Skottie-specific deserialization failure case.
TEST(PaintOpBufferTest,
     DrawSkottieOpSerializationFailureFromUnPrivilegedProcess) {}

TEST(PaintOpBufferTest, DrawSkottieOpRasterWithoutImageAssets) {}

TEST(PaintOpBufferTest, DrawSkottieOpRasterWithNullImages) {}

TEST(PaintOpBufferTest, DrawSkottieOpRasterWithoutImageProvider) {}

TEST(PaintOpBufferTest, DrawSkottieOpRasterWithImageProvider) {}

TEST(PaintOpBufferTest, DiscardableImagesTrackingSkottieOpNoImages) {}

TEST(PaintOpBufferTest, DiscardableImagesTrackingSkottieOpWithImages) {}

TEST(PaintOpBufferTest, OpHasDiscardableImagesSkottieOpNoImages) {}

TEST(PaintOpBufferTest, OpHasDiscardableImagesSkottieOpWithImages) {}
#endif  // BUILDFLAG(SKIA_SUPPORT_SKOTTIE)

TEST(PaintOpBufferTest, CustomData) {}

TEST(PaintOpBufferTest, SecurityConstrainedImageSerialization) {}

TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) {}

TEST(PaintOpBufferTest, RecordShadersSerializeScaledImages) {}

TEST(PaintOpBufferTest, RecordShadersCached) {}

TEST(PaintOpBufferTest, RecordShadersCachedSize) {}

TEST(PaintOpBufferTest, RecordFilterSerializeScaledImages) {}

TEST(PaintOpBufferTest, TotalOpCount) {}

TEST(PaintOpBufferTest, NullImages) {}

TEST(PaintOpBufferTest, HasDrawOpsAndHasDrawTextOps) {}

TEST(PaintOpBufferTest, HasEffectsPreventingLCDTextForSaveLayerAlpha) {}

TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) {}

// A regression test for crbug.com/1195276. Ensure that PlaybackParams works
// with kSetMatrix operations.
TEST(PaintOpBufferTest, SetMatrixOpWithNonIdentityPlaybackParams) {}

// Tests playback of a `PaintOpBuffer` with `local_ctm` set to `true`. In
// that mode, the transforms are local to the `PaintOpBuffer`: any transform
// changes are undone and `SetMatrixOp` operates relatively to the canvas
// current transform.
TEST(PaintOpBufferTest, PlaybackSetMatrixWithLocalCTM) {}

// Tests playback of a `PaintOpBuffer` with `local_ctm` set to `false`. In that
// mode, the played-back buffer acts on the parent CTM: any transform changes
// done are preserved and `SetMatrixOp` ignores and overrides any transform the
// canvas might have previously had.
TEST(PaintOpBufferTest, PlaybackSetMatrixWithNonLocalCTM) {}

// Tests playback of a `DrawRecordOp` having `local_ctm` set to `true`.
// `DrawRecordOp` can't have side effect on the canvas transform and
// `SetMatrixOp` act relatively to the current canvas transform.
TEST(PaintOpBufferTest, PlaybackDrawRecordWithLocalCTM) {}

// Tests playback of a `DrawRecordOp` having `local_ctm` set to `false`.
// Transform changes done by `DrawRecordOp` are preserved and `SetMatrixOp`
// ignores the current canvas transform.
TEST(PaintOpBufferTest, PlaybackDrawRecordWithNonLocalCTM) {}

// Tests playback of a non-local-ctm `DrawRecordOp` into a local-ctm
// `DrawRecordOp`. The `SetMatrixOp` in the inner non-local-ctm `DrawRecordOp`
// will override the transform of the parent record, but not the global one.
TEST(PaintOpBufferTest, PlaybackDrawRecordNestedLocalAndNonLocalCTM) {}

// Tests playback of a non-local-ctm `DrawRecordOp` into another non-local-ctm
// `DrawRecordOp`. The `SetMatrixOp` in the inner non-local-ctm `DrawRecordOp`
// will override all transforms.
TEST(PaintOpBufferTest, PlaybackDrawRecordNestedNonLocalAndNonLocalCTM) {}

TEST(PaintOpBufferTest, PathCaching) {}

TEST(PaintOpBufferTest, ShrinkToFit) {}

TEST(PaintOpBufferTest, ReleaseAsRecord) {}

TEST(IteratorTest, StlContainerLikeIterationTest) {}

TEST(IteratorTest, IterationTest) {}

TEST(IteratorTest, OffsetIterationTest) {}

TEST(IteratorTest, CompositeIterationTest) {}

TEST(IteratorTest, EqualityTest) {}

TEST(IteratorTest, OffsetEqualityTest) {}

TEST(IteratorTest, CompositeEqualityTest) {}

TEST(IteratorTest, CompositeOffsetEqualityTest) {}

TEST(IteratorTest, CompositeOffsetMixedTypeEqualityTest) {}

TEST(IteratorTest, CompositeOffsetBoolCheck) {}

TEST(IteratorTest, PlaybackFoldingIteratorEmptyDrawRecordInSaveLayerAlpha) {}

TEST(PaintOpBufferTest, ContentColorUsageFromImages) {}

TEST(PaintOpBufferTest, ContentColorUsageFromShader) {}

TEST(PaintOpBufferTest, ContentColorUsageFromFilter) {}

}  // namespace cc