[Impeller] move Aiks path unittests to DL (flutter/engine#53799)
Move more impeller tests to display list format. This allows our integration tests to also test the display list -> aiks interop and be future compatible with the new canvas backend that removes AIKS. Part of https://github.com/flutter/flutter/issues/142054
This commit is contained in:
parent
e2b3f88cac
commit
50c6b6f326
@ -127,7 +127,6 @@
|
||||
../../../flutter/impeller/aiks/aiks_blend_unittests.cc
|
||||
../../../flutter/impeller/aiks/aiks_blur_unittests.cc
|
||||
../../../flutter/impeller/aiks/aiks_gradient_unittests.cc
|
||||
../../../flutter/impeller/aiks/aiks_path_unittests.cc
|
||||
../../../flutter/impeller/aiks/aiks_unittests.cc
|
||||
../../../flutter/impeller/aiks/aiks_unittests.h
|
||||
../../../flutter/impeller/aiks/canvas_unittests.cc
|
||||
|
@ -14,6 +14,15 @@ struct DlColor {
|
||||
constexpr DlColor() : argb_(0xFF000000) {}
|
||||
constexpr explicit DlColor(uint32_t argb) : argb_(argb) {}
|
||||
|
||||
/// @brief Construct a 32 bit color from a floating point A, R, G, and B color
|
||||
/// channel.
|
||||
constexpr explicit DlColor(SkScalar a, SkScalar r, SkScalar g, SkScalar b)
|
||||
: argb_(static_cast<uint8_t>(std::round(a * 255)) << 24 | //
|
||||
static_cast<uint8_t>(std::round(r * 255)) << 16 | //
|
||||
static_cast<uint8_t>(std::round(g * 255)) << 8 | //
|
||||
static_cast<uint8_t>(std::round(b * 255)) << 0 //
|
||||
) {}
|
||||
|
||||
static constexpr uint8_t toAlpha(SkScalar opacity) { return toC(opacity); }
|
||||
static constexpr SkScalar toOpacity(uint8_t alpha) { return toF(alpha); }
|
||||
|
||||
|
@ -78,7 +78,6 @@ template("aiks_unittests_component") {
|
||||
"aiks_blend_unittests.cc",
|
||||
"aiks_blur_unittests.cc",
|
||||
"aiks_gradient_unittests.cc",
|
||||
"aiks_path_unittests.cc",
|
||||
"aiks_unittests.cc",
|
||||
"aiks_unittests.h",
|
||||
]
|
||||
|
@ -1,460 +0,0 @@
|
||||
// Copyright 2013 The Flutter 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 "flutter/impeller/aiks/aiks_unittests.h"
|
||||
|
||||
#include "impeller/aiks/canvas.h"
|
||||
#include "impeller/geometry/path_builder.h"
|
||||
#include "impeller/playground/widgets.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This is for tests of Canvas that are interested the results of rendering
|
||||
// paths.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokes) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.stroke_width = 20.0;
|
||||
paint.style = Paint::Style::kStroke;
|
||||
canvas.DrawPath(PathBuilder{}.AddLine({200, 100}, {800, 100}).TakePath(),
|
||||
paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderCurvedStrokes) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.stroke_width = 25.0;
|
||||
paint.style = Paint::Style::kStroke;
|
||||
canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.stroke_width = 100.0;
|
||||
paint.style = Paint::Style::kStroke;
|
||||
canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
// Impeller doesn't support hairlines yet, but size this guarantees
|
||||
// the smallest possible stroke width.
|
||||
paint.stroke_width = 0.01;
|
||||
paint.style = Paint::Style::kStroke;
|
||||
canvas.DrawPath(PathBuilder{}.AddCircle({100, 100}, 50).TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
|
||||
Canvas canvas;
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 200;
|
||||
|
||||
Rect rect = Rect::MakeXYWH(100, 100, 200, 200);
|
||||
PathBuilder builder;
|
||||
builder.AddArc(rect, Degrees(0), Degrees(90), false);
|
||||
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
|
||||
Canvas canvas;
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 20;
|
||||
|
||||
PathBuilder builder;
|
||||
builder.AddCubicCurve({0, 200}, {50, 400}, {350, 0}, {400, 200});
|
||||
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderQuadraticStrokeWithInstantTurn) {
|
||||
Canvas canvas;
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_cap = Cap::kRound;
|
||||
paint.stroke_width = 50;
|
||||
|
||||
// Should draw a diagonal pill shape. If flat on either end, the stroke is
|
||||
// rendering wrong.
|
||||
PathBuilder builder;
|
||||
builder.MoveTo({250, 250});
|
||||
builder.QuadraticCurveTo({100, 100}, {250, 250});
|
||||
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderDifferencePaths) {
|
||||
Canvas canvas;
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
|
||||
PathBuilder builder;
|
||||
|
||||
PathBuilder::RoundingRadii radii;
|
||||
radii.top_left = {50, 25};
|
||||
radii.top_right = {25, 50};
|
||||
radii.bottom_right = {50, 25};
|
||||
radii.bottom_left = {25, 50};
|
||||
|
||||
builder.AddRoundedRect(Rect::MakeXYWH(100, 100, 200, 200), radii);
|
||||
builder.AddCircle({200, 200}, 50);
|
||||
auto path = builder.TakePath(FillType::kOdd);
|
||||
|
||||
canvas.DrawImage(
|
||||
std::make_shared<Image>(CreateTextureForFixture("boston.jpg")), {10, 10},
|
||||
Paint{});
|
||||
canvas.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/134816.
|
||||
//
|
||||
// It should be possible to draw 3 lines, and not have an implicit close path.
|
||||
TEST_P(AiksTest, CanDrawAnOpenPath) {
|
||||
Canvas canvas;
|
||||
|
||||
// Starting at (50, 50), draw lines from:
|
||||
// 1. (50, height)
|
||||
// 2. (width, height)
|
||||
// 3. (width, 50)
|
||||
PathBuilder builder;
|
||||
builder.MoveTo({50, 50});
|
||||
builder.LineTo({50, 100});
|
||||
builder.LineTo({100, 100});
|
||||
builder.LineTo({100, 50});
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 10;
|
||||
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
|
||||
Canvas canvas;
|
||||
|
||||
// Draw a stroked path that is explicitly closed to verify
|
||||
// It doesn't become a rectangle.
|
||||
PathBuilder builder;
|
||||
builder.MoveTo({50, 50});
|
||||
builder.LineTo({520, 120});
|
||||
builder.LineTo({300, 310});
|
||||
builder.LineTo({100, 50});
|
||||
builder.Close();
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 10;
|
||||
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
|
||||
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
|
||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
|
||||
static Color color = Color::Black().WithAlpha(0.5);
|
||||
static float scale = 3;
|
||||
static bool add_circle_clip = true;
|
||||
|
||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
|
||||
ImGui::SliderFloat("Scale", &scale, 0, 6);
|
||||
ImGui::Checkbox("Circle clip", &add_circle_clip);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
Paint paint;
|
||||
|
||||
paint.color = Color::White();
|
||||
canvas.DrawPaint(paint);
|
||||
|
||||
paint.color = color;
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 10;
|
||||
|
||||
Path path = PathBuilder{}
|
||||
.MoveTo({20, 20})
|
||||
.QuadraticCurveTo({60, 20}, {60, 60})
|
||||
.Close()
|
||||
.MoveTo({60, 20})
|
||||
.QuadraticCurveTo({60, 60}, {20, 60})
|
||||
.TakePath();
|
||||
|
||||
canvas.Scale(Vector2(scale, scale));
|
||||
|
||||
if (add_circle_clip) {
|
||||
static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
|
||||
Color::Red());
|
||||
static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
|
||||
Color::Red());
|
||||
auto [handle_a, handle_b] =
|
||||
DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
|
||||
|
||||
auto screen_to_canvas = canvas.GetCurrentTransform().Invert();
|
||||
Point point_a = screen_to_canvas * handle_a * GetContentScale();
|
||||
Point point_b = screen_to_canvas * handle_b * GetContentScale();
|
||||
|
||||
Point middle = (point_a + point_b) / 2;
|
||||
auto radius = point_a.GetDistance(middle);
|
||||
canvas.ClipPath(PathBuilder{}.AddCircle(middle, radius).TakePath());
|
||||
}
|
||||
|
||||
for (auto join : {Join::kBevel, Join::kRound, Join::kMiter}) {
|
||||
paint.stroke_join = join;
|
||||
for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
|
||||
paint.stroke_cap = cap;
|
||||
canvas.DrawPath(path, paint);
|
||||
canvas.Translate({80, 0});
|
||||
}
|
||||
canvas.Translate({-240, 60});
|
||||
}
|
||||
|
||||
return canvas.EndRecordingAsPicture();
|
||||
};
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawLinesRenderCorrectly) {
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
Paint paint;
|
||||
paint.color = Color::Blue();
|
||||
paint.stroke_width = 10;
|
||||
|
||||
auto draw = [&canvas](Paint& paint) {
|
||||
for (auto cap : {Cap::kButt, Cap::kSquare, Cap::kRound}) {
|
||||
paint.stroke_cap = cap;
|
||||
Point origin = {100, 100};
|
||||
Point p0 = {50, 0};
|
||||
Point p1 = {150, 0};
|
||||
canvas.DrawLine({150, 100}, {250, 100}, paint);
|
||||
for (int d = 15; d < 90; d += 15) {
|
||||
Matrix m = Matrix::MakeRotationZ(Degrees(d));
|
||||
canvas.DrawLine(origin + m * p0, origin + m * p1, paint);
|
||||
}
|
||||
canvas.DrawLine({100, 150}, {100, 250}, paint);
|
||||
canvas.DrawCircle({origin}, 35, paint);
|
||||
|
||||
canvas.DrawLine({250, 250}, {250, 250}, paint);
|
||||
|
||||
canvas.Translate({250, 0});
|
||||
}
|
||||
canvas.Translate({-750, 250});
|
||||
};
|
||||
|
||||
std::vector<Color> colors = {
|
||||
Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0},
|
||||
Color{0x5b / 255.0, 0.0, 0x60 / 255.0, 1.0},
|
||||
Color{0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0, 1.0},
|
||||
Color{0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0, 1.0},
|
||||
Color{0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0, 1.0},
|
||||
Color{0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0, 1.0},
|
||||
Color{0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0, 1.0}};
|
||||
std::vector<Scalar> stops = {
|
||||
0.0,
|
||||
(1.0 / 6.0) * 1,
|
||||
(1.0 / 6.0) * 2,
|
||||
(1.0 / 6.0) * 3,
|
||||
(1.0 / 6.0) * 4,
|
||||
(1.0 / 6.0) * 5,
|
||||
1.0,
|
||||
};
|
||||
|
||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||
/*enable_mipmapping=*/true);
|
||||
|
||||
draw(paint);
|
||||
|
||||
paint.color_source = ColorSource::MakeRadialGradient(
|
||||
{100, 100}, 200, std::move(colors), std::move(stops),
|
||||
Entity::TileMode::kMirror, {});
|
||||
draw(paint);
|
||||
|
||||
paint.color_source = ColorSource::MakeImage(
|
||||
texture, Entity::TileMode::kRepeat, Entity::TileMode::kRepeat, {},
|
||||
Matrix::MakeTranslation({-150, 75}));
|
||||
draw(paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 10;
|
||||
|
||||
canvas.Translate({100, 100});
|
||||
canvas.DrawPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeSize(Size{100, 100})).TakePath(),
|
||||
{paint});
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
paint.style = Paint::Style::kStroke;
|
||||
paint.stroke_width = 10;
|
||||
paint.stroke_join = Join::kBevel;
|
||||
|
||||
canvas.Translate({100, 100});
|
||||
canvas.DrawPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeSize(Size{100, 100})).TakePath(),
|
||||
{paint});
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
|
||||
PathBuilder builder = {};
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
if (i % 2 == 0) {
|
||||
builder.AddCircle(Point(100 + 50 * i, 100 + 50 * i), 100);
|
||||
} else {
|
||||
builder.MoveTo({100.f + 50.f * i - 100, 100.f + 50.f * i});
|
||||
builder.LineTo({100.f + 50.f * i, 100.f + 50.f * i - 100});
|
||||
builder.LineTo({100.f + 50.f * i - 100, 100.f + 50.f * i - 100});
|
||||
builder.Close();
|
||||
}
|
||||
}
|
||||
builder.SetConvexity(Convexity::kConvex);
|
||||
|
||||
Canvas canvas;
|
||||
canvas.DrawPath(builder.TakePath(), {.color = Color::Red().WithAlpha(0.4)});
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
std::vector<Color> colors = {Color{1.0, 0.0, 0.0, 1.0},
|
||||
Color{0.0, 0.0, 0.0, 1.0}};
|
||||
std::vector<Scalar> stops = {0.0, 1.0};
|
||||
paint.color_source = ColorSource::MakeSweepGradient(
|
||||
{100, 100}, Degrees(45), Degrees(135), std::move(colors),
|
||||
std::move(stops), Entity::TileMode::kMirror, {});
|
||||
paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
|
||||
.style = FilterContents::BlurStyle::kNormal,
|
||||
.sigma = Sigma(20),
|
||||
};
|
||||
|
||||
PathBuilder builder;
|
||||
builder.AddArc(Rect::MakeXYWH(10, 10, 100, 100), Degrees(0), Degrees(0),
|
||||
false);
|
||||
canvas.DrawPath(builder.TakePath(), paint);
|
||||
|
||||
// Check that this empty picture can be created without crashing.
|
||||
canvas.EndRecordingAsPicture();
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderClips) {
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
paint.color = Color::Fuchsia();
|
||||
canvas.ClipPath(
|
||||
PathBuilder{}.AddRect(Rect::MakeXYWH(0, 0, 500, 500)).TakePath());
|
||||
canvas.DrawPath(PathBuilder{}.AddCircle({500, 500}, 250).TakePath(), paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
|
||||
Canvas canvas;
|
||||
|
||||
Paint paint;
|
||||
paint.color = Color::Red();
|
||||
|
||||
PathBuilder::RoundingRadii radii;
|
||||
radii.top_left = {50, 50};
|
||||
radii.top_right = {50, 50};
|
||||
radii.bottom_right = {50, 50};
|
||||
radii.bottom_left = {50, 50};
|
||||
|
||||
const Scalar kTriangleHeight = 100;
|
||||
canvas.Translate(Vector2(200, 200));
|
||||
// Form a path similar to the Material drop slider value indicator. Both
|
||||
// shapes should render identically side-by-side.
|
||||
{
|
||||
auto path =
|
||||
PathBuilder{}
|
||||
.MoveTo({0, kTriangleHeight})
|
||||
.LineTo({-kTriangleHeight / 2.0f, 0})
|
||||
.LineTo({kTriangleHeight / 2.0f, 0})
|
||||
.Close()
|
||||
.AddRoundedRect(
|
||||
Rect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
|
||||
kTriangleHeight, kTriangleHeight),
|
||||
radii)
|
||||
.TakePath();
|
||||
|
||||
canvas.DrawPath(path, paint);
|
||||
}
|
||||
canvas.Translate(Vector2(100, 0));
|
||||
{
|
||||
auto path =
|
||||
PathBuilder{}
|
||||
.MoveTo({0, kTriangleHeight})
|
||||
.LineTo({-kTriangleHeight / 2.0f, 0})
|
||||
.LineTo({0, -10})
|
||||
.LineTo({kTriangleHeight / 2.0f, 0})
|
||||
.Close()
|
||||
.AddRoundedRect(
|
||||
Rect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
|
||||
kTriangleHeight, kTriangleHeight),
|
||||
radii)
|
||||
.TakePath();
|
||||
|
||||
canvas.DrawPath(path, paint);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
@ -2570,5 +2570,4 @@ TEST_P(AiksTest, SetContentsWithRegion) {
|
||||
// █ - aiks_blend_unittests.cc
|
||||
// █ - aiks_blur_unittests.cc
|
||||
// █ - aiks_gradient_unittests.cc
|
||||
// █ - aiks_path_unittests.cc
|
||||
// █████████████████████████████████████████████████████████████████████████████
|
||||
|
@ -476,7 +476,12 @@ void Canvas::DrawRect(const Rect& rect, const Paint& paint) {
|
||||
}
|
||||
|
||||
void Canvas::DrawOval(const Rect& rect, const Paint& paint) {
|
||||
if (rect.IsSquare()) {
|
||||
// TODO(jonahwilliams): This additional condition avoids an assert in the
|
||||
// stroke circle geometry generator. I need to verify the condition that this
|
||||
// assert prevents.
|
||||
if (rect.IsSquare() && (paint.style == Paint::Style::kFill ||
|
||||
(paint.style == Paint::Style::kStroke &&
|
||||
paint.stroke_width < rect.GetWidth()))) {
|
||||
// Circles have slightly less overhead and can do stroking
|
||||
DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
|
||||
return;
|
||||
|
@ -2,6 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "display_list/dl_sampling_options.h"
|
||||
#include "display_list/dl_tile_mode.h"
|
||||
#include "display_list/effects/dl_color_source.h"
|
||||
#include "display_list/effects/dl_mask_filter.h"
|
||||
#include "flutter/impeller/aiks/aiks_unittests.h"
|
||||
|
||||
#include "flutter/display_list/dl_blend_mode.h"
|
||||
@ -10,6 +14,13 @@
|
||||
#include "flutter/display_list/dl_paint.h"
|
||||
#include "flutter/display_list/effects/dl_color_filter.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "impeller/display_list/dl_image_impeller.h"
|
||||
#include "impeller/playground/widgets.h"
|
||||
|
||||
#include "include/core/SkMatrix.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkPathTypes.h"
|
||||
#include "include/core/SkRRect.h"
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
@ -42,5 +53,458 @@ TEST_P(AiksTest, RotateColorFilteredPath) {
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokes) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(20);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
builder.DrawPath(SkPath::Line({200, 100}, {800, 100}), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderCurvedStrokes) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(25);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderThickCurvedStrokes) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(100);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderThinCurvedStrokes) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(0.01);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
builder.DrawPath(SkPath::Circle(100, 100, 50), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokePathThatEndsAtSharpTurn) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(200);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
SkPath path;
|
||||
path.arcTo(SkRect::MakeXYWH(100, 100, 200, 200), 0, 90, false);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderStrokePathWithCubicLine) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(20);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
|
||||
SkPath path;
|
||||
path.moveTo(0, 200);
|
||||
path.cubicTo(50, 400, 350, 0, 400, 200);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderQuadraticStrokeWithInstantTurn) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setStrokeWidth(50);
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeCap(DlStrokeCap::kRound);
|
||||
|
||||
// Should draw a diagonal pill shape. If flat on either end, the stroke is
|
||||
// rendering wrong.
|
||||
SkPath path;
|
||||
path.moveTo(250, 250);
|
||||
path.quadTo(100, 100, 250, 250);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderDifferencePaths) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
|
||||
SkPoint radii[4] = {{50, 25}, {25, 50}, {50, 25}, {25, 50}};
|
||||
SkPath path;
|
||||
SkRRect rrect;
|
||||
rrect.setRectRadii(SkRect::MakeXYWH(100, 100, 200, 200), radii);
|
||||
path.addRRect(rrect);
|
||||
path.addCircle(200, 200, 50);
|
||||
path.setFillType(SkPathFillType::kEvenOdd);
|
||||
|
||||
builder.DrawImage(
|
||||
DlImageImpeller::Make(CreateTextureForFixture("boston.jpg")), {10, 10},
|
||||
{});
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/134816.
|
||||
//
|
||||
// It should be possible to draw 3 lines, and not have an implicit close path.
|
||||
TEST_P(AiksTest, CanDrawAnOpenPath) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
// Starting at (50, 50), draw lines from:
|
||||
// 1. (50, height)
|
||||
// 2. (width, height)
|
||||
// 3. (width, 50)
|
||||
SkPath path;
|
||||
path.moveTo(50, 50);
|
||||
path.lineTo(50, 100);
|
||||
path.lineTo(100, 100);
|
||||
path.lineTo(100, 50);
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeWidth(10);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanDrawAnOpenPathThatIsntARect) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
// Draw a stroked path that is explicitly closed to verify
|
||||
// It doesn't become a rectangle.
|
||||
SkPath path;
|
||||
// PathBuilder builder;
|
||||
path.moveTo(50, 50);
|
||||
path.lineTo(520, 120);
|
||||
path.lineTo(300, 310);
|
||||
path.lineTo(100, 50);
|
||||
path.close();
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeWidth(10);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
|
||||
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
|
||||
auto callback = [&]() -> sk_sp<DisplayList> {
|
||||
static Color color = Color::Black().WithAlpha(0.5);
|
||||
static float scale = 3;
|
||||
static bool add_circle_clip = true;
|
||||
|
||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&color));
|
||||
ImGui::SliderFloat("Scale", &scale, 0, 6);
|
||||
ImGui::Checkbox("Circle clip", &add_circle_clip);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
DisplayListBuilder builder;
|
||||
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||
DlPaint paint;
|
||||
|
||||
paint.setColor(DlColor::kWhite());
|
||||
builder.DrawPaint(paint);
|
||||
|
||||
paint.setColor(DlColor(color.alpha, color.red, color.green, color.blue));
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeWidth(10);
|
||||
|
||||
SkPath path;
|
||||
path.moveTo({20, 20});
|
||||
path.quadTo({60, 20}, {60, 60});
|
||||
path.close();
|
||||
path.moveTo({60, 20});
|
||||
path.quadTo({60, 60}, {20, 60});
|
||||
|
||||
builder.Scale(scale, scale);
|
||||
|
||||
if (add_circle_clip) {
|
||||
static PlaygroundPoint circle_clip_point_a(Point(60, 300), 20,
|
||||
Color::Red());
|
||||
static PlaygroundPoint circle_clip_point_b(Point(600, 300), 20,
|
||||
Color::Red());
|
||||
auto [handle_a, handle_b] =
|
||||
DrawPlaygroundLine(circle_clip_point_a, circle_clip_point_b);
|
||||
|
||||
SkMatrix screen_to_canvas = SkMatrix::I();
|
||||
if (!builder.GetTransform().invert(&screen_to_canvas)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkPoint point_a =
|
||||
screen_to_canvas.mapPoint(SkPoint::Make(handle_a.x, handle_a.y));
|
||||
SkPoint point_b =
|
||||
screen_to_canvas.mapPoint(SkPoint::Make(handle_b.x, handle_b.y));
|
||||
|
||||
SkPoint middle = point_a + point_b;
|
||||
middle.scale(GetContentScale().x / 2);
|
||||
|
||||
auto radius = SkPoint::Distance(point_a, middle);
|
||||
|
||||
builder.ClipPath(SkPath::Circle(middle.x(), middle.y(), radius));
|
||||
}
|
||||
|
||||
for (auto join :
|
||||
{DlStrokeJoin::kBevel, DlStrokeJoin::kRound, DlStrokeJoin::kMiter}) {
|
||||
paint.setStrokeJoin(join);
|
||||
for (auto cap :
|
||||
{DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
|
||||
paint.setStrokeCap(cap);
|
||||
builder.DrawPath(path, paint);
|
||||
builder.Translate(80, 0);
|
||||
}
|
||||
builder.Translate(-240, 60);
|
||||
}
|
||||
|
||||
return builder.Build();
|
||||
};
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawLinesRenderCorrectly) {
|
||||
DisplayListBuilder builder;
|
||||
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kBlue());
|
||||
paint.setStrokeWidth(10);
|
||||
|
||||
auto draw = [&builder](DlPaint& paint) {
|
||||
for (auto cap :
|
||||
{DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) {
|
||||
paint.setStrokeCap(cap);
|
||||
SkPoint origin = {100, 100};
|
||||
builder.DrawLine({150, 100}, {250, 100}, paint);
|
||||
for (int d = 15; d < 90; d += 15) {
|
||||
Matrix m = Matrix::MakeRotationZ(Degrees(d));
|
||||
Point origin = {100, 100};
|
||||
Point p0 = {50, 0};
|
||||
Point p1 = {150, 0};
|
||||
auto a = origin + m * p0;
|
||||
auto b = origin + m * p1;
|
||||
|
||||
builder.DrawLine(SkPoint::Make(a.x, a.y), SkPoint::Make(b.x, b.y),
|
||||
paint);
|
||||
}
|
||||
builder.DrawLine({100, 150}, {100, 250}, paint);
|
||||
builder.DrawCircle({origin}, 35, paint);
|
||||
|
||||
builder.DrawLine({250, 250}, {250, 250}, paint);
|
||||
|
||||
builder.Translate(250, 0);
|
||||
}
|
||||
builder.Translate(-750, 250);
|
||||
};
|
||||
|
||||
std::vector<DlColor> colors = {
|
||||
DlColor{1, 0x1f / 255.0, 0.0, 0x5c / 255.0},
|
||||
DlColor{1, 0x5b / 255.0, 0.0, 0x60 / 255.0},
|
||||
DlColor{1, 0x87 / 255.0, 0x01 / 255.0, 0x60 / 255.0},
|
||||
DlColor{1, 0xac / 255.0, 0x25 / 255.0, 0x53 / 255.0},
|
||||
DlColor{1, 0xe1 / 255.0, 0x6b / 255.0, 0x5c / 255.0},
|
||||
DlColor{1, 0xf3 / 255.0, 0x90 / 255.0, 0x60 / 255.0},
|
||||
DlColor{1, 0xff / 255.0, 0xb5 / 255.0, 0x6b / 250.0}};
|
||||
std::vector<Scalar> stops = {
|
||||
0.0,
|
||||
(1.0 / 6.0) * 1,
|
||||
(1.0 / 6.0) * 2,
|
||||
(1.0 / 6.0) * 3,
|
||||
(1.0 / 6.0) * 4,
|
||||
(1.0 / 6.0) * 5,
|
||||
1.0,
|
||||
};
|
||||
|
||||
auto texture = DlImageImpeller::Make(
|
||||
CreateTextureForFixture("airplane.jpg",
|
||||
/*enable_mipmapping=*/true));
|
||||
|
||||
draw(paint);
|
||||
|
||||
paint.setColorSource(DlColorSource::MakeRadial({100, 100}, 200, stops.size(),
|
||||
colors.data(), stops.data(),
|
||||
DlTileMode::kMirror));
|
||||
draw(paint);
|
||||
|
||||
SkMatrix matrix = SkMatrix::Translate(-150, 75);
|
||||
paint.setColorSource(std::make_shared<DlImageColorSource>(
|
||||
texture, DlTileMode::kRepeat, DlTileMode::kRepeat,
|
||||
DlImageSampling::kMipmapLinear, &matrix));
|
||||
draw(paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeWidth(10);
|
||||
|
||||
builder.Translate(100, 100);
|
||||
builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), {paint});
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawRectStrokesWithBevelJoinRenderCorrectly) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||
paint.setStrokeWidth(10);
|
||||
paint.setStrokeJoin(DlStrokeJoin::kBevel);
|
||||
|
||||
builder.Translate(100, 100);
|
||||
builder.DrawPath(SkPath::Rect(SkRect::MakeSize(SkSize{100, 100})), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanDrawMultiContourConvexPath) {
|
||||
SkPath path;
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
if (i % 2 == 0) {
|
||||
path.addCircle(100 + 50 * i, 100 + 50 * i, 100);
|
||||
path.close();
|
||||
} else {
|
||||
path.moveTo({100.f + 50.f * i - 100, 100.f + 50.f * i});
|
||||
path.lineTo({100.f + 50.f * i, 100.f + 50.f * i - 100});
|
||||
path.lineTo({100.f + 50.f * i - 100, 100.f + 50.f * i - 100});
|
||||
path.close();
|
||||
}
|
||||
}
|
||||
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed().withAlpha(102));
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, ArcWithZeroSweepAndBlur) {
|
||||
DisplayListBuilder builder;
|
||||
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
|
||||
std::vector<DlColor> colors = {DlColor{1.0, 0.0, 0.0, 1.0},
|
||||
DlColor{0.0, 0.0, 0.0, 1.0}};
|
||||
std::vector<Scalar> stops = {0.0, 1.0};
|
||||
|
||||
paint.setColorSource(
|
||||
DlColorSource::MakeSweep({100, 100}, 45, 135, stops.size(), colors.data(),
|
||||
stops.data(), DlTileMode::kMirror));
|
||||
paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 20));
|
||||
|
||||
SkPath path;
|
||||
path.addArc(SkRect::MakeXYWH(10, 10, 100, 100), 0, 0);
|
||||
builder.DrawPath(path, paint);
|
||||
|
||||
// Check that this empty picture can be created without crashing.
|
||||
builder.Build();
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderClips) {
|
||||
DisplayListBuilder builder;
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kFuchsia());
|
||||
|
||||
builder.ClipPath(SkPath::Rect(SkRect::MakeXYWH(0, 0, 500, 500)));
|
||||
builder.DrawPath(SkPath::Circle(500, 500, 250), paint);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderOverlappingMultiContourPath) {
|
||||
DisplayListBuilder builder;
|
||||
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kRed());
|
||||
|
||||
SkPoint radii[4] = {{50, 50}, {50, 50}, {50, 50}, {50, 50}};
|
||||
|
||||
const Scalar kTriangleHeight = 100;
|
||||
SkRRect rrect;
|
||||
rrect.setRectRadii(
|
||||
SkRect::MakeXYWH(-kTriangleHeight / 2.0f, -kTriangleHeight / 2.0f,
|
||||
kTriangleHeight, kTriangleHeight),
|
||||
radii //
|
||||
);
|
||||
|
||||
builder.Translate(200, 200);
|
||||
// Form a path similar to the Material drop slider value indicator. Both
|
||||
// shapes should render identically side-by-side.
|
||||
{
|
||||
SkPath path;
|
||||
path.moveTo(0, kTriangleHeight);
|
||||
path.lineTo(-kTriangleHeight / 2.0f, 0);
|
||||
path.lineTo(kTriangleHeight / 2.0f, 0);
|
||||
path.close();
|
||||
path.addRRect(rrect);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
}
|
||||
builder.Translate(100, 0);
|
||||
|
||||
{
|
||||
SkPath path;
|
||||
path.moveTo(0, kTriangleHeight);
|
||||
path.lineTo(-kTriangleHeight / 2.0f, 0);
|
||||
path.lineTo(0, -10);
|
||||
path.lineTo(kTriangleHeight / 2.0f, 0);
|
||||
path.close();
|
||||
path.addRRect(rrect);
|
||||
|
||||
builder.DrawPath(path, paint);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
Loading…
x
Reference in New Issue
Block a user