[Impeller] migrate blend tests to DL. (flutter/engine#54457)
Part of https://github.com/flutter/flutter/issues/142054
This commit is contained in:
parent
c96be54305
commit
720cae8ed3
@ -144,6 +144,7 @@
|
|||||||
../../../flutter/impeller/core/allocator_unittests.cc
|
../../../flutter/impeller/core/allocator_unittests.cc
|
||||||
../../../flutter/impeller/display_list/aiks_dl_atlas_unittests.cc
|
../../../flutter/impeller/display_list/aiks_dl_atlas_unittests.cc
|
||||||
../../../flutter/impeller/display_list/aiks_dl_basic_unittests.cc
|
../../../flutter/impeller/display_list/aiks_dl_basic_unittests.cc
|
||||||
|
../../../flutter/impeller/display_list/aiks_dl_blend_unittests.cc
|
||||||
../../../flutter/impeller/display_list/aiks_dl_clip_unittests.cc
|
../../../flutter/impeller/display_list/aiks_dl_clip_unittests.cc
|
||||||
../../../flutter/impeller/display_list/aiks_dl_gradient_unittests.cc
|
../../../flutter/impeller/display_list/aiks_dl_gradient_unittests.cc
|
||||||
../../../flutter/impeller/display_list/aiks_dl_opacity_unittests.cc
|
../../../flutter/impeller/display_list/aiks_dl_opacity_unittests.cc
|
||||||
|
@ -665,7 +665,7 @@ class DlColorFilterImageFilter final : public DlImageFilter {
|
|||||||
class DlLocalMatrixImageFilter final : public DlImageFilter {
|
class DlLocalMatrixImageFilter final : public DlImageFilter {
|
||||||
public:
|
public:
|
||||||
explicit DlLocalMatrixImageFilter(const SkMatrix& matrix,
|
explicit DlLocalMatrixImageFilter(const SkMatrix& matrix,
|
||||||
std::shared_ptr<DlImageFilter> filter)
|
std::shared_ptr<const DlImageFilter> filter)
|
||||||
: matrix_(matrix), image_filter_(std::move(filter)) {}
|
: matrix_(matrix), image_filter_(std::move(filter)) {}
|
||||||
explicit DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter* filter)
|
explicit DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter* filter)
|
||||||
: DlLocalMatrixImageFilter(filter->matrix_, filter->image_filter_) {}
|
: DlLocalMatrixImageFilter(filter->matrix_, filter->image_filter_) {}
|
||||||
@ -682,7 +682,7 @@ class DlLocalMatrixImageFilter final : public DlImageFilter {
|
|||||||
|
|
||||||
const SkMatrix& matrix() const { return matrix_; }
|
const SkMatrix& matrix() const { return matrix_; }
|
||||||
|
|
||||||
const std::shared_ptr<DlImageFilter> image_filter() const {
|
const std::shared_ptr<const DlImageFilter> image_filter() const {
|
||||||
return image_filter_;
|
return image_filter_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,7 +738,7 @@ class DlLocalMatrixImageFilter final : public DlImageFilter {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
SkMatrix matrix_;
|
SkMatrix matrix_;
|
||||||
std::shared_ptr<DlImageFilter> image_filter_;
|
std::shared_ptr<const DlImageFilter> image_filter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace flutter
|
} // namespace flutter
|
||||||
|
@ -18,166 +18,6 @@
|
|||||||
namespace impeller {
|
namespace impeller {
|
||||||
namespace testing {
|
namespace testing {
|
||||||
|
|
||||||
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer) {
|
|
||||||
Canvas canvas;
|
|
||||||
|
|
||||||
Rect layer_rect = Rect::MakeXYWH(0, 0, 500, 500);
|
|
||||||
canvas.ClipRect(layer_rect);
|
|
||||||
|
|
||||||
canvas.SaveLayer(
|
|
||||||
{
|
|
||||||
.color_filter = ColorFilter::MakeBlend(BlendMode::kDifference,
|
|
||||||
Color(0, 1, 0, 0.5)),
|
|
||||||
},
|
|
||||||
layer_rect);
|
|
||||||
|
|
||||||
Paint paint;
|
|
||||||
canvas.DrawPaint({.color = Color::Black()});
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 300, 300),
|
|
||||||
{.color = Color::White()});
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, BlendModeShouldCoverWholeScreen) {
|
|
||||||
Canvas canvas;
|
|
||||||
Paint paint;
|
|
||||||
|
|
||||||
paint.color = Color::Red();
|
|
||||||
canvas.DrawPaint(paint);
|
|
||||||
|
|
||||||
paint.blend_mode = BlendMode::kSourceOver;
|
|
||||||
canvas.SaveLayer(paint);
|
|
||||||
|
|
||||||
paint.color = Color::White();
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
|
|
||||||
|
|
||||||
paint.blend_mode = BlendMode::kSource;
|
|
||||||
canvas.SaveLayer(paint);
|
|
||||||
|
|
||||||
paint.color = Color::Blue();
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(200, 200, 200, 200), paint);
|
|
||||||
|
|
||||||
canvas.Restore();
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, CanDrawPaintWithAdvancedBlend) {
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(Vector2(0.2, 0.2));
|
|
||||||
canvas.DrawPaint({.color = Color::MediumTurquoise()});
|
|
||||||
canvas.DrawPaint({.color = Color::Color::OrangeRed().WithAlpha(0.5),
|
|
||||||
.blend_mode = BlendMode::kHue});
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) {
|
|
||||||
Paint filtered = {
|
|
||||||
.color = Color::Black(),
|
|
||||||
.mask_blur_descriptor =
|
|
||||||
Paint::MaskBlurDescriptor{
|
|
||||||
.style = FilterContents::BlurStyle::kNormal,
|
|
||||||
.sigma = Sigma(60),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::White()});
|
|
||||||
canvas.DrawCircle({300, 300}, 200, filtered);
|
|
||||||
canvas.DrawPaint({.color = Color::Green(), .blend_mode = BlendMode::kScreen});
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, DrawAdvancedBlendPartlyOffscreen) {
|
|
||||||
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 paint = {
|
|
||||||
.color_source = ColorSource::MakeLinearGradient(
|
|
||||||
{0, 0}, {100, 100}, std::move(colors), std::move(stops),
|
|
||||||
Entity::TileMode::kRepeat, Matrix::MakeScale(Vector3(0.3, 0.3, 0.3))),
|
|
||||||
.blend_mode = BlendMode::kLighten,
|
|
||||||
};
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::Blue()});
|
|
||||||
canvas.Scale(Vector2(2, 2));
|
|
||||||
canvas.ClipRect(Rect::MakeLTRB(0, 0, 200, 200));
|
|
||||||
canvas.DrawCircle({100, 100}, 100, paint);
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, PaintBlendModeIsRespected) {
|
|
||||||
Paint paint;
|
|
||||||
Canvas canvas;
|
|
||||||
// Default is kSourceOver.
|
|
||||||
paint.color = Color(1, 0, 0, 0.5);
|
|
||||||
canvas.DrawCircle(Point(150, 200), 100, paint);
|
|
||||||
paint.color = Color(0, 1, 0, 0.5);
|
|
||||||
canvas.DrawCircle(Point(250, 200), 100, paint);
|
|
||||||
|
|
||||||
paint.blend_mode = BlendMode::kPlus;
|
|
||||||
paint.color = Color::Red();
|
|
||||||
canvas.DrawCircle(Point(450, 250), 100, paint);
|
|
||||||
paint.color = Color::Green();
|
|
||||||
canvas.DrawCircle(Point(550, 250), 100, paint);
|
|
||||||
paint.color = Color::Blue();
|
|
||||||
canvas.DrawCircle(Point(500, 150), 100, paint);
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bug: https://github.com/flutter/flutter/issues/142549
|
|
||||||
TEST_P(AiksTest, BlendModePlusAlphaWideGamut) {
|
|
||||||
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
|
|
||||||
PixelFormat::kB10G10R10A10XR);
|
|
||||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
|
||||||
/*enable_mipmapping=*/true);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(GetContentScale());
|
|
||||||
canvas.DrawPaint({.color = Color(0.9, 1.0, 0.9, 1.0)});
|
|
||||||
canvas.SaveLayer({});
|
|
||||||
Paint paint;
|
|
||||||
paint.blend_mode = BlendMode::kPlus;
|
|
||||||
paint.color = Color::Red();
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
|
|
||||||
paint.color = Color::White();
|
|
||||||
canvas.DrawImageRect(
|
|
||||||
std::make_shared<Image>(texture), Rect::MakeSize(texture->GetSize()),
|
|
||||||
Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100), paint);
|
|
||||||
canvas.Restore();
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bug: https://github.com/flutter/flutter/issues/142549
|
|
||||||
TEST_P(AiksTest, BlendModePlusAlphaColorFilterWideGamut) {
|
|
||||||
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
|
|
||||||
PixelFormat::kB10G10R10A10XR);
|
|
||||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
|
||||||
/*enable_mipmapping=*/true);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(GetContentScale());
|
|
||||||
canvas.DrawPaint({.color = Color(0.1, 0.2, 0.1, 1.0)});
|
|
||||||
canvas.SaveLayer({
|
|
||||||
.color_filter =
|
|
||||||
ColorFilter::MakeBlend(BlendMode::kPlus, Color(Vector4{1, 0, 0, 1})),
|
|
||||||
});
|
|
||||||
Paint paint;
|
|
||||||
paint.color = Color::Red();
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 400, 400), paint);
|
|
||||||
paint.color = Color::White();
|
|
||||||
canvas.DrawImageRect(
|
|
||||||
std::make_shared<Image>(texture), Rect::MakeSize(texture->GetSize()),
|
|
||||||
Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100), paint);
|
|
||||||
canvas.Restore();
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode},
|
#define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode},
|
||||||
|
|
||||||
struct BlendModeSelection {
|
struct BlendModeSelection {
|
||||||
@ -338,258 +178,5 @@ TEST_P(AiksTest, ColorWheel) {
|
|||||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
|
|
||||||
Canvas canvas;
|
|
||||||
|
|
||||||
canvas.SaveLayer({
|
|
||||||
.color_filter =
|
|
||||||
ColorFilter::MakeBlend(BlendMode::kColorDodge, Color::Red()),
|
|
||||||
});
|
|
||||||
|
|
||||||
canvas.Translate({500, 300, 0});
|
|
||||||
canvas.Rotate(Radians(2 * kPi / 3));
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(100, 100, 200, 200), {.color = Color::Blue()});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, ClearBlend) {
|
|
||||||
Canvas canvas;
|
|
||||||
Paint white;
|
|
||||||
white.color = Color::Blue();
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600.0, 600.0), white);
|
|
||||||
|
|
||||||
Paint clear;
|
|
||||||
clear.blend_mode = BlendMode::kClear;
|
|
||||||
|
|
||||||
canvas.DrawCircle(Point::MakeXY(300.0, 300.0), 200.0, clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Picture BlendModeTest(Vector2 content_scale,
|
|
||||||
BlendMode blend_mode,
|
|
||||||
const std::shared_ptr<Image>& src_image,
|
|
||||||
const std::shared_ptr<Image>& dst_image,
|
|
||||||
Scalar src_alpha) {
|
|
||||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
|
|
||||||
auto source_colors = std::vector<Color>({Color::White().WithAlpha(0.75),
|
|
||||||
Color::LimeGreen().WithAlpha(0.75),
|
|
||||||
Color::Black().WithAlpha(0.75)});
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::Black()});
|
|
||||||
// TODO(bdero): Why does this cause the left image to double scale on high DPI
|
|
||||||
// displays.
|
|
||||||
// canvas.Scale(content_scale);
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
/// 1. Save layer blending (top squares).
|
|
||||||
///
|
|
||||||
|
|
||||||
canvas.Save();
|
|
||||||
for (const auto& color : source_colors) {
|
|
||||||
canvas.Save();
|
|
||||||
{
|
|
||||||
canvas.ClipRect(Rect::MakeXYWH(25, 25, 100, 100));
|
|
||||||
// Perform the blend in a SaveLayer so that the initial backdrop color is
|
|
||||||
// fully transparent black. SourceOver blend the result onto the parent
|
|
||||||
// pass.
|
|
||||||
canvas.SaveLayer({});
|
|
||||||
{
|
|
||||||
canvas.DrawPaint({.color = destination_color});
|
|
||||||
// Draw the source color in an offscreen pass and blend it to the parent
|
|
||||||
// pass.
|
|
||||||
canvas.SaveLayer({.blend_mode = blend_mode});
|
|
||||||
{ //
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(25, 25, 100, 100), {.color = color});
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
canvas.Translate(Vector2(100, 0));
|
|
||||||
}
|
|
||||||
canvas.RestoreToCount(0);
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
/// 2. CPU blend modes (bottom squares).
|
|
||||||
///
|
|
||||||
|
|
||||||
canvas.Save();
|
|
||||||
canvas.Translate({0, 100});
|
|
||||||
// Perform the blend in a SaveLayer so that the initial backdrop color is
|
|
||||||
// fully transparent black. SourceOver blend the result onto the parent pass.
|
|
||||||
canvas.SaveLayer({});
|
|
||||||
for (const auto& color : source_colors) {
|
|
||||||
// Simply write the CPU blended color to the pass.
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(25, 25, 100, 100),
|
|
||||||
{.color = destination_color.Blend(color, blend_mode),
|
|
||||||
.blend_mode = BlendMode::kSourceOver});
|
|
||||||
canvas.Translate(Vector2(100, 0));
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
/// 3. Image blending (bottom images).
|
|
||||||
///
|
|
||||||
/// Compare these results with the images in the Flutter blend mode
|
|
||||||
/// documentation: https://api.flutter.dev/flutter/dart-ui/BlendMode.html
|
|
||||||
///
|
|
||||||
|
|
||||||
canvas.Translate({0, 250});
|
|
||||||
|
|
||||||
// Draw grid behind the images.
|
|
||||||
canvas.DrawRect(Rect::MakeLTRB(0, 0, 800, 400),
|
|
||||||
{.color = Color::MakeRGBA8(41, 41, 41, 255)});
|
|
||||||
Paint square_paint = {.color = Color::MakeRGBA8(15, 15, 15, 255)};
|
|
||||||
for (int y = 0; y < 400 / 8; y++) {
|
|
||||||
for (int x = 0; x < 800 / 16; x++) {
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(x * 16 + (y % 2) * 8, y * 8, 8, 8),
|
|
||||||
square_paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uploaded image source (left image).
|
|
||||||
canvas.Save();
|
|
||||||
canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver});
|
|
||||||
{
|
|
||||||
canvas.DrawImage(dst_image, {0, 0},
|
|
||||||
{
|
|
||||||
.blend_mode = BlendMode::kSourceOver,
|
|
||||||
});
|
|
||||||
canvas.DrawImage(src_image, {0, 0},
|
|
||||||
{
|
|
||||||
.color = Color::White().WithAlpha(src_alpha),
|
|
||||||
.blend_mode = blend_mode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
// Rendered image source (right image).
|
|
||||||
canvas.Save();
|
|
||||||
canvas.SaveLayer({.blend_mode = BlendMode::kSourceOver});
|
|
||||||
{
|
|
||||||
canvas.DrawImage(dst_image, {400, 0},
|
|
||||||
{.blend_mode = BlendMode::kSourceOver});
|
|
||||||
canvas.SaveLayer({.color = Color::White().WithAlpha(src_alpha),
|
|
||||||
.blend_mode = blend_mode});
|
|
||||||
{
|
|
||||||
canvas.DrawImage(src_image, {400, 0},
|
|
||||||
{.blend_mode = BlendMode::kSourceOver});
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
return canvas.EndRecordingAsPicture();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BLEND_MODE_TEST(blend_mode) \
|
|
||||||
TEST_P(AiksTest, BlendMode##blend_mode) { \
|
|
||||||
auto src_image = std::make_shared<Image>( \
|
|
||||||
CreateTextureForFixture("blend_mode_src.png")); \
|
|
||||||
auto dst_image = std::make_shared<Image>( \
|
|
||||||
CreateTextureForFixture("blend_mode_dst.png")); \
|
|
||||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> { \
|
|
||||||
return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
|
|
||||||
src_image, dst_image, /*src_alpha=*/1.0); \
|
|
||||||
}; \
|
|
||||||
OpenPlaygroundHere(callback); \
|
|
||||||
}
|
|
||||||
IMPELLER_FOR_EACH_BLEND_MODE(BLEND_MODE_TEST)
|
|
||||||
|
|
||||||
#define BLEND_MODE_SRC_ALPHA_TEST(blend_mode) \
|
|
||||||
TEST_P(AiksTest, BlendModeSrcAlpha##blend_mode) { \
|
|
||||||
auto src_image = std::make_shared<Image>( \
|
|
||||||
CreateTextureForFixture("blend_mode_src.png")); \
|
|
||||||
auto dst_image = std::make_shared<Image>( \
|
|
||||||
CreateTextureForFixture("blend_mode_dst.png")); \
|
|
||||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> { \
|
|
||||||
return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
|
|
||||||
src_image, dst_image, /*src_alpha=*/0.5); \
|
|
||||||
}; \
|
|
||||||
OpenPlaygroundHere(callback); \
|
|
||||||
}
|
|
||||||
IMPELLER_FOR_EACH_BLEND_MODE(BLEND_MODE_SRC_ALPHA_TEST)
|
|
||||||
|
|
||||||
TEST_P(AiksTest, CanDrawPaintMultipleTimesInteractive) {
|
|
||||||
auto modes = GetBlendModeSelection();
|
|
||||||
|
|
||||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
|
|
||||||
static Color background = Color::MediumTurquoise();
|
|
||||||
static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
|
|
||||||
static int current_blend_index = 3;
|
|
||||||
|
|
||||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
|
|
||||||
ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
|
|
||||||
ImGui::ListBox("Blend mode", ¤t_blend_index,
|
|
||||||
modes.blend_mode_names.data(),
|
|
||||||
modes.blend_mode_names.size());
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(Vector2(0.2, 0.2));
|
|
||||||
canvas.DrawPaint({.color = background});
|
|
||||||
canvas.DrawPaint(
|
|
||||||
{.color = foreground,
|
|
||||||
.blend_mode = static_cast<BlendMode>(current_blend_index)});
|
|
||||||
return canvas.EndRecordingAsPicture();
|
|
||||||
};
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, ForegroundPipelineBlendAppliesTransformCorrectly) {
|
|
||||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
|
||||||
/*enable_mipmapping=*/true);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Rotate(Degrees(30));
|
|
||||||
canvas.DrawImage(std::make_shared<Image>(texture), {200, 200},
|
|
||||||
{.color_filter = ColorFilter::MakeBlend(BlendMode::kSourceIn,
|
|
||||||
Color::Orange())});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, ForegroundAdvancedBlendAppliesTransformCorrectly) {
|
|
||||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
|
||||||
/*enable_mipmapping=*/true);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Rotate(Degrees(30));
|
|
||||||
canvas.DrawImage(std::make_shared<Image>(texture), {200, 200},
|
|
||||||
{.color_filter = ColorFilter::MakeBlend(
|
|
||||||
BlendMode::kColorDodge, Color::Orange())});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, FramebufferAdvancedBlendCoverage) {
|
|
||||||
auto texture = CreateTextureForFixture("airplane.jpg",
|
|
||||||
/*enable_mipmapping=*/true);
|
|
||||||
|
|
||||||
// Draw with an advanced blend that can use FramebufferBlendContents and
|
|
||||||
// verify that the scale transform is correctly applied to the image.
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::DarkGray()});
|
|
||||||
canvas.Scale(Vector2(0.4, 0.4));
|
|
||||||
canvas.DrawImage(std::make_shared<Image>(texture), {20, 20},
|
|
||||||
{.blend_mode = BlendMode::kMultiply});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace impeller
|
} // namespace impeller
|
||||||
|
@ -354,95 +354,6 @@ TEST_P(AiksTest, ClearColorOptimizationDoesNotApplyForBackdropFilters) {
|
|||||||
EXPECT_FALSE(actual_color.has_value());
|
EXPECT_FALSE(actual_color.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(GetContentScale());
|
|
||||||
|
|
||||||
auto test = [&canvas](const std::shared_ptr<ImageFilter>& filter) {
|
|
||||||
auto DrawLine = [&canvas](const Point& p0, const Point& p1,
|
|
||||||
const Paint& p) {
|
|
||||||
auto path = PathBuilder{}
|
|
||||||
.AddLine(p0, p1)
|
|
||||||
.SetConvexity(Convexity::kConvex)
|
|
||||||
.TakePath();
|
|
||||||
Paint paint = p;
|
|
||||||
paint.style = Paint::Style::kStroke;
|
|
||||||
canvas.DrawPath(path, paint);
|
|
||||||
};
|
|
||||||
// Registration marks for the edge of the SaveLayer
|
|
||||||
DrawLine(Point(75, 100), Point(225, 100), {.color = Color::White()});
|
|
||||||
DrawLine(Point(75, 200), Point(225, 200), {.color = Color::White()});
|
|
||||||
DrawLine(Point(100, 75), Point(100, 225), {.color = Color::White()});
|
|
||||||
DrawLine(Point(200, 75), Point(200, 225), {.color = Color::White()});
|
|
||||||
|
|
||||||
canvas.SaveLayer({.image_filter = filter},
|
|
||||||
Rect::MakeLTRB(100, 100, 200, 200));
|
|
||||||
{
|
|
||||||
// DrawPaint to verify correct behavior when the contents are unbounded.
|
|
||||||
canvas.DrawPaint({.color = Color::Yellow()});
|
|
||||||
|
|
||||||
// Contrasting rectangle to see interior blurring
|
|
||||||
canvas.DrawRect(Rect::MakeLTRB(125, 125, 175, 175),
|
|
||||||
{.color = Color::Blue()});
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
};
|
|
||||||
|
|
||||||
test(ImageFilter::MakeBlur(Sigma{10.0}, Sigma{10.0},
|
|
||||||
FilterContents::BlurStyle::kNormal,
|
|
||||||
Entity::TileMode::kDecal));
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeDilate(Radius{10.0}, Radius{10.0}));
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeErode(Radius{10.0}, Radius{10.0}));
|
|
||||||
|
|
||||||
canvas.Translate({-400.0, 200.0});
|
|
||||||
|
|
||||||
auto rotate_filter =
|
|
||||||
ImageFilter::MakeMatrix(Matrix::MakeTranslation({150, 150}) *
|
|
||||||
Matrix::MakeRotationZ(Degrees{10.0}) *
|
|
||||||
Matrix::MakeTranslation({-150, -150}),
|
|
||||||
SamplerDescriptor{});
|
|
||||||
test(rotate_filter);
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
auto rgb_swap_filter = ImageFilter::MakeFromColorFilter(
|
|
||||||
*ColorFilter::MakeMatrix({.array = {
|
|
||||||
0, 1, 0, 0, 0, //
|
|
||||||
0, 0, 1, 0, 0, //
|
|
||||||
1, 0, 0, 0, 0, //
|
|
||||||
0, 0, 0, 1, 0 //
|
|
||||||
}}));
|
|
||||||
test(rgb_swap_filter);
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter));
|
|
||||||
|
|
||||||
canvas.Translate({-400.0, 200.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
|
|
||||||
*rotate_filter));
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeLocalMatrix(Matrix::MakeTranslation({25.0, 25.0}),
|
|
||||||
*rgb_swap_filter));
|
|
||||||
|
|
||||||
canvas.Translate({200.0, 0.0});
|
|
||||||
|
|
||||||
test(ImageFilter::MakeLocalMatrix(
|
|
||||||
Matrix::MakeTranslation({25.0, 25.0}),
|
|
||||||
*ImageFilter::MakeCompose(*rotate_filter, *rgb_swap_filter)));
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
|
TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
|
||||||
Canvas canvas;
|
Canvas canvas;
|
||||||
canvas.Scale(Vector2(1.618, 1.618));
|
canvas.Scale(Vector2(1.618, 1.618));
|
||||||
@ -472,57 +383,6 @@ TEST_P(AiksTest, OpaqueEntitiesGetCoercedToSource) {
|
|||||||
ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
|
ASSERT_EQ(entity[0].GetBlendMode(), BlendMode::kSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(AiksTest, MatrixSaveLayerFilter) {
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::Black()});
|
|
||||||
canvas.SaveLayer({}, std::nullopt);
|
|
||||||
{
|
|
||||||
canvas.DrawCircle(Point(200, 200), 100,
|
|
||||||
{.color = Color::Green().WithAlpha(0.5),
|
|
||||||
.blend_mode = BlendMode::kPlus});
|
|
||||||
// Should render a second circle, centered on the bottom-right-most edge of
|
|
||||||
// the circle.
|
|
||||||
canvas.SaveLayer({.image_filter = ImageFilter::MakeMatrix(
|
|
||||||
Matrix::MakeTranslation(Vector2(1, 1) *
|
|
||||||
(200 + 100 * k1OverSqrt2)) *
|
|
||||||
Matrix::MakeScale(Vector2(1, 1) * 0.5) *
|
|
||||||
Matrix::MakeTranslation(Vector2(-200, -200)),
|
|
||||||
SamplerDescriptor{})},
|
|
||||||
std::nullopt);
|
|
||||||
canvas.DrawCircle(Point(200, 200), 100,
|
|
||||||
{.color = Color::Green().WithAlpha(0.5),
|
|
||||||
.blend_mode = BlendMode::kPlus});
|
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, MatrixBackdropFilter) {
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawPaint({.color = Color::Black()});
|
|
||||||
canvas.SaveLayer({}, std::nullopt);
|
|
||||||
{
|
|
||||||
canvas.DrawCircle(Point(200, 200), 100,
|
|
||||||
{.color = Color::Green().WithAlpha(0.5),
|
|
||||||
.blend_mode = BlendMode::kPlus});
|
|
||||||
// Should render a second circle, centered on the bottom-right-most edge of
|
|
||||||
// the circle.
|
|
||||||
canvas.SaveLayer(
|
|
||||||
{}, std::nullopt,
|
|
||||||
ImageFilter::MakeMatrix(
|
|
||||||
Matrix::MakeTranslation(Vector2(1, 1) * (100 + 100 * k1OverSqrt2)) *
|
|
||||||
Matrix::MakeScale(Vector2(1, 1) * 0.5) *
|
|
||||||
Matrix::MakeTranslation(Vector2(-100, -100)),
|
|
||||||
SamplerDescriptor{}));
|
|
||||||
canvas.Restore();
|
|
||||||
}
|
|
||||||
canvas.Restore();
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, SolidColorApplyColorFilter) {
|
TEST_P(AiksTest, SolidColorApplyColorFilter) {
|
||||||
auto contents = SolidColorContents();
|
auto contents = SolidColorContents();
|
||||||
contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
|
contents.SetColor(Color::CornflowerBlue().WithAlpha(0.75));
|
||||||
@ -534,65 +394,6 @@ TEST_P(AiksTest, SolidColorApplyColorFilter) {
|
|||||||
Color(0.424452, 0.828743, 0.79105, 0.9375));
|
Color(0.424452, 0.828743, 0.79105, 0.9375));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/134678.
|
|
||||||
TEST_P(AiksTest, ReleasesTextureOnTeardown) {
|
|
||||||
auto context = MakeContext();
|
|
||||||
std::weak_ptr<Texture> weak_texture;
|
|
||||||
|
|
||||||
{
|
|
||||||
auto texture = CreateTextureForFixture("table_mountain_nx.png");
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(GetContentScale());
|
|
||||||
canvas.Translate({100.0f, 100.0f, 0});
|
|
||||||
|
|
||||||
Paint paint;
|
|
||||||
paint.color_source = ColorSource::MakeImage(
|
|
||||||
texture, Entity::TileMode::kClamp, Entity::TileMode::kClamp, {}, {});
|
|
||||||
canvas.DrawRect(Rect::MakeXYWH(0, 0, 600, 600), paint);
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://github.com/flutter/flutter/issues/134751.
|
|
||||||
//
|
|
||||||
// If the fence waiter was working this may not be released by the end of the
|
|
||||||
// scope above. Adding a manual shutdown so that future changes to the fence
|
|
||||||
// waiter will not flake this test.
|
|
||||||
context->Shutdown();
|
|
||||||
|
|
||||||
// The texture should be released by now.
|
|
||||||
ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
|
|
||||||
"by the backend, it should be "
|
|
||||||
"released.";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, MatrixImageFilterMagnify) {
|
|
||||||
Scalar scale = 2.0;
|
|
||||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
|
|
||||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::SliderFloat("Scale", &scale, 1, 2);
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Scale(GetContentScale());
|
|
||||||
auto image =
|
|
||||||
std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
|
|
||||||
canvas.Translate({600, -200});
|
|
||||||
canvas.SaveLayer({
|
|
||||||
.image_filter = std::make_shared<MatrixImageFilter>(
|
|
||||||
Matrix::MakeScale({scale, scale, 1}), SamplerDescriptor{}),
|
|
||||||
});
|
|
||||||
canvas.DrawImage(image, {0, 0},
|
|
||||||
Paint{.color = Color::White().WithAlpha(0.5)});
|
|
||||||
canvas.Restore();
|
|
||||||
return canvas.EndRecordingAsPicture();
|
|
||||||
};
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
|
TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
|
||||||
Canvas canvas; // Depth 1 (base pass)
|
Canvas canvas; // Depth 1 (base pass)
|
||||||
canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
|
canvas.DrawRRect(Rect::MakeLTRB(0, 0, 100, 100), {10, 10}, {}); // Depth 2
|
||||||
@ -640,114 +441,6 @@ TEST_P(AiksTest, CorrectClipDepthAssignedToEntities) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
|
|
||||||
TextureDescriptor texture_descriptor;
|
|
||||||
texture_descriptor.size = ISize{1024, 1024};
|
|
||||||
texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
|
|
||||||
texture_descriptor.storage_mode = StorageMode::kHostVisible;
|
|
||||||
texture_descriptor.mip_count = texture_descriptor.size.MipCount();
|
|
||||||
|
|
||||||
std::vector<uint8_t> bytes(4194304);
|
|
||||||
bool alternate = false;
|
|
||||||
for (auto i = 0u; i < 4194304; i += 4) {
|
|
||||||
if (alternate) {
|
|
||||||
bytes[i] = 255;
|
|
||||||
bytes[i + 1] = 0;
|
|
||||||
bytes[i + 2] = 0;
|
|
||||||
bytes[i + 3] = 255;
|
|
||||||
} else {
|
|
||||||
bytes[i] = 0;
|
|
||||||
bytes[i + 1] = 255;
|
|
||||||
bytes[i + 2] = 0;
|
|
||||||
bytes[i + 3] = 255;
|
|
||||||
}
|
|
||||||
alternate = !alternate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
|
|
||||||
auto mapping = std::make_shared<fml::NonOwnedMapping>(
|
|
||||||
bytes.data(), // data
|
|
||||||
texture_descriptor.GetByteSizeOfBaseMipLevel() // size
|
|
||||||
);
|
|
||||||
auto texture =
|
|
||||||
GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
|
|
||||||
|
|
||||||
auto device_buffer =
|
|
||||||
GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
|
|
||||||
auto command_buffer = GetContext()->CreateCommandBuffer();
|
|
||||||
auto blit_pass = command_buffer->CreateBlitPass();
|
|
||||||
|
|
||||||
blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
|
|
||||||
texture);
|
|
||||||
blit_pass->GenerateMipmap(texture);
|
|
||||||
EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
|
|
||||||
EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
|
|
||||||
|
|
||||||
auto image = std::make_shared<Image>(texture);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawImageRect(image, Rect::MakeSize(texture->GetSize()),
|
|
||||||
Rect::MakeLTRB(0, 0, 100, 100), {});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/flutter/flutter/issues/146648
|
|
||||||
TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
|
|
||||||
Path path = PathBuilder{}
|
|
||||||
.MoveTo({0, 400})
|
|
||||||
.LineTo({0, 0})
|
|
||||||
.LineTo({400, 0})
|
|
||||||
// MoveTo implicitly adds a contour, ensure that close doesn't
|
|
||||||
// add another nearly-empty contour.
|
|
||||||
.MoveTo({0, 400})
|
|
||||||
.Close()
|
|
||||||
.TakePath();
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.Translate({50, 50, 0});
|
|
||||||
canvas.DrawPath(path, {
|
|
||||||
.color = Color::Blue(),
|
|
||||||
.stroke_width = 10,
|
|
||||||
.stroke_cap = Cap::kRound,
|
|
||||||
.style = Paint::Style::kStroke,
|
|
||||||
});
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(AiksTest, SetContentsWithRegion) {
|
|
||||||
auto bridge = CreateTextureForFixture("bay_bridge.jpg");
|
|
||||||
|
|
||||||
// Replace part of the texture with a red rectangle.
|
|
||||||
std::vector<uint8_t> bytes(100 * 100 * 4);
|
|
||||||
for (auto i = 0u; i < bytes.size(); i += 4) {
|
|
||||||
bytes[i] = 255;
|
|
||||||
bytes[i + 1] = 0;
|
|
||||||
bytes[i + 2] = 0;
|
|
||||||
bytes[i + 3] = 255;
|
|
||||||
}
|
|
||||||
auto mapping =
|
|
||||||
std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
|
|
||||||
auto device_buffer =
|
|
||||||
GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
|
|
||||||
auto cmd_buffer = GetContext()->CreateCommandBuffer();
|
|
||||||
auto blit_pass = cmd_buffer->CreateBlitPass();
|
|
||||||
blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
|
|
||||||
IRect::MakeLTRB(50, 50, 150, 150));
|
|
||||||
|
|
||||||
auto did_submit =
|
|
||||||
blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
|
|
||||||
GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
|
|
||||||
ASSERT_TRUE(did_submit);
|
|
||||||
|
|
||||||
auto image = std::make_shared<Image>(bridge);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.DrawImage(image, {0, 0}, {});
|
|
||||||
|
|
||||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace impeller
|
} // namespace impeller
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ template("display_list_unittests_component") {
|
|||||||
predefined_sources = [
|
predefined_sources = [
|
||||||
"aiks_dl_atlas_unittests.cc",
|
"aiks_dl_atlas_unittests.cc",
|
||||||
"aiks_dl_basic_unittests.cc",
|
"aiks_dl_basic_unittests.cc",
|
||||||
|
"aiks_dl_blend_unittests.cc",
|
||||||
"aiks_dl_clip_unittests.cc",
|
"aiks_dl_clip_unittests.cc",
|
||||||
"aiks_dl_gradient_unittests.cc",
|
"aiks_dl_gradient_unittests.cc",
|
||||||
"aiks_dl_opacity_unittests.cc",
|
"aiks_dl_opacity_unittests.cc",
|
||||||
|
@ -0,0 +1,570 @@
|
|||||||
|
// 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 <memory>
|
||||||
|
|
||||||
|
#include "display_list/display_list.h"
|
||||||
|
#include "display_list/dl_sampling_options.h"
|
||||||
|
#include "display_list/dl_tile_mode.h"
|
||||||
|
#include "display_list/effects/dl_color_filter.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"
|
||||||
|
#include "flutter/display_list/dl_builder.h"
|
||||||
|
#include "flutter/display_list/dl_color.h"
|
||||||
|
#include "flutter/display_list/dl_paint.h"
|
||||||
|
#include "flutter/impeller/display_list/dl_image_impeller.h"
|
||||||
|
#include "flutter/impeller/geometry/scalar.h"
|
||||||
|
#include "include/core/SkMatrix.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This is for tests of Canvas that are interested the results of rendering
|
||||||
|
// blends.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace impeller {
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
using namespace flutter;
|
||||||
|
|
||||||
|
#define BLEND_MODE_TUPLE(blend_mode) {#blend_mode, BlendMode::k##blend_mode},
|
||||||
|
|
||||||
|
struct BlendModeSelection {
|
||||||
|
std::vector<const char*> blend_mode_names;
|
||||||
|
std::vector<BlendMode> blend_mode_values;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BlendModeSelection GetBlendModeSelection() {
|
||||||
|
std::vector<const char*> blend_mode_names;
|
||||||
|
std::vector<BlendMode> blend_mode_values;
|
||||||
|
{
|
||||||
|
const std::vector<std::tuple<const char*, BlendMode>> blends = {
|
||||||
|
IMPELLER_FOR_EACH_BLEND_MODE(BLEND_MODE_TUPLE)};
|
||||||
|
assert(blends.size() ==
|
||||||
|
static_cast<size_t>(Entity::kLastAdvancedBlendMode) + 1);
|
||||||
|
for (const auto& [name, mode] : blends) {
|
||||||
|
blend_mode_names.push_back(name);
|
||||||
|
blend_mode_values.push_back(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {blend_mode_names, blend_mode_values};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
SkRect layer_rect = SkRect::MakeXYWH(0, 0, 500, 500);
|
||||||
|
builder.ClipRect(layer_rect);
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setColorFilter(DlBlendColorFilter::Make(
|
||||||
|
DlColor::RGBA(0, 1, 0, 0.5), DlBlendMode::kDifference));
|
||||||
|
builder.SaveLayer(&layer_rect, &save_paint);
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint);
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, BlendModeShouldCoverWholeScreen) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
DlPaint paint;
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kRed());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
paint.setBlendMode(DlBlendMode::kSrcOver);
|
||||||
|
builder.SaveLayer(nullptr, &paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(100, 100, 400, 400), paint);
|
||||||
|
|
||||||
|
paint.setBlendMode(DlBlendMode::kSrc);
|
||||||
|
builder.SaveLayer(nullptr, &paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(200, 200, 200, 200), paint);
|
||||||
|
|
||||||
|
builder.Restore();
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, CanDrawPaintWithAdvancedBlend) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
builder.Scale(0.2, 0.2);
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::RGBA(
|
||||||
|
Color::MediumTurquoise().red, Color::MediumTurquoise().green,
|
||||||
|
Color::MediumTurquoise().blue, Color::MediumTurquoise().alpha));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::RGBA(Color::OrangeRed().red, Color::OrangeRed().green,
|
||||||
|
Color::OrangeRed().blue, 0.5));
|
||||||
|
paint.setBlendMode(DlBlendMode::kHue);
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, DrawPaintWithAdvancedBlendOverFilter) {
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
paint.setMaskFilter(DlBlurMaskFilter::Make(DlBlurStyle::kNormal, 60));
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
builder.DrawCircle({300, 300}, 200, paint);
|
||||||
|
paint.setColor(DlColor::kGreen());
|
||||||
|
paint.setBlendMode(DlBlendMode::kScreen);
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, DrawAdvancedBlendPartlyOffscreen) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint draw_paint;
|
||||||
|
draw_paint.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawPaint(draw_paint);
|
||||||
|
builder.Scale(2, 2);
|
||||||
|
builder.ClipRect(SkRect::MakeLTRB(0, 0, 200, 200));
|
||||||
|
|
||||||
|
std::vector<DlColor> colors = {DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0),
|
||||||
|
DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0)};
|
||||||
|
std::vector<Scalar> stops = {0.0, 1.0};
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
SkMatrix matrix = SkMatrix::Scale(0.3, 0.3);
|
||||||
|
paint.setColorSource(DlColorSource::MakeLinear(
|
||||||
|
/*start_point=*/{0, 0}, //
|
||||||
|
/*end_point=*/{100, 100}, //
|
||||||
|
/*stop_count=*/colors.size(), //
|
||||||
|
/*colors=*/colors.data(), //
|
||||||
|
/*stops=*/stops.data(), //
|
||||||
|
/*tile_mode=*/DlTileMode::kRepeat, //
|
||||||
|
/*matrix=*/&matrix //
|
||||||
|
));
|
||||||
|
paint.setBlendMode(DlBlendMode::kLighten);
|
||||||
|
|
||||||
|
builder.DrawCircle({100, 100}, 100, paint);
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, PaintBlendModeIsRespected) {
|
||||||
|
DlPaint paint;
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
// Default is kSourceOver.
|
||||||
|
|
||||||
|
paint.setColor(DlColor::RGBA(1, 0, 0, 0.5));
|
||||||
|
builder.DrawCircle({150, 200}, 100, paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::RGBA(0, 1, 0, 0.5));
|
||||||
|
builder.DrawCircle({250, 200}, 100, paint);
|
||||||
|
|
||||||
|
paint.setBlendMode(DlBlendMode::kPlus);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kRed());
|
||||||
|
builder.DrawCircle({450, 250}, 100, paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kGreen());
|
||||||
|
builder.DrawCircle({550, 250}, 100, paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawCircle({500, 150}, 100, paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug: https://github.com/flutter/flutter/issues/142549
|
||||||
|
TEST_P(AiksTest, BlendModePlusAlphaWideGamut) {
|
||||||
|
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
|
||||||
|
PixelFormat::kB10G10R10A10XR);
|
||||||
|
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||||
|
/*enable_mipmapping=*/true);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
DlPaint paint;
|
||||||
|
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::RGBA(0.9, 1, 0.9, 1.0));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
builder.SaveLayer(nullptr);
|
||||||
|
|
||||||
|
paint.setBlendMode(DlBlendMode::kPlus);
|
||||||
|
paint.setColor(DlColor::kRed());
|
||||||
|
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(100, 100, 400, 400), paint);
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
|
||||||
|
auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
|
||||||
|
builder.DrawImageRect(
|
||||||
|
DlImageImpeller::Make(texture),
|
||||||
|
SkRect::MakeSize(
|
||||||
|
SkSize::Make(texture->GetSize().width, texture->GetSize().height)),
|
||||||
|
SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), rect.GetRight(),
|
||||||
|
rect.GetBottom()),
|
||||||
|
DlImageSampling::kMipmapLinear, &paint);
|
||||||
|
builder.Restore();
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug: https://github.com/flutter/flutter/issues/142549
|
||||||
|
TEST_P(AiksTest, BlendModePlusAlphaColorFilterWideGamut) {
|
||||||
|
EXPECT_EQ(GetContext()->GetCapabilities()->GetDefaultColorFormat(),
|
||||||
|
PixelFormat::kB10G10R10A10XR);
|
||||||
|
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||||
|
/*enable_mipmapping=*/true);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::RGBA(0.1, 0.2, 0.1, 1.0));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setColorFilter(
|
||||||
|
DlBlendColorFilter::Make(DlColor::RGBA(1, 0, 0, 1), DlBlendMode::kPlus));
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kRed());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(100, 100, 400, 400), paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
|
||||||
|
auto rect = Rect::MakeXYWH(100, 100, 400, 400).Expand(-100, -100);
|
||||||
|
builder.DrawImageRect(
|
||||||
|
DlImageImpeller::Make(texture),
|
||||||
|
SkRect::MakeSize(
|
||||||
|
SkSize::Make(texture->GetSize().width, texture->GetSize().height)),
|
||||||
|
SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), rect.GetRight(),
|
||||||
|
rect.GetBottom()),
|
||||||
|
DlImageSampling::kMipmapLinear, &paint);
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, ForegroundBlendSubpassCollapseOptimization) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setColorFilter(
|
||||||
|
DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kColorDodge));
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
|
||||||
|
builder.Translate(500, 300);
|
||||||
|
builder.Rotate(120);
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, ClearBlend) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint blue;
|
||||||
|
blue.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(0, 0, 600.0, 600.0), blue);
|
||||||
|
|
||||||
|
DlPaint clear;
|
||||||
|
clear.setBlendMode(DlBlendMode::kClear);
|
||||||
|
|
||||||
|
builder.DrawCircle({300.0, 300.0}, 200.0, clear);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static sk_sp<DisplayList> BlendModeTest(Vector2 content_scale,
|
||||||
|
BlendMode blend_mode,
|
||||||
|
const sk_sp<DlImageImpeller>& src_image,
|
||||||
|
const sk_sp<DlImageImpeller>& dst_image,
|
||||||
|
Scalar src_alpha) {
|
||||||
|
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::SliderFloat("Source alpha", &src_alpha, 0, 1);
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color destination_color = Color::CornflowerBlue().WithAlpha(0.75);
|
||||||
|
auto source_colors = std::vector<Color>({Color::White().WithAlpha(0.75),
|
||||||
|
Color::LimeGreen().WithAlpha(0.75),
|
||||||
|
Color::Black().WithAlpha(0.75)});
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
{
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
}
|
||||||
|
// TODO(bdero): Why does this cause the left image to double scale on high DPI
|
||||||
|
// displays.
|
||||||
|
// builder.Scale(content_scale);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// 1. Save layer blending (top squares).
|
||||||
|
///
|
||||||
|
|
||||||
|
builder.Save();
|
||||||
|
for (const auto& color : source_colors) {
|
||||||
|
builder.Save();
|
||||||
|
{
|
||||||
|
builder.ClipRect(SkRect::MakeXYWH(25, 25, 100, 100));
|
||||||
|
// Perform the blend in a SaveLayer so that the initial backdrop color is
|
||||||
|
// fully transparent black. SourceOver blend the result onto the parent
|
||||||
|
// pass.
|
||||||
|
builder.SaveLayer({});
|
||||||
|
{
|
||||||
|
DlPaint draw_paint;
|
||||||
|
draw_paint.setColor(
|
||||||
|
DlColor::RGBA(destination_color.red, destination_color.green,
|
||||||
|
destination_color.blue, destination_color.alpha));
|
||||||
|
builder.DrawPaint(draw_paint);
|
||||||
|
|
||||||
|
// Draw the source color in an offscreen pass and blend it to the parent
|
||||||
|
// pass.
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
{ //
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(
|
||||||
|
DlColor::RGBA(color.red, color.green, color.blue, color.alpha));
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(25, 25, 100, 100), paint);
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
builder.Translate(100, 0);
|
||||||
|
}
|
||||||
|
builder.RestoreToCount(0);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// 2. CPU blend modes (bottom squares).
|
||||||
|
///
|
||||||
|
|
||||||
|
builder.Save();
|
||||||
|
builder.Translate(0, 100);
|
||||||
|
// Perform the blend in a SaveLayer so that the initial backdrop color is
|
||||||
|
// fully transparent black. SourceOver blend the result onto the parent pass.
|
||||||
|
builder.SaveLayer({});
|
||||||
|
for (const auto& color : source_colors) {
|
||||||
|
// Simply write the CPU blended color to the pass.
|
||||||
|
DlPaint paint;
|
||||||
|
auto dest = destination_color.Blend(color, blend_mode);
|
||||||
|
paint.setColor(DlColor::RGBA(dest.red, dest.green, dest.blue, dest.alpha));
|
||||||
|
paint.setBlendMode(DlBlendMode::kSrcOver);
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(25, 25, 100, 100), paint);
|
||||||
|
builder.Translate(100, 0);
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
/// 3. Image blending (bottom images).
|
||||||
|
///
|
||||||
|
/// Compare these results with the images in the Flutter blend mode
|
||||||
|
/// documentation: https://api.flutter.dev/flutter/dart-ui/BlendMode.html
|
||||||
|
///
|
||||||
|
|
||||||
|
builder.Translate(0, 250);
|
||||||
|
|
||||||
|
// Draw grid behind the images.
|
||||||
|
{
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::RGBA(41 / 255.0, 41 / 255.0, 41 / 255.0, 255));
|
||||||
|
builder.DrawRect(SkRect::MakeLTRB(0, 0, 800, 400), paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
DlPaint square_paint;
|
||||||
|
square_paint.setColor(DlColor::RGBA(15 / 255.0, 15 / 255.0, 15 / 255.0, 1));
|
||||||
|
for (int y = 0; y < 400 / 8; y++) {
|
||||||
|
for (int x = 0; x < 800 / 16; x++) {
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(x * 16 + (y % 2) * 8, y * 8, 8, 8),
|
||||||
|
square_paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uploaded image source (left image).
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setBlendMode(DlBlendMode::kSrcOver);
|
||||||
|
builder.Save();
|
||||||
|
builder.SaveLayer(nullptr, &paint);
|
||||||
|
{
|
||||||
|
builder.DrawImage(dst_image, {0, 0}, DlImageSampling::kMipmapLinear,
|
||||||
|
&paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
|
||||||
|
paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
|
||||||
|
builder.DrawImage(src_image, {0, 0}, DlImageSampling::kMipmapLinear,
|
||||||
|
&paint);
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
// Rendered image source (right image).
|
||||||
|
builder.Save();
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
{
|
||||||
|
builder.DrawImage(dst_image, {400, 0}, DlImageSampling::kMipmapLinear,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setColor(DlColor::kWhite().withAlpha(src_alpha * 255));
|
||||||
|
save_paint.setBlendMode(static_cast<DlBlendMode>(blend_mode));
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
{
|
||||||
|
builder.DrawImage(src_image, {400, 0}, DlImageSampling::kMipmapLinear,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLEND_MODE_TEST(blend_mode) \
|
||||||
|
TEST_P(AiksTest, BlendMode##blend_mode) { \
|
||||||
|
auto src_image = \
|
||||||
|
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
|
||||||
|
auto dst_image = \
|
||||||
|
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
|
||||||
|
auto callback = [&]() -> sk_sp<DisplayList> { \
|
||||||
|
return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
|
||||||
|
src_image, dst_image, /*src_alpha=*/1.0); \
|
||||||
|
}; \
|
||||||
|
OpenPlaygroundHere(callback); \
|
||||||
|
}
|
||||||
|
IMPELLER_FOR_EACH_BLEND_MODE(BLEND_MODE_TEST)
|
||||||
|
|
||||||
|
#define BLEND_MODE_SRC_ALPHA_TEST(blend_mode) \
|
||||||
|
TEST_P(AiksTest, BlendModeSrcAlpha##blend_mode) { \
|
||||||
|
auto src_image = \
|
||||||
|
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_src.png")); \
|
||||||
|
auto dst_image = \
|
||||||
|
DlImageImpeller::Make(CreateTextureForFixture("blend_mode_dst.png")); \
|
||||||
|
auto callback = [&]() -> sk_sp<DisplayList> { \
|
||||||
|
return BlendModeTest(GetContentScale(), BlendMode::k##blend_mode, \
|
||||||
|
src_image, dst_image, /*src_alpha=*/0.5); \
|
||||||
|
}; \
|
||||||
|
OpenPlaygroundHere(callback); \
|
||||||
|
}
|
||||||
|
IMPELLER_FOR_EACH_BLEND_MODE(BLEND_MODE_SRC_ALPHA_TEST)
|
||||||
|
|
||||||
|
TEST_P(AiksTest, CanDrawPaintMultipleTimesInteractive) {
|
||||||
|
auto modes = GetBlendModeSelection();
|
||||||
|
|
||||||
|
auto callback = [&]() -> sk_sp<DisplayList> {
|
||||||
|
static Color background = Color::MediumTurquoise();
|
||||||
|
static Color foreground = Color::Color::OrangeRed().WithAlpha(0.5);
|
||||||
|
static int current_blend_index = 3;
|
||||||
|
|
||||||
|
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::ColorEdit4("Background", reinterpret_cast<float*>(&background));
|
||||||
|
ImGui::ColorEdit4("Foreground", reinterpret_cast<float*>(&foreground));
|
||||||
|
ImGui::ListBox("Blend mode", ¤t_blend_index,
|
||||||
|
modes.blend_mode_names.data(),
|
||||||
|
modes.blend_mode_names.size());
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Scale(0.2, 0.2);
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor(background.ToARGB()));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
paint.setColor(DlColor(foreground.ToARGB()));
|
||||||
|
paint.setBlendMode(static_cast<DlBlendMode>(current_blend_index));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
return builder.Build();
|
||||||
|
};
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, ForegroundPipelineBlendAppliesTransformCorrectly) {
|
||||||
|
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||||
|
/*enable_mipmapping=*/true);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Rotate(30);
|
||||||
|
|
||||||
|
DlPaint image_paint;
|
||||||
|
image_paint.setColorFilter(DlBlendColorFilter::Make(
|
||||||
|
DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
|
||||||
|
DlBlendMode::kSrcIn));
|
||||||
|
|
||||||
|
builder.DrawImage(DlImageImpeller::Make(texture), {200, 200},
|
||||||
|
DlImageSampling::kMipmapLinear, &image_paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, ForegroundAdvancedBlendAppliesTransformCorrectly) {
|
||||||
|
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||||
|
/*enable_mipmapping=*/true);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Rotate(30);
|
||||||
|
|
||||||
|
DlPaint image_paint;
|
||||||
|
image_paint.setColorFilter(DlBlendColorFilter::Make(
|
||||||
|
DlColor::RGBA(255.0f / 255.0f, 165.0f / 255.0f, 0.0f / 255.0f, 1.0f),
|
||||||
|
DlBlendMode::kColorDodge));
|
||||||
|
|
||||||
|
builder.DrawImage(DlImageImpeller::Make(texture), {200, 200},
|
||||||
|
DlImageSampling::kMipmapLinear, &image_paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, FramebufferAdvancedBlendCoverage) {
|
||||||
|
auto texture = CreateTextureForFixture("airplane.jpg",
|
||||||
|
/*enable_mipmapping=*/true);
|
||||||
|
|
||||||
|
// Draw with an advanced blend that can use FramebufferBlendContents and
|
||||||
|
// verify that the scale transform is correctly applied to the image.
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(
|
||||||
|
DlColor::RGBA(169.0f / 255.0f, 169.0f / 255.0f, 169.0f / 255.0f, 1.0f));
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
builder.Scale(0.4, 0.4);
|
||||||
|
|
||||||
|
DlPaint image_paint;
|
||||||
|
image_paint.setBlendMode(DlBlendMode::kMultiply);
|
||||||
|
|
||||||
|
builder.DrawImage(DlImageImpeller::Make(texture), {20, 20},
|
||||||
|
DlImageSampling::kMipmapLinear, &image_paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace impeller
|
@ -20,6 +20,7 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "impeller/display_list/dl_image_impeller.h"
|
#include "impeller/display_list/dl_image_impeller.h"
|
||||||
#include "impeller/geometry/scalar.h"
|
#include "impeller/geometry/scalar.h"
|
||||||
|
#include "include/core/SkMatrix.h"
|
||||||
#include "include/core/SkRSXform.h"
|
#include "include/core/SkRSXform.h"
|
||||||
#include "include/core/SkRefCnt.h"
|
#include "include/core/SkRefCnt.h"
|
||||||
|
|
||||||
@ -467,5 +468,337 @@ TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
|
|||||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
|
||||||
|
TextureDescriptor texture_descriptor;
|
||||||
|
texture_descriptor.size = ISize{1024, 1024};
|
||||||
|
texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
|
||||||
|
texture_descriptor.storage_mode = StorageMode::kHostVisible;
|
||||||
|
texture_descriptor.mip_count = texture_descriptor.size.MipCount();
|
||||||
|
|
||||||
|
std::vector<uint8_t> bytes(4194304);
|
||||||
|
bool alternate = false;
|
||||||
|
for (auto i = 0u; i < 4194304; i += 4) {
|
||||||
|
if (alternate) {
|
||||||
|
bytes[i] = 255;
|
||||||
|
bytes[i + 1] = 0;
|
||||||
|
bytes[i + 2] = 0;
|
||||||
|
bytes[i + 3] = 255;
|
||||||
|
} else {
|
||||||
|
bytes[i] = 0;
|
||||||
|
bytes[i + 1] = 255;
|
||||||
|
bytes[i + 2] = 0;
|
||||||
|
bytes[i + 3] = 255;
|
||||||
|
}
|
||||||
|
alternate = !alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
|
||||||
|
auto mapping = std::make_shared<fml::NonOwnedMapping>(
|
||||||
|
bytes.data(), // data
|
||||||
|
texture_descriptor.GetByteSizeOfBaseMipLevel() // size
|
||||||
|
);
|
||||||
|
auto texture =
|
||||||
|
GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
|
||||||
|
|
||||||
|
auto device_buffer =
|
||||||
|
GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
|
||||||
|
auto command_buffer = GetContext()->CreateCommandBuffer();
|
||||||
|
auto blit_pass = command_buffer->CreateBlitPass();
|
||||||
|
|
||||||
|
blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
|
||||||
|
texture);
|
||||||
|
blit_pass->GenerateMipmap(texture);
|
||||||
|
EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
|
||||||
|
EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
|
||||||
|
|
||||||
|
auto image = DlImageImpeller::Make(texture);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.DrawImageRect(
|
||||||
|
image,
|
||||||
|
SkRect::MakeSize(
|
||||||
|
SkSize::Make(texture->GetSize().width, texture->GetSize().height)),
|
||||||
|
SkRect::MakeLTRB(0, 0, 100, 100), DlImageSampling::kMipmapLinear);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/flutter/flutter/issues/146648
|
||||||
|
TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
|
||||||
|
SkPath path;
|
||||||
|
path.moveTo(0, 400)
|
||||||
|
.lineTo(0, 0)
|
||||||
|
.lineTo(400, 0)
|
||||||
|
// MoveTo implicitly adds a contour, ensure that close doesn't
|
||||||
|
// add another nearly-empty contour.
|
||||||
|
.moveTo(0, 400)
|
||||||
|
.close();
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Translate(50, 50);
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlue());
|
||||||
|
paint.setStrokeCap(DlStrokeCap::kRound);
|
||||||
|
paint.setStrokeWidth(10);
|
||||||
|
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||||
|
builder.DrawPath(path, paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, SetContentsWithRegion) {
|
||||||
|
auto bridge = CreateTextureForFixture("bay_bridge.jpg");
|
||||||
|
|
||||||
|
// Replace part of the texture with a red rectangle.
|
||||||
|
std::vector<uint8_t> bytes(100 * 100 * 4);
|
||||||
|
for (auto i = 0u; i < bytes.size(); i += 4) {
|
||||||
|
bytes[i] = 255;
|
||||||
|
bytes[i + 1] = 0;
|
||||||
|
bytes[i + 2] = 0;
|
||||||
|
bytes[i + 3] = 255;
|
||||||
|
}
|
||||||
|
auto mapping =
|
||||||
|
std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
|
||||||
|
auto device_buffer =
|
||||||
|
GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
|
||||||
|
auto cmd_buffer = GetContext()->CreateCommandBuffer();
|
||||||
|
auto blit_pass = cmd_buffer->CreateBlitPass();
|
||||||
|
blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
|
||||||
|
IRect::MakeLTRB(50, 50, 150, 150));
|
||||||
|
|
||||||
|
auto did_submit =
|
||||||
|
blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
|
||||||
|
GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
|
||||||
|
ASSERT_TRUE(did_submit);
|
||||||
|
|
||||||
|
auto image = DlImageImpeller::Make(bridge);
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.DrawImage(image, {0, 0}, {});
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/flutter/flutter/issues/134678.
|
||||||
|
TEST_P(AiksTest, ReleasesTextureOnTeardown) {
|
||||||
|
auto context = MakeContext();
|
||||||
|
std::weak_ptr<Texture> weak_texture;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto texture = CreateTextureForFixture("table_mountain_nx.png");
|
||||||
|
weak_texture = texture;
|
||||||
|
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||||
|
builder.Translate(100.0f, 100.0f);
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColorSource(std::make_shared<DlImageColorSource>(
|
||||||
|
DlImageImpeller::Make(texture), DlTileMode::kClamp, DlTileMode::kClamp,
|
||||||
|
DlImageSampling::kLinear, nullptr));
|
||||||
|
|
||||||
|
builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://github.com/flutter/flutter/issues/134751.
|
||||||
|
//
|
||||||
|
// If the fence waiter was working this may not be released by the end of the
|
||||||
|
// scope above. Adding a manual shutdown so that future changes to the fence
|
||||||
|
// waiter will not flake this test.
|
||||||
|
context->Shutdown();
|
||||||
|
|
||||||
|
// The texture should be released by now.
|
||||||
|
ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
|
||||||
|
"by the backend, it should be "
|
||||||
|
"released.";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, MatrixImageFilterMagnify) {
|
||||||
|
Scalar scale = 2.0;
|
||||||
|
auto callback = [&]() -> sk_sp<DisplayList> {
|
||||||
|
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
ImGui::SliderFloat("Scale", &scale, 1, 2);
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||||
|
auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
|
||||||
|
|
||||||
|
builder.Translate(600, -200);
|
||||||
|
|
||||||
|
SkMatrix matrix = SkMatrix::Scale(scale, scale);
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setImageFilter(
|
||||||
|
DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear));
|
||||||
|
builder.SaveLayer(nullptr, &paint);
|
||||||
|
|
||||||
|
DlPaint rect_paint;
|
||||||
|
rect_paint.setAlpha(0.5 * 255);
|
||||||
|
builder.DrawImage(image, {0, 0}, DlImageSampling::kLinear, &rect_paint);
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
builder.Scale(GetContentScale().x, GetContentScale().y);
|
||||||
|
|
||||||
|
auto test = [&builder](const std::shared_ptr<const DlImageFilter>& filter) {
|
||||||
|
auto DrawLine = [&builder](const SkPoint& p0, const SkPoint& p1,
|
||||||
|
const DlPaint& p) {
|
||||||
|
DlPaint paint = p;
|
||||||
|
paint.setDrawStyle(DlDrawStyle::kStroke);
|
||||||
|
builder.DrawPath(SkPath::Line(p0, p1), paint);
|
||||||
|
};
|
||||||
|
// Registration marks for the edge of the SaveLayer
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kWhite());
|
||||||
|
DrawLine(SkPoint::Make(75, 100), SkPoint::Make(225, 100), paint);
|
||||||
|
DrawLine(SkPoint::Make(75, 200), SkPoint::Make(225, 200), paint);
|
||||||
|
DrawLine(SkPoint::Make(100, 75), SkPoint::Make(100, 225), paint);
|
||||||
|
DrawLine(SkPoint::Make(200, 75), SkPoint::Make(200, 225), paint);
|
||||||
|
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setImageFilter(filter);
|
||||||
|
SkRect bounds = SkRect::MakeLTRB(100, 100, 200, 200);
|
||||||
|
builder.SaveLayer(&bounds, &save_paint);
|
||||||
|
|
||||||
|
{
|
||||||
|
// DrawPaint to verify correct behavior when the contents are unbounded.
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kYellow());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
|
||||||
|
// Contrasting rectangle to see interior blurring
|
||||||
|
paint.setColor(DlColor::kBlue());
|
||||||
|
builder.DrawRect(SkRect::MakeLTRB(125, 125, 175, 175), paint);
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
test(std::make_shared<DlBlurImageFilter>(10.0, 10.0, DlTileMode::kDecal));
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
test(std::make_shared<DlDilateImageFilter>(10.0, 10.0));
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
test(std::make_shared<DlErodeImageFilter>(10.0, 10.0));
|
||||||
|
|
||||||
|
builder.Translate(-400.0, 200.0);
|
||||||
|
|
||||||
|
SkMatrix sk_matrix = SkMatrix::RotateDeg(10);
|
||||||
|
|
||||||
|
auto rotate_filter = std::make_shared<DlMatrixImageFilter>(
|
||||||
|
sk_matrix, DlImageSampling::kLinear);
|
||||||
|
test(rotate_filter);
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
const float m[20] = {
|
||||||
|
0, 1, 0, 0, 0, //
|
||||||
|
0, 0, 1, 0, 0, //
|
||||||
|
1, 0, 0, 0, 0, //
|
||||||
|
0, 0, 0, 1, 0 //
|
||||||
|
};
|
||||||
|
auto rgb_swap_filter = std::make_shared<DlColorFilterImageFilter>(
|
||||||
|
std::make_shared<DlMatrixColorFilter>(m));
|
||||||
|
test(rgb_swap_filter);
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
test(DlComposeImageFilter::Make(rotate_filter, rgb_swap_filter));
|
||||||
|
|
||||||
|
builder.Translate(-400.0, 200.0);
|
||||||
|
|
||||||
|
test(std::make_shared<DlLocalMatrixImageFilter>(
|
||||||
|
SkMatrix::Translate(25.0, 25.0), rotate_filter));
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
test(std::make_shared<DlLocalMatrixImageFilter>(
|
||||||
|
SkMatrix::Translate(25.0, 25.0), rgb_swap_filter));
|
||||||
|
|
||||||
|
builder.Translate(200.0, 0.0);
|
||||||
|
|
||||||
|
test(std::make_shared<DlLocalMatrixImageFilter>(
|
||||||
|
SkMatrix::Translate(25.0, 25.0),
|
||||||
|
DlComposeImageFilter::Make(rotate_filter, rgb_swap_filter)));
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, MatrixBackdropFilter) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
builder.SaveLayer(nullptr, nullptr);
|
||||||
|
{
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kGreen().withAlpha(0.5 * 255));
|
||||||
|
paint.setBlendMode(DlBlendMode::kPlus);
|
||||||
|
builder.DrawCircle(SkPoint::Make(200, 200), 100, paint);
|
||||||
|
// Should render a second circle, centered on the bottom-right-most edge of
|
||||||
|
// the circle.
|
||||||
|
SkMatrix matrix = SkMatrix::Translate((100 + 100 * k1OverSqrt2),
|
||||||
|
(100 + 100 * k1OverSqrt2)) *
|
||||||
|
SkMatrix::Scale(0.5, 0.5) *
|
||||||
|
SkMatrix::Translate(-100, -100);
|
||||||
|
auto backdrop_filter =
|
||||||
|
DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear);
|
||||||
|
builder.SaveLayer(nullptr, nullptr, backdrop_filter.get());
|
||||||
|
builder.Restore();
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(AiksTest, MatrixSaveLayerFilter) {
|
||||||
|
DisplayListBuilder builder;
|
||||||
|
|
||||||
|
DlPaint paint;
|
||||||
|
paint.setColor(DlColor::kBlack());
|
||||||
|
builder.DrawPaint(paint);
|
||||||
|
builder.SaveLayer({}, nullptr);
|
||||||
|
{
|
||||||
|
paint.setColor(DlColor::kGreen().withAlpha(255 * 0.5));
|
||||||
|
paint.setBlendMode(DlBlendMode::kPlus);
|
||||||
|
builder.DrawCircle({200, 200}, 100, paint);
|
||||||
|
// Should render a second circle, centered on the bottom-right-most edge of
|
||||||
|
// the circle.
|
||||||
|
|
||||||
|
SkMatrix matrix = SkMatrix::Translate((200 + 100 * k1OverSqrt2),
|
||||||
|
(200 + 100 * k1OverSqrt2)) *
|
||||||
|
SkMatrix::Scale(0.5, 0.5) *
|
||||||
|
SkMatrix::Translate(-200, -200);
|
||||||
|
DlPaint save_paint;
|
||||||
|
save_paint.setImageFilter(
|
||||||
|
DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear));
|
||||||
|
|
||||||
|
builder.SaveLayer(nullptr, &save_paint);
|
||||||
|
|
||||||
|
DlPaint circle_paint;
|
||||||
|
circle_paint.setColor(DlColor::kGreen().withAlpha(255 * 0.5));
|
||||||
|
circle_paint.setBlendMode(DlBlendMode::kPlus);
|
||||||
|
builder.DrawCircle({200, 200}, 100, circle_paint);
|
||||||
|
builder.Restore();
|
||||||
|
}
|
||||||
|
builder.Restore();
|
||||||
|
|
||||||
|
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
} // namespace impeller
|
} // namespace impeller
|
||||||
|
@ -600,7 +600,7 @@ void DlDispatcherBase::saveLayer(const SkRect& bounds,
|
|||||||
? ContentBoundsPromise::kMayClipContents
|
? ContentBoundsPromise::kMayClipContents
|
||||||
: ContentBoundsPromise::kContainsContents;
|
: ContentBoundsPromise::kContainsContents;
|
||||||
std::optional<Rect> impeller_bounds;
|
std::optional<Rect> impeller_bounds;
|
||||||
if (!options.content_is_unbounded()) {
|
if (!options.content_is_unbounded() || options.bounds_from_caller()) {
|
||||||
impeller_bounds = skia_conversions::ToRect(bounds);
|
impeller_bounds = skia_conversions::ToRect(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class SurfaceMTL final : public Surface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Perform the final blit and trigger end of frame workloads.
|
/// @brief Perform the final blit and trigger end of frame workloads.
|
||||||
bool PreparePresent();
|
bool PreparePresent() const;
|
||||||
|
|
||||||
// |Surface|
|
// |Surface|
|
||||||
bool Present() const override;
|
bool Present() const override;
|
||||||
@ -85,7 +85,7 @@ class SurfaceMTL final : public Surface {
|
|||||||
std::optional<IRect> clip_rect_;
|
std::optional<IRect> clip_rect_;
|
||||||
bool frame_boundary_ = false;
|
bool frame_boundary_ = false;
|
||||||
bool present_with_transaction_ = false;
|
bool present_with_transaction_ = false;
|
||||||
bool prepared_ = false;
|
mutable bool prepared_ = false;
|
||||||
|
|
||||||
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
|
static bool ShouldPerformPartialRepaint(std::optional<IRect> damage_rect);
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ IRect SurfaceMTL::coverage() const {
|
|||||||
return IRect::MakeSize(resolve_texture_->GetSize());
|
return IRect::MakeSize(resolve_texture_->GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SurfaceMTL::PreparePresent() {
|
bool SurfaceMTL::PreparePresent() const {
|
||||||
auto context = context_.lock();
|
auto context = context_.lock();
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return false;
|
return false;
|
||||||
@ -265,7 +265,9 @@ bool SurfaceMTL::PreparePresent() {
|
|||||||
|
|
||||||
// |Surface|
|
// |Surface|
|
||||||
bool SurfaceMTL::Present() const {
|
bool SurfaceMTL::Present() const {
|
||||||
FML_CHECK(prepared_);
|
if (!prepared_) {
|
||||||
|
PreparePresent();
|
||||||
|
}
|
||||||
auto context = context_.lock();
|
auto context = context_.lock();
|
||||||
if (!context) {
|
if (!context) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -493,6 +493,9 @@ impeller_Play_AiksTest_CanSaveLayerStandalone_Vulkan.png
|
|||||||
impeller_Play_AiksTest_ClearBlendWithBlur_Metal.png
|
impeller_Play_AiksTest_ClearBlendWithBlur_Metal.png
|
||||||
impeller_Play_AiksTest_ClearBlendWithBlur_OpenGLES.png
|
impeller_Play_AiksTest_ClearBlendWithBlur_OpenGLES.png
|
||||||
impeller_Play_AiksTest_ClearBlendWithBlur_Vulkan.png
|
impeller_Play_AiksTest_ClearBlendWithBlur_Vulkan.png
|
||||||
|
impeller_Play_AiksTest_ClearBlend_Metal.png
|
||||||
|
impeller_Play_AiksTest_ClearBlend_OpenGLES.png
|
||||||
|
impeller_Play_AiksTest_ClearBlend_Vulkan.png
|
||||||
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_Metal.png
|
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_Metal.png
|
||||||
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_OpenGLES.png
|
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_OpenGLES.png
|
||||||
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_Vulkan.png
|
impeller_Play_AiksTest_ClearColorOptimizationWhenSubpassIsBiggerThanParentPass_Vulkan.png
|
||||||
|
Loading…
x
Reference in New Issue
Block a user