blob: 70fe173daaf2c7e9bc3918d228ac7f7afdbb6fbc [file] [log] [blame]
// 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/entity/contents/conical_gradient_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/linear_gradient_contents.h"
#include "impeller/entity/contents/radial_gradient_contents.h"
#include "impeller/entity/contents/solid_color_contents.h"
#include "impeller/entity/contents/sweep_gradient_contents.h"
#include "impeller/geometry/geometry_asserts.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/playground/widgets.h"
#include "third_party/imgui/imgui.h"
#include "txt/platform.h"
////////////////////////////////////////////////////////////////////////////////
// This is for tests of Canvas that are interested the results of rendering
// gradients.
////////////////////////////////////////////////////////////////////////////////
namespace impeller {
namespace testing {
namespace {
void CanRenderLinearGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) {
Canvas canvas;
canvas.Scale(aiks_test->GetContentScale());
Paint paint;
canvas.Translate({100.0f, 0, 0});
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 0.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
TEST_P(AiksTest, CanRenderLinearGradientClamp) {
CanRenderLinearGradient(this, Entity::TileMode::kClamp);
}
TEST_P(AiksTest, CanRenderLinearGradientRepeat) {
CanRenderLinearGradient(this, Entity::TileMode::kRepeat);
}
TEST_P(AiksTest, CanRenderLinearGradientMirror) {
CanRenderLinearGradient(this, Entity::TileMode::kMirror);
}
TEST_P(AiksTest, CanRenderLinearGradientDecal) {
CanRenderLinearGradient(this, Entity::TileMode::kDecal);
}
TEST_P(AiksTest, CanRenderLinearGradientDecalWithColorFilter) {
Canvas canvas;
canvas.Scale(GetContentScale());
Paint paint;
canvas.Translate({100.0f, 0, 0});
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 0.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {200, 200}, std::move(colors), std::move(stops),
Entity::TileMode::kDecal, {});
// Overlay the gradient with 25% green. This should appear as the entire
// rectangle being drawn with 25% green, including the border area outside the
// decal gradient.
paint.color_filter = ColorFilter::MakeBlend(BlendMode::kSourceOver,
Color::Green().WithAlpha(0.25));
paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
static void CanRenderLinearGradientWithDithering(AiksTest* aiks_test,
bool use_dithering) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
// 0xffcccccc --> 0xff333333, taken from
// https://github.com/flutter/flutter/issues/118073#issue-1521699748
std::vector<Color> colors = {Color{0.8, 0.8, 0.8, 1.0},
Color{0.2, 0.2, 0.2, 1.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {800, 500}, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});
paint.dither = use_dithering;
canvas.DrawRect(Rect::MakeXYWH(0, 0, 800, 500), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderLinearGradientWithDitheringDisabled) {
CanRenderLinearGradientWithDithering(this, false);
}
TEST_P(AiksTest, CanRenderLinearGradientWithDitheringEnabled) {
CanRenderLinearGradientWithDithering(this, true);
} // namespace
static void CanRenderRadialGradientWithDithering(AiksTest* aiks_test,
bool use_dithering) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
// #FFF -> #000
std::vector<Color> colors = {Color{1.0, 1.0, 1.0, 1.0},
Color{0.0, 0.0, 0.0, 1.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeRadialGradient(
{600, 600}, 600, std::move(colors), std::move(stops),
Entity::TileMode::kClamp, {});
paint.dither = use_dithering;
canvas.DrawRect(Rect::MakeXYWH(0, 0, 1200, 1200), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderRadialGradientWithDitheringDisabled) {
CanRenderRadialGradientWithDithering(this, false);
}
TEST_P(AiksTest, CanRenderRadialGradientWithDitheringEnabled) {
CanRenderRadialGradientWithDithering(this, true);
}
static void CanRenderSweepGradientWithDithering(AiksTest* aiks_test,
bool use_dithering) {
Canvas canvas;
canvas.Scale(aiks_test->GetContentScale());
Paint paint;
canvas.Translate({100.0, 100.0, 0});
// #FFF -> #000
std::vector<Color> colors = {Color{1.0, 1.0, 1.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.dither = use_dithering;
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderSweepGradientWithDitheringDisabled) {
CanRenderSweepGradientWithDithering(this, false);
}
TEST_P(AiksTest, CanRenderSweepGradientWithDitheringEnabled) {
CanRenderSweepGradientWithDithering(this, true);
}
static void CanRenderConicalGradientWithDithering(AiksTest* aiks_test,
bool use_dithering) {
Canvas canvas;
canvas.Scale(aiks_test->GetContentScale());
Paint paint;
canvas.Translate({100.0, 100.0, 0});
// #FFF -> #000
std::vector<Color> colors = {Color{1.0, 1.0, 1.0, 1.0},
Color{0.0, 0.0, 0.0, 1.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeConicalGradient(
{100, 100}, 100, std::move(colors), std::move(stops), {0, 1}, 0,
Entity::TileMode::kMirror, {});
paint.dither = use_dithering;
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderConicalGradientWithDitheringDisabled) {
CanRenderConicalGradientWithDithering(this, false);
}
TEST_P(AiksTest, CanRenderConicalGradientWithDitheringEnabled) {
CanRenderConicalGradientWithDithering(this, true);
}
namespace {
void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test,
Entity::TileMode tile_mode) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
std::vector<Color> colors = {
Color{0.9568, 0.2627, 0.2118, 1.0}, Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0}, Color{0.1294, 0.5882, 0.9529, 1.0}};
std::vector<Scalar> stops = {0.0, 0.5, 0.5, 1.0};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {500, 500}, std::move(colors), std::move(stops), tile_mode, {});
paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 500, 500), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
// Only clamp is necessary. All tile modes are the same output.
TEST_P(AiksTest, CanRenderLinearGradientWithOverlappingStopsClamp) {
CanRenderLinearGradientWithOverlappingStops(this, Entity::TileMode::kClamp);
}
namespace {
void CanRenderLinearGradientManyColors(AiksTest* aiks_test,
Entity::TileMode tile_mode) {
Canvas canvas;
canvas.Scale(aiks_test->GetContentScale());
Paint paint;
canvas.Translate({100, 100, 0});
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,
};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
paint.color = Color(1.0, 1.0, 1.0, 1.0);
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
canvas.Restore();
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
TEST_P(AiksTest, CanRenderLinearGradientManyColorsClamp) {
CanRenderLinearGradientManyColors(this, Entity::TileMode::kClamp);
}
TEST_P(AiksTest, CanRenderLinearGradientManyColorsRepeat) {
CanRenderLinearGradientManyColors(this, Entity::TileMode::kRepeat);
}
TEST_P(AiksTest, CanRenderLinearGradientManyColorsMirror) {
CanRenderLinearGradientManyColors(this, Entity::TileMode::kMirror);
}
TEST_P(AiksTest, CanRenderLinearGradientManyColorsDecal) {
CanRenderLinearGradientManyColors(this, Entity::TileMode::kDecal);
}
namespace {
void CanRenderLinearGradientWayManyColors(AiksTest* aiks_test,
Entity::TileMode tile_mode) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
auto color = Color{0x1f / 255.0, 0.0, 0x5c / 255.0, 1.0};
std::vector<Color> colors;
std::vector<Scalar> stops;
auto current_stop = 0.0;
for (int i = 0; i < 2000; i++) {
colors.push_back(color);
stops.push_back(current_stop);
current_stop += 1 / 2000.0;
}
stops[2000 - 1] = 1.0;
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
// Only test clamp on purpose since they all look the same.
TEST_P(AiksTest, CanRenderLinearGradientWayManyColorsClamp) {
CanRenderLinearGradientWayManyColors(this, Entity::TileMode::kClamp);
}
TEST_P(AiksTest, CanRenderLinearGradientManyColorsUnevenStops) {
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
const Entity::TileMode tile_modes[] = {
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
static int selected_tile_mode = 0;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
static Matrix matrix = {
1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1 //
};
std::string label = "##1";
for (int i = 0; i < 4; i++) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, &(matrix.vec[i]),
4, NULL, NULL, "%.2f", 0);
label[2]++;
}
ImGui::End();
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
auto tile_mode = tile_modes[selected_tile_mode];
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, 2.0 / 62.0, 4.0 / 62.0, 8.0 / 62.0, 16.0 / 62.0, 32.0 / 62.0, 1.0,
};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {200, 200}, std::move(colors), std::move(stops), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, CanRenderLinearGradientMaskBlur) {
Canvas canvas;
Paint paint = {
.color = Color::White(),
.color_source = ColorSource::MakeLinearGradient(
{200, 200}, {400, 400},
{Color::Red(), Color::White(), Color::Red(), Color::White(),
Color::Red(), Color::White(), Color::Red(), Color::White(),
Color::Red(), Color::White(), Color::Red()},
{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
Entity::TileMode::kClamp, {}),
.mask_blur_descriptor =
Paint::MaskBlurDescriptor{
.style = FilterContents::BlurStyle::kNormal,
.sigma = Sigma(20),
},
};
canvas.DrawCircle({300, 300}, 200, paint);
canvas.DrawRect(Rect::MakeLTRB(100, 300, 500, 600), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderRadialGradient) {
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
const Entity::TileMode tile_modes[] = {
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
static int selected_tile_mode = 0;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
static Matrix matrix = {
1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1 //
};
std::string label = "##1";
for (int i = 0; i < 4; i++) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, &(matrix.vec[i]),
4, NULL, NULL, "%.2f", 0);
label[2]++;
}
ImGui::End();
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
auto tile_mode = tile_modes[selected_tile_mode];
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeRadialGradient(
{100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, CanRenderRadialGradientManyColors) {
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
const Entity::TileMode tile_modes[] = {
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
static int selected_tile_mode = 0;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
static Matrix matrix = {
1, 0, 0, 0, //
0, 1, 0, 0, //
0, 0, 1, 0, //
0, 0, 0, 1 //
};
std::string label = "##1";
for (int i = 0; i < 4; i++) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float, &(matrix.vec[i]),
4, NULL, NULL, "%.2f", 0);
label[2]++;
}
ImGui::End();
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
auto tile_mode = tile_modes[selected_tile_mode];
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,
};
paint.color_source = ColorSource::MakeRadialGradient(
{100, 100}, 100, std::move(colors), std::move(stops), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
namespace {
void CanRenderSweepGradient(AiksTest* aiks_test, Entity::TileMode tile_mode) {
Canvas canvas;
canvas.Scale(aiks_test->GetContentScale());
Paint paint;
canvas.Translate({100, 100, 0});
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 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), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
TEST_P(AiksTest, CanRenderSweepGradientClamp) {
CanRenderSweepGradient(this, Entity::TileMode::kClamp);
}
TEST_P(AiksTest, CanRenderSweepGradientRepeat) {
CanRenderSweepGradient(this, Entity::TileMode::kRepeat);
}
TEST_P(AiksTest, CanRenderSweepGradientMirror) {
CanRenderSweepGradient(this, Entity::TileMode::kMirror);
}
TEST_P(AiksTest, CanRenderSweepGradientDecal) {
CanRenderSweepGradient(this, Entity::TileMode::kDecal);
}
namespace {
void CanRenderSweepGradientManyColors(AiksTest* aiks_test,
Entity::TileMode tile_mode) {
Canvas canvas;
Paint paint;
canvas.Translate({100.0, 100.0, 0});
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,
};
paint.color_source = ColorSource::MakeSweepGradient(
{100, 100}, Degrees(45), Degrees(135), std::move(colors),
std::move(stops), tile_mode, {});
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
ASSERT_TRUE(aiks_test->OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
} // namespace
TEST_P(AiksTest, CanRenderSweepGradientManyColorsClamp) {
CanRenderSweepGradientManyColors(this, Entity::TileMode::kClamp);
}
TEST_P(AiksTest, CanRenderSweepGradientManyColorsRepeat) {
CanRenderSweepGradientManyColors(this, Entity::TileMode::kRepeat);
}
TEST_P(AiksTest, CanRenderSweepGradientManyColorsMirror) {
CanRenderSweepGradientManyColors(this, Entity::TileMode::kMirror);
}
TEST_P(AiksTest, CanRenderSweepGradientManyColorsDecal) {
CanRenderSweepGradientManyColors(this, Entity::TileMode::kDecal);
}
TEST_P(AiksTest, CanRenderConicalGradient) {
Scalar size = 256;
Canvas canvas;
Paint paint;
paint.color = Color::White();
canvas.DrawRect(Rect::MakeXYWH(0, 0, size * 3, size * 3), paint);
std::vector<Color> colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF),
Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF),
Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF),
Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)};
std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
std::array<std::tuple<Point, float, Point, float>, 8> array{
std::make_tuple(Point{size / 2.f, size / 2.f}, 0.f,
Point{size / 2.f, size / 2.f}, size / 2.f),
std::make_tuple(Point{size / 2.f, size / 2.f}, size / 4.f,
Point{size / 2.f, size / 2.f}, size / 2.f),
std::make_tuple(Point{size / 4.f, size / 4.f}, 0.f,
Point{size / 2.f, size / 2.f}, size / 2.f),
std::make_tuple(Point{size / 4.f, size / 4.f}, size / 2.f,
Point{size / 2.f, size / 2.f}, 0),
std::make_tuple(Point{size / 4.f, size / 4.f}, size / 4.f,
Point{size / 2.f, size / 2.f}, size / 2.f),
std::make_tuple(Point{size / 4.f, size / 4.f}, size / 16.f,
Point{size / 2.f, size / 2.f}, size / 8.f),
std::make_tuple(Point{size / 4.f, size / 4.f}, size / 8.f,
Point{size / 2.f, size / 2.f}, size / 16.f),
std::make_tuple(Point{size / 8.f, size / 8.f}, size / 8.f,
Point{size / 2.f, size / 2.f}, size / 8.f),
};
for (int i = 0; i < 8; i++) {
canvas.Save();
canvas.Translate({(i % 3) * size, i / 3 * size, 0});
paint.color_source = ColorSource::MakeConicalGradient(
std::get<0>(array[i]), std::get<1>(array[i]), colors, stops,
std::get<2>(array[i]), std::get<3>(array[i]), Entity::TileMode::kClamp,
{});
canvas.DrawRect(Rect::MakeXYWH(0, 0, size, size), paint);
canvas.Restore();
}
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanRenderGradientDecalWithBackground) {
std::vector<Color> colors = {Color::MakeRGBA8(0xF4, 0x43, 0x36, 0xFF),
Color::MakeRGBA8(0xFF, 0xEB, 0x3B, 0xFF),
Color::MakeRGBA8(0x4c, 0xAF, 0x50, 0xFF),
Color::MakeRGBA8(0x21, 0x96, 0xF3, 0xFF)};
std::vector<Scalar> stops = {0.0, 1.f / 3.f, 2.f / 3.f, 1.0};
std::array<ColorSource, 3> color_sources = {
ColorSource::MakeLinearGradient({0, 0}, {100, 100}, colors, stops,
Entity::TileMode::kDecal, {}),
ColorSource::MakeRadialGradient({100, 100}, 100, colors, stops,
Entity::TileMode::kDecal, {}),
ColorSource::MakeSweepGradient({100, 100}, Degrees(45), Degrees(135),
colors, stops, Entity::TileMode::kDecal,
{}),
};
Canvas canvas;
Paint paint;
paint.color = Color::White();
canvas.DrawRect(Rect::MakeLTRB(0, 0, 605, 205), paint);
for (int i = 0; i < 3; i++) {
canvas.Save();
canvas.Translate({i * 200.0f, 0, 0});
paint.color_source = color_sources[i];
canvas.DrawRect(Rect::MakeLTRB(0, 0, 200, 200), paint);
canvas.Restore();
}
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
#define APPLY_COLOR_FILTER_GRADIENT_TEST(name) \
TEST_P(AiksTest, name##GradientApplyColorFilter) { \
auto contents = name##GradientContents(); \
contents.SetColors({Color::CornflowerBlue().WithAlpha(0.75)}); \
auto result = contents.ApplyColorFilter([](const Color& color) { \
return color.Blend(Color::LimeGreen().WithAlpha(0.75), \
BlendMode::kScreen); \
}); \
ASSERT_TRUE(result); \
\
std::vector<Color> expected = {Color(0.433247, 0.879523, 0.825324, 0.75)}; \
ASSERT_COLORS_NEAR(contents.GetColors(), expected); \
}
APPLY_COLOR_FILTER_GRADIENT_TEST(Linear);
APPLY_COLOR_FILTER_GRADIENT_TEST(Radial);
APPLY_COLOR_FILTER_GRADIENT_TEST(Conical);
APPLY_COLOR_FILTER_GRADIENT_TEST(Sweep);
TEST_P(AiksTest, GradientStrokesRenderCorrectly) {
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
static float scale = 3;
static bool add_circle_clip = true;
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
const Entity::TileMode tile_modes[] = {
Entity::TileMode::kClamp, Entity::TileMode::kRepeat,
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
static int selected_tile_mode = 0;
static float alpha = 1;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Scale", &scale, 0, 6);
ImGui::Checkbox("Circle clip", &add_circle_clip);
ImGui::SliderFloat("Alpha", &alpha, 0, 1);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
ImGui::End();
Canvas canvas;
canvas.Scale(GetContentScale());
Paint paint;
paint.color = Color::White();
canvas.DrawPaint(paint);
paint.style = Paint::Style::kStroke;
paint.color = Color(1.0, 1.0, 1.0, alpha);
paint.stroke_width = 10;
auto tile_mode = tile_modes[selected_tile_mode];
std::vector<Color> colors = {Color{0.9568, 0.2627, 0.2118, 1.0},
Color{0.1294, 0.5882, 0.9529, 1.0}};
std::vector<Scalar> stops = {0.0, 1.0};
paint.color_source = ColorSource::MakeLinearGradient(
{0, 0}, {50, 50}, std::move(colors), std::move(stops), tile_mode, {});
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, paint);
canvas.Translate({80, 0});
}
canvas.Translate({-240, 60});
}
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
} // namespace testing
} // namespace impeller