diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 3e41760234bc0..cfcf89ac869ec 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -124,6 +124,7 @@ ../../../flutter/impeller/.gitignore ../../../flutter/impeller/README.md ../../../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_recorder_unittests.cc diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 845a2bde8eb71..f1d7d8ef681ac 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -85,6 +85,7 @@ template("aiks_unittests_component") { target_name = invoker.target_name predefined_sources = [ "aiks_gradient_unittests.cc", + "aiks_path_unittests.cc", "aiks_unittests.cc", "aiks_unittests.h", ] diff --git a/impeller/aiks/aiks_path_unittests.cc b/impeller/aiks/aiks_path_unittests.cc new file mode 100644 index 0000000000000..47b835b1c45e9 --- /dev/null +++ b/impeller/aiks/aiks_path_unittests.cc @@ -0,0 +1,396 @@ +// 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, RotateColorFilteredPath) { + Canvas canvas; + canvas.Concat(Matrix::MakeTranslation({300, 300})); + canvas.Concat(Matrix::MakeRotationZ(Radians(kPiOver2))); + auto arrow_stem = + PathBuilder{}.MoveTo({120, 190}).LineTo({120, 50}).TakePath(); + auto arrow_head = PathBuilder{} + .MoveTo({50, 120}) + .LineTo({120, 190}) + .LineTo({190, 120}) + .TakePath(); + auto paint = Paint{ + .stroke_width = 15.0, + .stroke_cap = Cap::kRound, + .stroke_join = Join::kRound, + .style = Paint::Style::kStroke, + .color_filter = + ColorFilter::MakeBlend(BlendMode::kSourceIn, Color::AliceBlue()), + }; + + canvas.DrawPath(std::move(arrow_stem), paint); + canvas.DrawPath(std::move(arrow_head), paint); + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + +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, 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, 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(CreateTextureForFixture("boston.jpg")), {10, 10}, + Paint{}); + canvas.DrawPath(std::move(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 { + static Color color = Color::Black().WithAlpha(0.5); + static float scale = 3; + static bool add_circle_clip = true; + + ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::ColorEdit4("Color", reinterpret_cast(&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) { + auto [handle_a, handle_b] = IMPELLER_PLAYGROUND_LINE( + Point(60, 300), Point(600, 300), 20, Color::Red(), Color::Red()); + + 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.Clone(), 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 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 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 colors = {Color{1.0, 0.0, 0.0, 1.0}, + Color{0.0, 0.0, 0.0, 1.0}}; + std::vector 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())); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 61ddc200d3f83..bc9b3d6ccb451 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -53,31 +53,6 @@ namespace testing { INSTANTIATE_PLAYGROUND_SUITE(AiksTest); -TEST_P(AiksTest, RotateColorFilteredPath) { - Canvas canvas; - canvas.Concat(Matrix::MakeTranslation({300, 300})); - canvas.Concat(Matrix::MakeRotationZ(Radians(kPiOver2))); - auto arrow_stem = - PathBuilder{}.MoveTo({120, 190}).LineTo({120, 50}).TakePath(); - auto arrow_head = PathBuilder{} - .MoveTo({50, 120}) - .LineTo({120, 190}) - .LineTo({190, 120}) - .TakePath(); - auto paint = Paint{ - .stroke_width = 15.0, - .stroke_cap = Cap::kRound, - .stroke_join = Join::kRound, - .style = Paint::Style::kStroke, - .color_filter = - ColorFilter::MakeBlend(BlendMode::kSourceIn, Color::AliceBlue()), - }; - - canvas.DrawPath(std::move(arrow_stem), paint); - canvas.DrawPath(std::move(arrow_head), paint); - ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); -} - TEST_P(AiksTest, CanvasCTMCanBeUpdated) { Canvas canvas; Matrix identity; @@ -273,47 +248,6 @@ TEST_P(AiksTest, CanRenderImageRect) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -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, 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, CanRenderSimpleClips) { Canvas canvas; canvas.Scale(GetContentScale()); @@ -740,111 +674,6 @@ TEST_P(AiksTest, CanRenderRoundedRectWithNonUniformRadii) { 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, 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(CreateTextureForFixture("boston.jpg")), {10, 10}, - Paint{}); - canvas.DrawPath(std::move(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())); -} - struct TextRenderOptions { Scalar font_size = 50; Color color = Color::Yellow(); @@ -1415,133 +1244,6 @@ TEST_P(AiksTest, TransformMultipliesCorrectly) { // clang-format on } -TEST_P(AiksTest, SolidStrokesRenderCorrectly) { - // Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2 - auto callback = [&](AiksContext& renderer) -> std::optional { - static Color color = Color::Black().WithAlpha(0.5); - static float scale = 3; - static bool add_circle_clip = true; - - ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::ColorEdit4("Color", reinterpret_cast(&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) { - auto [handle_a, handle_b] = IMPELLER_PLAYGROUND_LINE( - Point(60, 300), Point(600, 300), 20, Color::Red(), Color::Red()); - - 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.Clone(), 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 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 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, FilledCirclesRenderCorrectly) { Canvas canvas; canvas.Scale(GetContentScale()); @@ -1946,37 +1648,6 @@ TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } -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, SaveLayerDrawsBehindSubsequentEntities) { // Compare with https://fiddle.skia.org/c/9e03de8567ffb49e7e83f53b64bcf636 Canvas canvas; @@ -3290,26 +2961,6 @@ TEST_P(AiksTest, CanCanvasDrawPictureWithAdvancedBlend) { 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, CanCanvasDrawPictureWithBackdropFilter) { Canvas subcanvas; subcanvas.SaveLayer({}, {}, @@ -3830,32 +3481,6 @@ TEST_P(AiksTest, BlurredRectangleWithShader) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -TEST_P(AiksTest, ArcWithZeroSweepAndBlur) { - Canvas canvas; - canvas.Scale(GetContentScale()); - - Paint paint; - paint.color = Color::Red(); - std::vector colors = {Color{1.0, 0.0, 0.0, 1.0}, - Color{0.0, 0.0, 0.0, 1.0}}; - std::vector 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, MaskBlurWithZeroSigmaIsSkipped) { Canvas canvas;