Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
#include "impeller/aiks/paint_pass_delegate.h"
#include "impeller/aiks/testing/context_spy.h"
#include "impeller/entity/contents/color_source_contents.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/scene_contents.h"
#include "impeller/entity/contents/solid_color_contents.h"
#include "impeller/entity/contents/tiled_texture_contents.h"
Expand Down Expand Up @@ -393,6 +395,30 @@ 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({0, 0, 600, 600}, paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

namespace {
void CanRenderLinearGradientWithOverlappingStops(AiksTest* aiks_test,
Entity::TileMode tile_mode) {
Expand Down Expand Up @@ -2867,5 +2893,35 @@ TEST_P(AiksTest, MatrixBackdropFilter) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, SolidColorApplyColorFilter) {
auto contents = SolidColorContents();
contents.SetColor(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);
ASSERT_COLOR_NEAR(contents.GetColor(),
Color(0.433247, 0.879523, 0.825324, 0.75));
}

#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);

} // namespace testing
} // namespace impeller
10 changes: 6 additions & 4 deletions impeller/aiks/color_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ BlendColorFilter::BlendColorFilter(BlendMode blend_mode, Color color)

BlendColorFilter::~BlendColorFilter() = default;

std::shared_ptr<ColorFilterContents> BlendColorFilter::GetColorFilter(
std::shared_ptr<ColorFilterContents> BlendColorFilter::WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter =
Expand All @@ -67,7 +67,7 @@ MatrixColorFilter::MatrixColorFilter(ColorMatrix color_matrix)

MatrixColorFilter::~MatrixColorFilter() = default;

std::shared_ptr<ColorFilterContents> MatrixColorFilter::GetColorFilter(
std::shared_ptr<ColorFilterContents> MatrixColorFilter::WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter =
Expand All @@ -90,7 +90,8 @@ SrgbToLinearColorFilter::SrgbToLinearColorFilter() = default;

SrgbToLinearColorFilter::~SrgbToLinearColorFilter() = default;

std::shared_ptr<ColorFilterContents> SrgbToLinearColorFilter::GetColorFilter(
std::shared_ptr<ColorFilterContents>
SrgbToLinearColorFilter::WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)});
Expand All @@ -111,7 +112,8 @@ LinearToSrgbColorFilter::LinearToSrgbColorFilter() = default;

LinearToSrgbColorFilter::~LinearToSrgbColorFilter() = default;

std::shared_ptr<ColorFilterContents> LinearToSrgbColorFilter::GetColorFilter(
std::shared_ptr<ColorFilterContents>
LinearToSrgbColorFilter::WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const {
auto filter = ColorFilterContents::MakeSrgbToLinearFilter({std::move(input)});
Expand Down
21 changes: 16 additions & 5 deletions impeller/aiks/color_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct Paint;

class ColorFilter {
public:
/// A procedure that filters a given unpremultiplied color to produce a new
/// unpremultiplied color.
using ColorFilterProc = std::function<Color(Color)>;

ColorFilter();
Expand All @@ -32,10 +34,19 @@ class ColorFilter {

static std::shared_ptr<ColorFilter> MakeLinearToSrgb();

virtual std::shared_ptr<ColorFilterContents> GetColorFilter(
/// @brief Wraps the given filter input with a GPU-based filter that will
/// perform the color operation. The given input will first be
/// rendered to a texture and then filtered.
///
/// Note that this operation has no consideration for the original
/// geometry mask of the filter input. And the entire input texture is
/// treated as color information.
virtual std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const = 0;

/// @brief Returns a function that can be used to filter unpremultiplied
/// Impeller Colors on the CPU.
virtual ColorFilterProc GetCPUColorFilterProc() const = 0;
};

Expand All @@ -50,7 +61,7 @@ class BlendColorFilter final : public ColorFilter {
~BlendColorFilter() override;

// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;

Expand All @@ -73,7 +84,7 @@ class MatrixColorFilter final : public ColorFilter {
~MatrixColorFilter() override;

// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;

Expand All @@ -95,7 +106,7 @@ class SrgbToLinearColorFilter final : public ColorFilter {
~SrgbToLinearColorFilter() override;

// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;

Expand All @@ -114,7 +125,7 @@ class LinearToSrgbColorFilter final : public ColorFilter {
~LinearToSrgbColorFilter() override;

// |ColorFilter|
std::shared_ptr<ColorFilterContents> GetColorFilter(
std::shared_ptr<ColorFilterContents> WrapWithGPUColorFilter(
std::shared_ptr<FilterInput> input,
bool absorb_opacity) const override;

Expand Down
3 changes: 2 additions & 1 deletion impeller/aiks/color_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ ColorSource ColorSource::MakeImage(std::shared_ptr<Texture> texture,
if (paint.color_filter) {
TiledTextureContents::ColorFilterProc filter_proc =
[color_filter = paint.color_filter](FilterInput::Ref input) {
return color_filter->GetColorFilter(std::move(input), false);
return color_filter->WrapWithGPUColorFilter(std::move(input),
false);
};
contents->SetColorFilter(filter_proc);
}
Expand Down
17 changes: 13 additions & 4 deletions impeller/aiks/paint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,20 @@ std::shared_ptr<Contents> Paint::WithColorFilter(
if (color_source.GetType() == ColorSource::Type::kImage) {
return input;
}
if (color_filter) {
input =
color_filter->GetColorFilter(FilterInput::Make(input), absorb_opacity);

if (!color_filter) {
return input;
}
return input;

// Attempt to apply the color filter on the CPU first.
// Note: This is not just an optimization; some color sources rely on
// CPU-applied color filters to behave properly.
if (input->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
return input;
}

return color_filter->WrapWithGPUColorFilter(FilterInput::Make(input),
absorb_opacity);
}

/// A color matrix which inverts colors.
Expand Down
10 changes: 6 additions & 4 deletions impeller/compiler/shader_lib/impeller/texture.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ vec4 IPSampleLinearWithTileMode(sampler2D tex,
float y_coord_scale,
vec2 half_texel,
float x_tile_mode,
float y_tile_mode) {
float y_tile_mode,
vec4 decal_border_color) {
if (x_tile_mode == kTileModeDecal && (coords.x < 0 || coords.x >= 1) ||
y_tile_mode == kTileModeDecal && (coords.y < 0 || coords.y >= 1)) {
return vec4(0);
return decal_border_color;
}

return IPSampleLinear(tex, IPVec2Tile(coords, x_tile_mode, y_tile_mode),
Expand Down Expand Up @@ -152,9 +153,10 @@ vec4 IPSampleLinearWithTileMode(sampler2D tex,
vec2 coords,
float y_coord_scale,
vec2 half_texel,
float tile_mode) {
float tile_mode,
vec4 decal_border_color) {
return IPSampleLinearWithTileMode(tex, coords, y_coord_scale, half_texel,
tile_mode, tile_mode);
tile_mode, tile_mode, decal_border_color);
}

#endif
2 changes: 1 addition & 1 deletion impeller/display_list/dl_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ static Paint::ImageFilterProc ToImageFilterProc(
// "absorb opacity" flag to false. For image filters, the snapshot
// opacity needs to be deferred until the result of the filter chain is
// being blended with the layer.
return filter->GetColorFilter(std::move(input), false);
return filter->WrapWithGPUColorFilter(std::move(input), false);
};
break;
}
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/contents/conical_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ bool ConicalGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha = GetOpacity();
if (focus_) {
frag_info.focus = focus_.value();
Expand Down Expand Up @@ -140,6 +141,7 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.center = center_;
frag_info.radius = radius_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale();
frag_info.alpha = GetOpacity();
frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width,
Expand Down Expand Up @@ -193,4 +195,13 @@ bool ConicalGradientContents::RenderTexture(const ContentContext& renderer,
return true;
}

bool ConicalGradientContents::ApplyColorFilter(
const ColorFilterProc& color_filter_proc) {
for (Color& color : colors_) {
color = color_filter_proc(color);
}
decal_border_color_ = color_filter_proc(decal_border_color_);
return true;
}

} // namespace impeller
5 changes: 5 additions & 0 deletions impeller/entity/contents/conical_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class ConicalGradientContents final : public ColorSourceContents {
const Entity& entity,
RenderPass& pass) const override;

// |Contents|
[[nodiscard]] bool ApplyColorFilter(
const ColorFilterProc& color_filter_proc) override;

void SetCenterAndRadius(Point center, Scalar radius);

void SetColors(std::vector<Color> colors);
Expand Down Expand Up @@ -56,6 +60,7 @@ class ConicalGradientContents final : public ColorSourceContents {
std::vector<Color> colors_;
std::vector<Scalar> stops_;
Entity::TileMode tile_mode_;
Color decal_border_color_ = Color::BlackTransparent();
std::optional<Point> focus_;
Scalar focus_radius_ = 0.0f;

Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ std::optional<Color> Contents::AsBackgroundColor(const Entity& entity,
return {};
}

bool Contents::ApplyColorFilter(
const Contents::ColorFilterProc& color_filter_proc) {
return false;
}

bool Contents::ShouldRender(const Entity& entity,
const std::optional<Rect>& stencil_coverage) const {
if (!stencil_coverage.has_value()) {
Expand Down
21 changes: 21 additions & 0 deletions impeller/entity/contents/contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ ContentContextOptions OptionsFromPassAndEntity(const RenderPass& pass,

class Contents {
public:
/// A procedure that filters a given unpremultiplied color to produce a new
/// unpremultiplied color.
using ColorFilterProc = std::function<Color(Color)>;

struct StencilCoverage {
enum class Type { kNoChange, kAppend, kRestore };

Expand Down Expand Up @@ -126,6 +130,23 @@ class Contents {
virtual std::optional<Color> AsBackgroundColor(const Entity& entity,
ISize target_size) const;

/// @brief If possible, applies a color filter to this contents inputs on
/// the CPU.
///
/// This method will either fully apply the color filter or
/// perform no action. Partial/incorrect application of the color
/// filter will never occur.
///
/// @param[in] color_filter_proc A function that filters a given
/// unpremultiplied color to produce a new
/// unpremultiplied color.
///
/// @return True if the color filter was able to be fully applied to all
/// all relevant inputs. Otherwise, this operation is a no-op and
/// false is returned.
[[nodiscard]] virtual bool ApplyColorFilter(
const ColorFilterProc& color_filter_proc);

private:
std::optional<Rect> coverage_hint_;
std::optional<Size> color_source_size_;
Expand Down
11 changes: 11 additions & 0 deletions impeller/entity/contents/linear_gradient_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ bool LinearGradientContents::RenderTexture(const ContentContext& renderer,
frag_info.start_point = start_point_;
frag_info.end_point = end_point_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale();
frag_info.alpha = GetOpacity();
frag_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width,
Expand Down Expand Up @@ -138,6 +139,7 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
frag_info.start_point = start_point_;
frag_info.end_point = end_point_;
frag_info.tile_mode = static_cast<Scalar>(tile_mode_);
frag_info.decal_border_color = decal_border_color_;
frag_info.alpha = GetOpacity();

auto& host_buffer = pass.GetTransientsBuffer();
Expand Down Expand Up @@ -184,4 +186,13 @@ bool LinearGradientContents::RenderSSBO(const ContentContext& renderer,
return true;
}

bool LinearGradientContents::ApplyColorFilter(
const ColorFilterProc& color_filter_proc) {
for (Color& color : colors_) {
color = color_filter_proc(color);
}
decal_border_color_ = color_filter_proc(decal_border_color_);
return true;
}

} // namespace impeller
5 changes: 5 additions & 0 deletions impeller/entity/contents/linear_gradient_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class LinearGradientContents final : public ColorSourceContents {
const Entity& entity,
RenderPass& pass) const override;

// |Contents|
[[nodiscard]] bool ApplyColorFilter(
const ColorFilterProc& color_filter_proc) override;

void SetEndPoints(Point start_point, Point end_point);

void SetColors(std::vector<Color> colors);
Expand All @@ -59,6 +63,7 @@ class LinearGradientContents final : public ColorSourceContents {
std::vector<Color> colors_;
std::vector<Scalar> stops_;
Entity::TileMode tile_mode_;
Color decal_border_color_ = Color::BlackTransparent();

FML_DISALLOW_COPY_AND_ASSIGN(LinearGradientContents);
};
Expand Down
Loading