blob: 231192444caf86f8ecaecd85858a1365c23b4e9f [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <canvas/CanvasOpBuffer.h>
#include <canvas/CanvasOps.h>
#include <canvas/CanvasOpRasterizer.h>
#include <tests/common/CallCountingCanvas.h>
#include "SkColor.h"
#include "pipeline/skia/AnimatedDrawables.h"
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::test;
// We lazy
using Op = CanvasOpType;
enum MockTypes {
Lifecycle,
COUNT
};
template<MockTypes T>
struct MockOp;
template<MockTypes T>
struct MockOpContainer {
OpBufferItemHeader<MockTypes> header;
MockOp<T> impl;
};
struct LifecycleTracker {
int ctor_count = 0;
int dtor_count = 0;
int alive() { return ctor_count - dtor_count; }
};
template<>
struct MockOp<MockTypes::Lifecycle> {
MockOp() = delete;
void operator=(const MockOp&) = delete;
MockOp(LifecycleTracker* tracker) : tracker(tracker) {
tracker->ctor_count += 1;
}
MockOp(const MockOp& other) {
tracker = other.tracker;
tracker->ctor_count += 1;
}
~MockOp() {
tracker->dtor_count += 1;
}
LifecycleTracker* tracker = nullptr;
};
using MockBuffer = OpBuffer<MockTypes, MockOpContainer>;
template<typename T>
static int countItems(const T& t) {
int count = 0;
t.for_each([&](auto i) {
count++;
});
return count;
}
TEST(CanvasOp, lifecycleCheck) {
LifecycleTracker tracker;
{
MockBuffer buffer;
buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
.impl = MockOp<MockTypes::Lifecycle>{&tracker}
});
EXPECT_EQ(tracker.alive(), 1);
buffer.clear();
EXPECT_EQ(tracker.alive(), 0);
}
EXPECT_EQ(tracker.alive(), 0);
}
TEST(CanvasOp, lifecycleCheckMove) {
LifecycleTracker tracker;
{
MockBuffer buffer;
buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
.impl = MockOp<MockTypes::Lifecycle>{&tracker}
});
EXPECT_EQ(tracker.alive(), 1);
{
MockBuffer other(std::move(buffer));
EXPECT_EQ(tracker.alive(), 1);
EXPECT_EQ(buffer.size(), 0);
EXPECT_GT(other.size(), 0);
EXPECT_EQ(1, countItems(other));
EXPECT_EQ(0, countItems(buffer));
other.push_container(MockOpContainer<MockTypes::Lifecycle> {
.impl = MockOp<MockTypes::Lifecycle>{&tracker}
});
EXPECT_EQ(2, countItems(other));
EXPECT_EQ(2, tracker.alive());
buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
.impl = MockOp<MockTypes::Lifecycle>{&tracker}
});
EXPECT_EQ(1, countItems(buffer));
EXPECT_EQ(3, tracker.alive());
buffer = std::move(other);
EXPECT_EQ(2, countItems(buffer));
EXPECT_EQ(2, tracker.alive());
}
EXPECT_EQ(2, countItems(buffer));
EXPECT_EQ(2, tracker.alive());
buffer.clear();
EXPECT_EQ(0, countItems(buffer));
EXPECT_EQ(0, tracker.alive());
}
EXPECT_EQ(tracker.alive(), 0);
}
TEST(CanvasOp, simplePush) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push<Op::Save>({});
buffer.push<Op::Save>({});
buffer.push<Op::Restore>({});
EXPECT_GT(buffer.size(), 0);
int saveCount = 0;
int restoreCount = 0;
int otherCount = 0;
buffer.for_each([&](auto op) {
switch (op->type()) {
case Op::Save:
saveCount++;
break;
case Op::Restore:
restoreCount++;
break;
default:
otherCount++;
break;
}
});
EXPECT_EQ(saveCount, 2);
EXPECT_EQ(restoreCount, 1);
EXPECT_EQ(otherCount, 0);
buffer.clear();
int itemCount = 0;
buffer.for_each([&](auto op) {
itemCount++;
});
EXPECT_EQ(itemCount, 0);
buffer.resize(0);
EXPECT_EQ(buffer.size(), 0);
}
TEST(CanvasOp, simpleDrawPaint) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawColor> {
.color = SkColor4f{1, 1, 1, 1},
.mode = SkBlendMode::kSrcIn
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawPaintCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawRect> {
.paint = SkPaint{},
.rect = SkRect::MakeEmpty()
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawRectCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawRoundRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawRoundRect> {
.paint = SkPaint{},
.rect = SkRect::MakeEmpty(),
.rx = 10,
.ry = 10
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawRRectCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawCircle) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawCircle> {
.cx = 5,
.cy = 7,
.radius = 10,
.paint = SkPaint{}
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawOvalCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawOval) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawOval> {
.oval = SkRect::MakeEmpty(),
.paint = SkPaint{}
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawOvalCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawArc) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
buffer.push(CanvasOp<Op::DrawArc> {
.oval = SkRect::MakeWH(100, 100),
.startAngle = 120,
.sweepAngle = 70,
.useCenter = true,
.paint = SkPaint{}
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawArcCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawRoundRectProperty) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
auto left = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1));
auto top = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2));
auto right = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(3));
auto bottom = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(4));
auto radiusX = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5));
auto radiusY = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(6));
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
buffer.push(CanvasOp<Op::DrawRoundRectProperty> {
.left = left,
.top = top,
.right = right,
.bottom = bottom,
.rx = radiusX,
.ry = radiusY,
.paint = propertyPaint
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawRRectCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, simpleDrawCircleProperty) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
auto x = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1));
auto y = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2));
auto radius = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5));
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
buffer.push(CanvasOp<Op::DrawCircleProperty> {
.x = x,
.y = y,
.radius = radius,
.paint = propertyPaint
});
CallCountingCanvas canvas;
EXPECT_EQ(0, canvas.sumTotalDrawCalls());
rasterizeCanvasBuffer(buffer, &canvas);
EXPECT_EQ(1, canvas.drawOvalCount);
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
TEST(CanvasOp, immediateRendering) {
auto canvas = std::make_shared<CallCountingCanvas>();
EXPECT_EQ(0, canvas->sumTotalDrawCalls());
ImmediateModeRasterizer rasterizer{canvas};
auto op = CanvasOp<Op::DrawRect> {
.paint = SkPaint{},
.rect = SkRect::MakeEmpty()
};
EXPECT_TRUE(CanvasOpTraits::can_draw<decltype(op)>);
rasterizer.draw(op);
EXPECT_EQ(1, canvas->drawRectCount);
EXPECT_EQ(1, canvas->sumTotalDrawCalls());
}