blob: 949e793f1e30b6a12cd9c5be816df82629753b2d [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h"
#include "cc/paint/paint_op.h"
#include "cc/test/paint_op_matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_style_test_utils.h"
#include "third_party/blink/renderer/modules/canvas/canvas2d/recording_test_utils.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
namespace blink {
namespace {
using ::blink_testing::RecordedOpsAre;
using ::cc::ConcatOp;
using ::cc::DrawColorOp;
using ::cc::DrawRectOp;
using ::cc::PaintOpEq;
using ::cc::RestoreOp;
using ::cc::SaveOp;
using ::cc::ScaleOp;
using ::cc::SetMatrixOp;
static const int kWidth = 50;
static const int kHeight = 75;
void TrySettingStrokeStyle(V8TestingScope& v8_testing_scope,
PaintRenderingContext2D* ctx,
const String& expected,
const String& value) {
auto* script_state = v8_testing_scope.GetScriptState();
SetStrokeStyleString(ctx, script_state, "red");
SetStrokeStyleString(ctx, script_state, value);
EXPECT_EQ(expected, GetStrokeStyleAsString(ctx, script_state));
}
TEST(PaintRenderingContext2DTest, testParseColorOrCurrentColor) {
test::TaskEnvironment task_environment;
V8TestingScope v8_testing_scope;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
context_settings->setAlpha(false);
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
TrySettingStrokeStyle(v8_testing_scope, ctx, "#0000ff", "blue");
TrySettingStrokeStyle(v8_testing_scope, ctx, "#000000", "currentColor");
}
TEST(PaintRenderingContext2DTest, testWidthAndHeight) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
EXPECT_EQ(kWidth, ctx->Width());
EXPECT_EQ(kHeight, ctx->Height());
}
TEST(PaintRenderingContext2DTest, testBasicState) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
const double kShadowBlurBefore = 2;
const double kShadowBlurAfter = 3;
const String line_join_before = "bevel";
const String line_join_after = "round";
ctx->setShadowBlur(kShadowBlurBefore);
ctx->setLineJoin(line_join_before);
EXPECT_EQ(kShadowBlurBefore, ctx->shadowBlur());
EXPECT_EQ(line_join_before, ctx->lineJoin());
ctx->save();
ctx->setShadowBlur(kShadowBlurAfter);
ctx->setLineJoin(line_join_after);
EXPECT_EQ(kShadowBlurAfter, ctx->shadowBlur());
EXPECT_EQ(line_join_after, ctx->lineJoin());
NonThrowableExceptionState exception_state;
ctx->restore(exception_state);
EXPECT_EQ(kShadowBlurBefore, ctx->shadowBlur());
EXPECT_EQ(line_join_before, ctx->lineJoin());
}
TEST(PaintRenderingContext2DTest, setTransformWithDeviceScaleFactor) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
float zoom = 1.23;
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, zoom,
scheduler::GetSingleThreadTaskRunnerForTesting());
DOMMatrix* matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
ctx->setTransform(2.1, 2.5, 1.4, 2.3, 20, 50);
matrix = ctx->getTransform();
EXPECT_FLOAT_EQ(matrix->a(), 2.1);
EXPECT_FLOAT_EQ(matrix->b(), 2.5);
EXPECT_FLOAT_EQ(matrix->c(), 1.4);
EXPECT_FLOAT_EQ(matrix->d(), 2.3);
EXPECT_FLOAT_EQ(matrix->e(), 20);
EXPECT_FLOAT_EQ(matrix->f(), 50);
EXPECT_THAT(ctx->GetRecord(),
RecordedOpsAre(PaintOpEq<ScaleOp>(1.23, 1.23),
PaintOpEq<DrawColorOp>(SkColors::kTransparent,
SkBlendMode::kSrc),
PaintOpEq<SetMatrixOp>(SkM44(1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1)),
PaintOpEq<ConcatOp>(SkM44(zoom, 0, 0, 0, //
0, zoom, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1)),
PaintOpEq<ConcatOp>(SkM44(2.1, 1.4, 0, 20, //
2.5, 2.3, 0, 50, //
0, 0, 1, 0, //
0, 0, 0, 1))));
}
TEST(PaintRenderingContext2DTest, setTransformWithDefaultDeviceScaleFactor) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
DOMMatrix* matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
ctx->setTransform(1.2, 2.3, 3.4, 4.5, 56, 67);
matrix = ctx->getTransform();
EXPECT_FLOAT_EQ(matrix->a(), 1.2);
EXPECT_FLOAT_EQ(matrix->b(), 2.3);
EXPECT_FLOAT_EQ(matrix->c(), 3.4);
EXPECT_FLOAT_EQ(matrix->d(), 4.5);
EXPECT_FLOAT_EQ(matrix->e(), 56);
EXPECT_FLOAT_EQ(matrix->f(), 67);
EXPECT_THAT(ctx->GetRecord(),
RecordedOpsAre(PaintOpEq<DrawColorOp>(SkColors::kTransparent,
SkBlendMode::kSrc),
PaintOpEq<ConcatOp>(SkM44(1.2, 3.4, 0, 56, //
2.3, 4.5, 0, 67, //
0, 0, 1, 0, //
0, 0, 0, 1))));
}
TEST(PaintRenderingContext2DTest, resetWithDeviceScaleFactor) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
float zoom = 1.23;
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, zoom,
scheduler::GetSingleThreadTaskRunnerForTesting());
DOMMatrix* matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
ctx->setTransform(2.1, 2.5, 1.4, 2.3, 20, 50);
matrix = ctx->getTransform();
EXPECT_FLOAT_EQ(matrix->a(), 2.1);
EXPECT_FLOAT_EQ(matrix->b(), 2.5);
EXPECT_FLOAT_EQ(matrix->c(), 1.4);
EXPECT_FLOAT_EQ(matrix->d(), 2.3);
EXPECT_FLOAT_EQ(matrix->e(), 20);
EXPECT_FLOAT_EQ(matrix->f(), 50);
ctx->reset();
matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
cc::PaintFlags clear_flags;
clear_flags.setBlendMode(SkBlendMode::kClear);
EXPECT_THAT(
ctx->GetRecord(),
RecordedOpsAre(PaintOpEq<DrawRectOp>(
SkRect::MakeXYWH(0, 0, kWidth, kHeight), clear_flags),
PaintOpEq<ConcatOp>(SkM44(zoom, 0, 0, 0, //
0, zoom, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1))));
}
TEST(PaintRenderingContext2DTest, resetWithDefaultDeviceScaleFactor) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
DOMMatrix* matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
ctx->setTransform(1.2, 2.3, 3.4, 4.5, 56, 67);
matrix = ctx->getTransform();
EXPECT_FLOAT_EQ(matrix->a(), 1.2);
EXPECT_FLOAT_EQ(matrix->b(), 2.3);
EXPECT_FLOAT_EQ(matrix->c(), 3.4);
EXPECT_FLOAT_EQ(matrix->d(), 4.5);
EXPECT_FLOAT_EQ(matrix->e(), 56);
EXPECT_FLOAT_EQ(matrix->f(), 67);
ctx->reset();
matrix = ctx->getTransform();
EXPECT_TRUE(matrix->isIdentity());
cc::PaintFlags clear_flags;
clear_flags.setBlendMode(SkBlendMode::kClear);
EXPECT_THAT(ctx->GetRecord(),
RecordedOpsAre(PaintOpEq<DrawRectOp>(
SkRect::MakeXYWH(0, 0, kWidth, kHeight), clear_flags)));
}
TEST(PaintRenderingContext2DTest, overdrawOptimizationNotApplied) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
NonThrowableExceptionState exception_state;
ctx->fillRect(1, 1, 1, 1);
ctx->save();
ctx->fillRect(2, 2, 2, 2);
ctx->clearRect(3, 3, 3, 3);
ctx->fillRect(4, 4, 4, 4);
ctx->restore(exception_state);
cc::PaintFlags clear_flags;
clear_flags.setBlendMode(SkBlendMode::kClear);
cc::PaintFlags rect_flags;
rect_flags.setAntiAlias(true);
rect_flags.setFilterQuality(cc::PaintFlags::FilterQuality::kLow);
EXPECT_THAT(
ctx->GetRecord(),
RecordedOpsAre(
PaintOpEq<DrawColorOp>(SkColors::kTransparent, SkBlendMode::kSrc),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(1, 1, 1, 1), rect_flags),
PaintOpEq<SaveOp>(),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(2, 2, 2, 2), rect_flags),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(3, 3, 3, 3), clear_flags),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(4, 4, 4, 4), rect_flags),
PaintOpEq<RestoreOp>()));
}
TEST(PaintRenderingContext2DTest, overdrawOptimizationApplied) {
test::TaskEnvironment task_environment;
PaintRenderingContext2DSettings* context_settings =
PaintRenderingContext2DSettings::Create();
PaintRenderingContext2D* ctx = MakeGarbageCollected<PaintRenderingContext2D>(
gfx::Size(kWidth, kHeight), context_settings, /*zoom=*/1,
scheduler::GetSingleThreadTaskRunnerForTesting());
NonThrowableExceptionState exception_state;
ctx->fillRect(1, 1, 1, 1);
ctx->save();
ctx->fillRect(2, 2, 2, 2);
// Clear the whole canvas, triggering overdraw optimization and discarding all
// previous draw commands.
ctx->clearRect(0, 0, kWidth, kHeight);
ctx->fillRect(3, 3, 3, 3);
ctx->restore(exception_state);
cc::PaintFlags clear_flags;
clear_flags.setBlendMode(SkBlendMode::kClear);
cc::PaintFlags rect_flags;
rect_flags.setAntiAlias(true);
rect_flags.setFilterQuality(cc::PaintFlags::FilterQuality::kLow);
// Draw calls done before the `clearRect` are discarded, but the matrix clip
// stack remains untouched.
EXPECT_THAT(
ctx->GetRecord(),
RecordedOpsAre(
PaintOpEq<SaveOp>(),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(0, 0, kWidth, kHeight),
clear_flags),
PaintOpEq<DrawRectOp>(SkRect::MakeXYWH(3, 3, 3, 3), rect_flags),
PaintOpEq<RestoreOp>()));
}
} // namespace
} // namespace blink