blob: 4bbf0840eb64b7ec0597ee670919437894ef6da8 [file] [log] [blame]
// Copyright 2016 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 "modules/canvas2d/CanvasRenderingContext2D.h"
#include "bindings/core/v8/V8BindingForCore.h"
#include "core/dom/Document.h"
#include "core/frame/FrameView.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/ImageData.h"
#include "core/loader/EmptyClients.h"
#include "core/testing/DummyPageHolder.h"
#include "modules/accessibility/AXObjectCacheImpl.h"
#include "modules/canvas2d/CanvasGradient.h"
#include "modules/canvas2d/CanvasPattern.h"
#include "modules/canvas2d/HitRegionOptions.h"
#include "modules/canvas2d/Path2D.h"
#include "modules/webgl/WebGLRenderingContext.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/StaticBitmapImage.h"
#include "platform/graphics/UnacceleratedImageBufferSurface.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkSurface.h"
using ::testing::Mock;
namespace blink {
namespace UsageTrackingTests {
enum BitmapOpacity { kOpaqueBitmap, kTransparentBitmap };
class FakeImageSource : public CanvasImageSource {
public:
FakeImageSource(IntSize, BitmapOpacity);
PassRefPtr<Image> GetSourceImageForCanvas(SourceImageStatus*,
AccelerationHint,
SnapshotReason,
const FloatSize&) override;
bool WouldTaintOrigin(
SecurityOrigin* destination_security_origin) const override {
return false;
}
FloatSize ElementSize(const FloatSize&) const override {
return FloatSize(size_);
}
bool IsOpaque() const override { return is_opaque_; }
bool IsAccelerated() const override { return false; }
int SourceWidth() override { return size_.Width(); }
int SourceHeight() override { return size_.Height(); }
~FakeImageSource() override {}
private:
IntSize size_;
RefPtr<Image> image_;
bool is_opaque_;
};
FakeImageSource::FakeImageSource(IntSize size, BitmapOpacity opacity)
: size_(size), is_opaque_(opacity == kOpaqueBitmap) {
sk_sp<SkSurface> surface(
SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height()));
surface->getCanvas()->clear(opacity == kOpaqueBitmap ? SK_ColorWHITE
: SK_ColorTRANSPARENT);
image_ = StaticBitmapImage::Create(surface->makeImageSnapshot());
}
PassRefPtr<Image> FakeImageSource::GetSourceImageForCanvas(
SourceImageStatus* status,
AccelerationHint,
SnapshotReason,
const FloatSize&) {
if (status)
*status = kNormalSourceImageStatus;
return image_;
}
class CanvasRenderingContextUsageTrackingTest : public ::testing::Test {
protected:
CanvasRenderingContextUsageTrackingTest();
void SetUp() override;
DummyPageHolder& Page() const { return *dummy_page_holder_; }
Document& GetDocument() const { return *document_; }
HTMLCanvasElement& CanvasElement() const { return *canvas_element_; }
CanvasRenderingContext2D* Context2d() const;
void CreateContext(OpacityMode);
FakeImageSource opaque_bitmap_;
FakeImageSource alpha_bitmap_;
Persistent<ImageData> full_image_data_;
void TearDown();
ScriptState* GetScriptState() {
return ToScriptStateForMainWorld(canvas_element_->GetFrame());
}
private:
std::shared_ptr<DummyPageHolder> dummy_page_holder_;
Persistent<Document> document_;
Persistent<HTMLCanvasElement> canvas_element_;
Persistent<MemoryCache> global_memory_cache_;
};
CanvasRenderingContextUsageTrackingTest::
CanvasRenderingContextUsageTrackingTest()
: opaque_bitmap_(IntSize(10, 10), kOpaqueBitmap),
alpha_bitmap_(IntSize(10, 10), kTransparentBitmap) {}
void CanvasRenderingContextUsageTrackingTest::TearDown() {
ThreadState::Current()->CollectGarbage(BlinkGC::kNoHeapPointersOnStack,
BlinkGC::kGCWithSweep,
BlinkGC::kForcedGC);
ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
}
CanvasRenderingContext2D* CanvasRenderingContextUsageTrackingTest::Context2d()
const {
// If the following check fails, perhaps you forgot to call createContext
// in your test?
EXPECT_NE(nullptr, CanvasElement().RenderingContext());
EXPECT_TRUE(CanvasElement().RenderingContext()->Is2d());
return static_cast<CanvasRenderingContext2D*>(
CanvasElement().RenderingContext());
}
void CanvasRenderingContextUsageTrackingTest::CreateContext(
OpacityMode opacity_mode) {
String canvas_type("2d");
CanvasContextCreationAttributes attributes;
attributes.setAlpha(opacity_mode == kNonOpaque);
canvas_element_->GetCanvasRenderingContext(canvas_type, attributes);
Context2d(); // Calling this for the checks
}
void CanvasRenderingContextUsageTrackingTest::SetUp() {
Page::PageClients page_clients;
FillWithEmptyClients(page_clients);
dummy_page_holder_ =
DummyPageHolder::Create(IntSize(800, 600), &page_clients);
document_ = &dummy_page_holder_->GetDocument();
document_->documentElement()->setInnerHTML(
"<body><canvas id='c'></canvas></body>");
document_->View()->UpdateAllLifecyclePhases();
canvas_element_ = toHTMLCanvasElement(document_->getElementById("c"));
full_image_data_ = ImageData::Create(IntSize(10, 10));
global_memory_cache_ = ReplaceMemoryCacheForTesting(MemoryCache::Create());
RuntimeEnabledFeatures::setEnableCanvas2dDynamicRenderingModeSwitchingEnabled(
true);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, FillTracking) {
CreateContext(kNonOpaque);
int num_reps = 100;
for (int i = 0; i < num_reps; i++) {
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(2, 2);
Context2d()->lineTo(1, 4);
Context2d()->fill(); // path is not convex
Context2d()->fillText("Hello World!!!", 10, 10, 1);
Context2d()->fillRect(10, 10, 100, 100);
Context2d()->fillText("Hello World!!!", 10, 10);
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(1, 4);
Context2d()->fill(); // path is convex
Context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(num_reps * 2,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillPath]);
EXPECT_EQ(num_reps * 2,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillRect]);
EXPECT_EQ(num_reps, Context2d()->GetUsage().num_non_convex_fill_path_calls);
EXPECT_EQ(num_reps * 2,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillText]);
// areas
EXPECT_NEAR(
static_cast<double>(num_reps * (100 * 100) * 2),
Context2d()
->GetUsage()
.bounding_box_area_draw_calls[BaseRenderingContext2D::kFillRect],
1.0);
EXPECT_NEAR(
static_cast<double>(num_reps * (2 * 100 + 2 * 100) * 2),
Context2d()
->GetUsage()
.bounding_box_perimeter_draw_calls[BaseRenderingContext2D::kFillRect],
1.0);
EXPECT_TRUE(
static_cast<double>(num_reps * (100 * 100) * 2) <
Context2d()
->GetUsage()
.bounding_box_area_fill_type[BaseRenderingContext2D::kColorFillType]);
EXPECT_TRUE(
static_cast<double>(num_reps * (100 * 100) * 2) * 1.1 >
Context2d()
->GetUsage()
.bounding_box_area_fill_type[BaseRenderingContext2D::kColorFillType]);
EXPECT_NEAR(
static_cast<double>(num_reps * (3 * 3) * 2),
Context2d()
->GetUsage()
.bounding_box_area_draw_calls[BaseRenderingContext2D::kFillPath],
1.0);
EXPECT_NEAR(
static_cast<double>(num_reps * (2 * 3 + 2 * 3) * 2),
Context2d()
->GetUsage()
.bounding_box_perimeter_draw_calls[BaseRenderingContext2D::kFillPath],
1.0);
EXPECT_TRUE(Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kFillText] > 0.0);
EXPECT_TRUE(Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kStrokeText] == 0.0);
// create gradient
CanvasGradient* gradient;
gradient = Context2d()->createLinearGradient(0, 0, 100, 100);
Context2d()->setFillStyle(
StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
Context2d()->fillRect(10, 10, 100, 20);
EXPECT_EQ(1, Context2d()->GetUsage().num_linear_gradients);
EXPECT_NEAR(100 * 20,
Context2d()->GetUsage().bounding_box_area_fill_type
[BaseRenderingContext2D::kLinearGradientFillType],
1.0);
NonThrowableExceptionState exception_state;
gradient = Context2d()->createRadialGradient(0, 0, 100, 100, 200, 200,
exception_state);
Context2d()->setFillStyle(
StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
Context2d()->fillRect(10, 10, 100, 20);
EXPECT_EQ(1, Context2d()->GetUsage().num_linear_gradients);
EXPECT_EQ(1, Context2d()->GetUsage().num_radial_gradients);
EXPECT_NEAR(100 * 20,
Context2d()->GetUsage().bounding_box_area_fill_type
[BaseRenderingContext2D::kRadialGradientFillType],
1.0);
// create pattern
NonThrowableExceptionState exception_state2;
CanvasPattern* canvas_pattern = Context2d()->createPattern(
GetScriptState(), &opaque_bitmap_, "repeat-x", exception_state2);
StringOrCanvasGradientOrCanvasPattern pattern =
StringOrCanvasGradientOrCanvasPattern::fromCanvasPattern(canvas_pattern);
Context2d()->setFillStyle(pattern);
Context2d()->fillRect(10, 10, 200, 100);
EXPECT_EQ(1, Context2d()->GetUsage().num_patterns);
EXPECT_NEAR(200 * 100,
Context2d()->GetUsage().bounding_box_area_fill_type
[BaseRenderingContext2D::kPatternFillType],
1.0);
// Other fill method
Context2d()->fill(Path2D::Create("Hello World!!!"));
EXPECT_EQ(num_reps * 2 + 1,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillPath]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokePath]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokeText]);
EXPECT_EQ(0, Context2d()->GetUsage().num_put_image_data_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kDrawVectorImage]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kDrawBitmapImage]);
EXPECT_EQ(0, Context2d()->GetUsage().num_get_image_data_calls);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, StrokeTracking) {
CreateContext(kNonOpaque);
int num_reps = 100;
for (int i = 0; i < num_reps; i++) {
Context2d()->strokeRect(10, 10, 100, 100);
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(2, 2);
Context2d()->lineTo(1, 4);
Context2d()->stroke();
Context2d()->strokeText("Hello World!!!", 10, 10, 1);
Context2d()->strokeRect(10, 10, 100, 100);
Context2d()->strokeText("Hello World!!!", 10, 10);
}
EXPECT_EQ(num_reps, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokePath]);
EXPECT_EQ(num_reps * 2,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokeRect]);
EXPECT_EQ(num_reps * 2,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokeText]);
EXPECT_NEAR(
static_cast<double>(num_reps * (100 * 100) * 2),
Context2d()
->GetUsage()
.bounding_box_area_draw_calls[BaseRenderingContext2D::kStrokeRect],
1.0);
EXPECT_NEAR(static_cast<double>(num_reps * (2 * 100 + 2 * 100) * 2),
Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kStrokeRect],
1.0);
EXPECT_NEAR(
static_cast<double>(num_reps * (3 * 3)),
Context2d()
->GetUsage()
.bounding_box_area_draw_calls[BaseRenderingContext2D::kStrokePath],
1.0);
EXPECT_NEAR(static_cast<double>(num_reps * (2 * 3 + 2 * 3)),
Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kStrokePath],
1.0);
// create gradient
CanvasGradient* gradient;
gradient = Context2d()->createLinearGradient(0, 0, 100, 100);
Context2d()->setStrokeStyle(
StringOrCanvasGradientOrCanvasPattern::fromCanvasGradient(gradient));
Context2d()->strokeRect(10, 10, 100, 100);
EXPECT_EQ(1, Context2d()->GetUsage().num_linear_gradients);
EXPECT_NEAR(100 * 100,
Context2d()->GetUsage().bounding_box_area_fill_type
[BaseRenderingContext2D::kLinearGradientFillType],
1.0);
// create pattern
NonThrowableExceptionState exception_state;
CanvasPattern* canvas_pattern = Context2d()->createPattern(
GetScriptState(), &opaque_bitmap_, "repeat-x", exception_state);
StringOrCanvasGradientOrCanvasPattern pattern =
StringOrCanvasGradientOrCanvasPattern::fromCanvasPattern(canvas_pattern);
Context2d()->setStrokeStyle(pattern);
Context2d()->strokeRect(10, 10, 100, 100);
EXPECT_EQ(1, Context2d()->GetUsage().num_patterns);
EXPECT_NEAR(100 * 100,
Context2d()->GetUsage().bounding_box_area_fill_type
[BaseRenderingContext2D::kPatternFillType],
1.0);
// Other stroke method
Context2d()->stroke(Path2D::Create("Hello World!!!"));
EXPECT_EQ(num_reps + 1,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokePath]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillPath]);
EXPECT_EQ(0, Context2d()->GetUsage().num_non_convex_fill_path_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillText]);
EXPECT_EQ(0, Context2d()->GetUsage().num_put_image_data_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kDrawVectorImage]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kDrawBitmapImage]);
EXPECT_EQ(0, Context2d()->GetUsage().num_get_image_data_calls);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ImageTracking) {
// Testing draw, put, get and filters
CreateContext(kNonOpaque);
NonThrowableExceptionState exception_state;
int num_reps = 5;
int img_width = 100;
int img_height = 200;
for (int i = 0; i < num_reps; i++) {
Context2d()->putImageData(full_image_data_.Get(), 0, 0, exception_state);
Context2d()->drawImage(GetScriptState(), &opaque_bitmap_, 0, 0, 1, 1, 0, 0,
img_width, img_height, exception_state);
Context2d()->getImageData(0, 0, 10, 100, exception_state);
}
EXPECT_NEAR(num_reps * img_width * img_height,
Context2d()->GetUsage().bounding_box_area_draw_calls
[BaseRenderingContext2D::kDrawBitmapImage],
0.1);
EXPECT_NEAR(num_reps * (2 * img_width + 2 * img_height),
Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kDrawBitmapImage],
0.1);
EXPECT_NEAR(0.0,
Context2d()->GetUsage().bounding_box_area_draw_calls
[BaseRenderingContext2D::kDrawVectorImage],
0.1);
EXPECT_NEAR(0.0,
Context2d()->GetUsage().bounding_box_perimeter_draw_calls
[BaseRenderingContext2D::kDrawVectorImage],
0.1);
Context2d()->setFilter("blur(5px)");
Context2d()->drawImage(GetScriptState(), &opaque_bitmap_, 0, 0, 1, 1, 0, 0,
10, 10, exception_state);
EXPECT_EQ(num_reps, Context2d()->GetUsage().num_put_image_data_calls);
EXPECT_NE(0, Context2d()->GetUsage().area_put_image_data_calls);
EXPECT_NEAR(num_reps * full_image_data_.Get()->width() *
full_image_data_.Get()->height(),
Context2d()->GetUsage().area_put_image_data_calls, 0.1);
EXPECT_EQ(num_reps + 1,
Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kDrawBitmapImage]);
EXPECT_EQ(num_reps, Context2d()->GetUsage().num_get_image_data_calls);
EXPECT_EQ(1, Context2d()->GetUsage().num_filters);
EXPECT_NEAR(num_reps * 10 * 100,
Context2d()->GetUsage().area_get_image_data_calls, 0.1);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillPath]);
EXPECT_EQ(0, Context2d()->GetUsage().num_non_convex_fill_path_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillText]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokePath]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokeText]);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ClearRectTracking) {
CreateContext(kNonOpaque);
Context2d()->clearRect(0, 0, 1, 1);
Context2d()->clearRect(0, 0, 2, 2);
EXPECT_EQ(2, Context2d()->GetUsage().num_clear_rect_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillPath]);
EXPECT_EQ(0, Context2d()->GetUsage().num_non_convex_fill_path_calls);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillText]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokePath]);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kStrokeText]);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ComplexClipsTracking) {
CreateContext(kNonOpaque);
Context2d()->save();
// simple clip
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(1, 4);
Context2d()->clip("nonzero");
Context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(0, Context2d()->GetUsage().num_draw_with_complex_clips);
// complex clips
int num_reps = 5;
for (int i = 0; i < num_reps; i++) {
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(2, 2);
Context2d()->lineTo(1, 4);
Context2d()->clip("nonzero");
Context2d()->fillRect(0, 0, 1, 1);
}
// remove all previous clips
Context2d()->restore();
// simple clip again
Context2d()->beginPath();
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(1, 4);
Context2d()->clip("nonzero");
Context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(num_reps, Context2d()->GetUsage().num_draw_with_complex_clips);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, ShadowsTracking) {
CreateContext(kNonOpaque);
Context2d()->beginPath();
Context2d()->fillRect(0, 0, 1, 1);
// shadow with blur = 0
Context2d()->beginPath();
Context2d()->setShadowBlur(0.0);
Context2d()->setShadowColor("rgba(255, 0, 0, 0.5)");
Context2d()->setShadowOffsetX(1.0);
Context2d()->setShadowOffsetY(1.0);
Context2d()->fillRect(0, 0, 1, 1);
// shadow with alpha = 0
Context2d()->beginPath();
Context2d()->setShadowBlur(0.5);
Context2d()->setShadowColor("rgba(255, 0, 0, 0)");
Context2d()->setShadowOffsetX(1.0);
Context2d()->setShadowOffsetY(1.0);
Context2d()->fillRect(0, 0, 1, 1);
EXPECT_EQ(0, Context2d()->GetUsage().num_blurred_shadows);
EXPECT_NEAR(
0.0, Context2d()->GetUsage().bounding_box_area_times_shadow_blur_squared,
0.1);
EXPECT_NEAR(
0.0,
Context2d()->GetUsage().bounding_box_perimeter_times_shadow_blur_squared,
0.1);
int num_reps = 5;
double shadow_blur = 0.5;
for (int i = 0; i < num_reps; i++) {
Context2d()->beginPath();
Context2d()->setShadowBlur(shadow_blur);
Context2d()->setShadowColor("rgba(255, 0, 0, 0.5)");
Context2d()->setShadowOffsetX(1.0);
Context2d()->setShadowOffsetY(1.0);
Context2d()->fillRect(0, 0, 3, 10);
}
EXPECT_EQ(num_reps, Context2d()->GetUsage().num_blurred_shadows);
double area_times_shadow_blur_squared =
shadow_blur * shadow_blur * num_reps * 3 * 10;
double perimeter_times_shadow_blur_squared =
shadow_blur * shadow_blur * num_reps * (2 * 3 + 2 * 10);
EXPECT_NEAR(
area_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_area_times_shadow_blur_squared, 0.1);
EXPECT_NEAR(
perimeter_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_perimeter_times_shadow_blur_squared,
0.1);
double shadow_blur2 = 4.5;
Context2d()->beginPath();
Context2d()->setShadowBlur(shadow_blur2);
Context2d()->moveTo(1, 1);
Context2d()->lineTo(4, 1);
Context2d()->lineTo(4, 4);
Context2d()->lineTo(2, 2);
Context2d()->lineTo(1, 4);
Context2d()->fill();
area_times_shadow_blur_squared += 3.0 * 3.0 * shadow_blur2 * shadow_blur2;
perimeter_times_shadow_blur_squared +=
(2 * 3 + 2 * 3) * shadow_blur2 * shadow_blur2;
EXPECT_NEAR(
area_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_area_times_shadow_blur_squared, 0.1);
EXPECT_NEAR(
perimeter_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_perimeter_times_shadow_blur_squared,
0.1);
NonThrowableExceptionState exception_state;
int img_width = 100;
int img_height = 200;
for (int i = 0; i < num_reps; i++) {
Context2d()->putImageData(full_image_data_.Get(), 0, 0, exception_state);
Context2d()->setShadowBlur(shadow_blur2);
Context2d()->drawImage(GetScriptState(), &opaque_bitmap_, 0, 0, 1, 1, 0, 0,
img_width, img_height, exception_state);
Context2d()->getImageData(0, 0, 1, 1, exception_state);
}
area_times_shadow_blur_squared +=
img_width * img_height * shadow_blur2 * shadow_blur2 * num_reps;
perimeter_times_shadow_blur_squared +=
(2 * img_width + 2 * img_height) * shadow_blur2 * shadow_blur2 * num_reps;
EXPECT_NEAR(
area_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_area_times_shadow_blur_squared, 0.1);
EXPECT_NEAR(
perimeter_times_shadow_blur_squared,
Context2d()->GetUsage().bounding_box_perimeter_times_shadow_blur_squared,
0.1);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, incrementFrameCount) {
CreateContext(kNonOpaque);
EXPECT_EQ(0, Context2d()->GetUsage().num_frames_since_reset);
Context2d()->FinalizeFrame();
EXPECT_EQ(1, Context2d()->GetUsage().num_frames_since_reset);
Context2d()->FinalizeFrame();
Context2d()->FinalizeFrame();
EXPECT_EQ(3, Context2d()->GetUsage().num_frames_since_reset);
}
TEST_F(CanvasRenderingContextUsageTrackingTest, resetUsageTracking) {
CreateContext(kNonOpaque);
EXPECT_EQ(0, Context2d()->GetUsage().num_frames_since_reset);
Context2d()->FinalizeFrame();
Context2d()->FinalizeFrame();
EXPECT_EQ(2, Context2d()->GetUsage().num_frames_since_reset);
const int kNumReps = 100;
for (int i = 0; i < kNumReps; i++) {
Context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(kNumReps, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillRect]);
Context2d()->ResetUsageTracking();
EXPECT_EQ(0, Context2d()->GetUsage().num_frames_since_reset);
EXPECT_EQ(0, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillRect]);
for (int i = 0; i < kNumReps; i++) {
Context2d()->fillRect(10, 10, 100, 100);
}
EXPECT_EQ(kNumReps, Context2d()
->GetUsage()
.num_draw_calls[BaseRenderingContext2D::kFillRect]);
}
} // namespace UsageTrackingTests
} // namespace blink