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 7 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
1 change: 1 addition & 0 deletions impeller/aiks/paint_pass_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ std::shared_ptr<Contents> PaintPassDelegate::CreateContentsForSubpassTarget(
contents->SetTexture(target);
contents->SetSourceRect(Rect::MakeSize(target->GetSize()));
contents->SetOpacity(paint_.color.alpha);
contents->SetDeferApplyingOpacity(true);

return paint_.WithFilters(std::move(contents), false, effect_transform);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we'll have to diverge with the ordering of WithFilters at some point anyhow, maybe we should just do the WithFilters logic inline here and build the filter chain exactly as it needs to be. Then, we can set the alpha absorption flag on the color filter here where it's needed instead of having to do it in the dispatcher or color filter procs.

To facilitate this, we could also add a ColorFilterContents class that extends FilterContents which is inherited by all of the color filters -- this would hold the setter and remove the repeated code without having to place it on filters that will never use it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I'll make the changes as you suggested after the holidays (aka next week).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I adjusted the PR according to your suggestion, the one difference is that I added a method Paint::WithFiltersForSubpassTarget, which can reuse some logic. Let me know what you think!

}
Expand Down
24 changes: 14 additions & 10 deletions impeller/display_list/display_list_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,8 @@ void DisplayListDispatcher::setColorSource(
}

static std::optional<Paint::ColorFilterProc> ToColorFilterProc(
const flutter::DlColorFilter* filter) {
const flutter::DlColorFilter* filter,
bool absorb_opacity = true) {
if (filter == nullptr) {
return std::nullopt;
}
Expand All @@ -446,25 +447,27 @@ static std::optional<Paint::ColorFilterProc> ToColorFilterProc(
auto dl_blend = filter->asBlend();
auto blend_mode = ToBlendMode(dl_blend->mode());
auto color = ToColor(dl_blend->color());
return [blend_mode, color](FilterInput::Ref input) {
return FilterContents::MakeBlend(blend_mode, {input}, color);
return [blend_mode, color, absorb_opacity](FilterInput::Ref input) {
return FilterContents::MakeBlend(blend_mode, {input}, color,
absorb_opacity);
};
}
case flutter::DlColorFilterType::kMatrix: {
const flutter::DlMatrixColorFilter* dl_matrix = filter->asMatrix();
impeller::FilterContents::ColorMatrix color_matrix;
dl_matrix->get_matrix(color_matrix.array);
return [color_matrix](FilterInput::Ref input) {
return FilterContents::MakeColorMatrix({input}, color_matrix);
return [color_matrix, absorb_opacity](FilterInput::Ref input) {
return FilterContents::MakeColorMatrix({input}, color_matrix,
absorb_opacity);
};
}
case flutter::DlColorFilterType::kSrgbToLinearGamma:
return [](FilterInput::Ref input) {
return FilterContents::MakeSrgbToLinearFilter({input});
return [absorb_opacity](FilterInput::Ref input) {
return FilterContents::MakeSrgbToLinearFilter({input}, absorb_opacity);
};
case flutter::DlColorFilterType::kLinearToSrgbGamma:
return [](FilterInput::Ref input) {
return FilterContents::MakeLinearToSrgbFilter({input});
return [absorb_opacity](FilterInput::Ref input) {
return FilterContents::MakeLinearToSrgbFilter({input}, absorb_opacity);
};
case flutter::DlColorFilterType::kUnknown:
FML_LOG(ERROR) << "requested DlColorFilterType::kUnknown";
Expand Down Expand Up @@ -630,7 +633,8 @@ static std::optional<Paint::ImageFilterProc> ToImageFilterProc(
auto color_filter_image_filter = filter->asColorFilter();
FML_DCHECK(color_filter_image_filter);
auto color_filter_proc =
ToColorFilterProc(color_filter_image_filter->color_filter().get());
ToColorFilterProc(color_filter_image_filter->color_filter().get(),
/*absorb_opacity=*/false);
if (!color_filter_proc.has_value()) {
return std::nullopt;
}
Expand Down
117 changes: 117 additions & 0 deletions impeller/display_list/display_list_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,123 @@ TEST_P(DisplayListTest, CanClampTheResultingColorOfColorMatrixFilter) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, SaveLayerWithColorMatrixFiltersAndAlphaDrawCorrectly) {
auto texture = CreateTextureForFixture("boston.jpg");
bool first_frame = true;
enum class Type { kUseAsImageFilter, kUseAsColorFilter, kDisableFilter };
auto callback = [&]() {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowPos({10, 10});
}

static float alpha = 0.5;
static int selected_type = 0;
const char* names[] = {"Use as image filter", "Use as color filter",
"Disable filter"};

static float color_matrix[20] = {
1, 0, 0, 0, 0, //
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
0, 0, 0, 2, 0, //
};

ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Alpha", &alpha, 0, 1);

ImGui::Combo("Type", &selected_type, names, sizeof(names) / sizeof(char*));
std::string label = "##1";
for (int i = 0; i < 20; i += 5) {
ImGui::InputScalarN(label.c_str(), ImGuiDataType_Float,
&(color_matrix[i]), 5, nullptr, nullptr, "%.2f", 0);
label[2]++;
}
ImGui::End();

flutter::DisplayListBuilder builder;
flutter::DlPaint save_paint;
save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
auto color_filter =
std::make_shared<flutter::DlMatrixColorFilter>(color_matrix);
Type type = static_cast<Type>(selected_type);
switch (type) {
case Type::kUseAsImageFilter: {
auto image_filter =
std::make_shared<flutter::DlColorFilterImageFilter>(color_filter);
save_paint.setImageFilter(image_filter);
break;
}
case Type::kUseAsColorFilter: {
save_paint.setColorFilter(color_filter);
break;
}
case Type::kDisableFilter:
break;
}
builder.saveLayer(nullptr, &save_paint);
flutter::DlPaint draw_paint;
builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
flutter::DlImageSampling::kNearestNeighbor, &draw_paint);
builder.restore();
return builder.Build();
};

ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(DisplayListTest, SaveLayerWithBlendFiltersAndAlphaDrawCorrectly) {
auto texture = CreateTextureForFixture("boston.jpg");
bool first_frame = true;
enum class Type { kUseAsImageFilter, kUseAsColorFilter, kDisableFilter };
auto callback = [&]() {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowPos({10, 10});
}

static float alpha = 0.5;
static int selected_type = 0;
const char* names[] = {"Use as image filter", "Use as color filter",
"Disable filter"};

ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::SliderFloat("Alpha", &alpha, 0, 1);

ImGui::Combo("Type", &selected_type, names, sizeof(names) / sizeof(char*));
ImGui::End();

flutter::DisplayListBuilder builder;
flutter::DlPaint save_paint;
save_paint.setAlpha(static_cast<uint8_t>(255 * alpha));
auto color_filter = std::make_shared<flutter::DlBlendColorFilter>(
flutter::DlColor::kRed(), flutter::DlBlendMode::kDstOver);
Type type = static_cast<Type>(selected_type);
switch (type) {
case Type::kUseAsImageFilter: {
auto image_filter =
std::make_shared<flutter::DlColorFilterImageFilter>(color_filter);
save_paint.setImageFilter(image_filter);
break;
}
case Type::kUseAsColorFilter: {
save_paint.setColorFilter(color_filter);
break;
}
case Type::kDisableFilter:
break;
}
builder.saveLayer(nullptr, &save_paint);
flutter::DlPaint draw_paint;
draw_paint.setColor(flutter::DlColor::kBlue());
builder.drawRect(SkRect::MakeLTRB(100, 100, 400, 400), draw_paint);
builder.restore();
return builder.Build();
};

ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(DisplayListTest, CanDrawBackdropFilter) {
auto texture = CreateTextureForFixture("embarcadero.jpg");

Expand Down
48 changes: 30 additions & 18 deletions impeller/entity/contents/filters/blend_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static std::optional<Snapshot> AdvancedBlend(
const Entity& entity,
const Rect& coverage,
std::optional<Color> foreground_color,
bool absorb_opacity,
PipelineProc pipeline_proc) {
using VS = typename TPipeline::VertexShader;
using FS = typename TPipeline::FragmentShader;
Expand Down Expand Up @@ -107,6 +108,7 @@ static std::optional<Snapshot> AdvancedBlend(
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, sampler);
blend_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
blend_info.dst_input_alpha = absorb_opacity ? dst_snapshot->opacity : 1.0f;

if (foreground_color.has_value()) {
blend_info.color_factor = 1;
Expand Down Expand Up @@ -141,7 +143,8 @@ static std::optional<Snapshot> AdvancedBlend(

return Snapshot{.texture = out_texture,
.transform = Matrix::MakeTranslation(coverage.origin),
.sampler_descriptor = dst_snapshot->sampler_descriptor};
.sampler_descriptor = dst_snapshot->sampler_descriptor,
.opacity = absorb_opacity ? 1.0f : dst_snapshot->opacity};
}

static std::optional<Snapshot> PipelineBlend(
Expand All @@ -150,10 +153,13 @@ static std::optional<Snapshot> PipelineBlend(
const Entity& entity,
const Rect& coverage,
BlendMode pipeline_blend,
std::optional<Color> foreground_color) {
std::optional<Color> foreground_color,
bool absorb_opacity) {
using VS = BlendPipeline::VertexShader;
using FS = BlendPipeline::FragmentShader;

auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity);

ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
RenderPass& pass) {
auto& host_buffer = pass.GetTransientsBuffer();
Expand Down Expand Up @@ -195,6 +201,7 @@ static std::optional<Snapshot> PipelineBlend(
FS::FragInfo frag_info;
frag_info.texture_sampler_y_coord_scale =
input->texture->GetYCoordScale();
frag_info.input_alpha = absorb_opacity ? input->opacity : 1.0f;
FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));

Expand All @@ -203,10 +210,9 @@ static std::optional<Snapshot> PipelineBlend(
};

// Draw the first texture using kSource.

options.blend_mode = BlendMode::kSource;
cmd.pipeline = renderer.GetBlendPipeline(options);
if (!add_blend_command(inputs[0]->GetSnapshot(renderer, entity))) {
if (!add_blend_command(input_snapshot)) {
return true;
}

Expand Down Expand Up @@ -255,19 +261,21 @@ static std::optional<Snapshot> PipelineBlend(
.texture = out_texture,
.transform = Matrix::MakeTranslation(coverage.origin),
.sampler_descriptor =
inputs[0]->GetSnapshot(renderer, entity)->sampler_descriptor};
inputs[0]->GetSnapshot(renderer, entity)->sampler_descriptor,
.opacity = absorb_opacity ? 1.0f : input_snapshot->opacity};
}

#define BLEND_CASE(mode) \
case BlendMode::k##mode: \
advanced_blend_proc_ = [](const FilterInput::Vector& inputs, \
const ContentContext& renderer, \
const Entity& entity, const Rect& coverage, \
std::optional<Color> fg_color) { \
PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
return AdvancedBlend<BlendScreenPipeline>(inputs, renderer, entity, \
coverage, fg_color, p); \
}; \
#define BLEND_CASE(mode) \
case BlendMode::k##mode: \
advanced_blend_proc_ = [](const FilterInput::Vector& inputs, \
const ContentContext& renderer, \
const Entity& entity, const Rect& coverage, \
std::optional<Color> fg_color, \
bool absorb_opacity) { \
PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
return AdvancedBlend<BlendScreenPipeline>( \
inputs, renderer, entity, coverage, fg_color, absorb_opacity, p); \
}; \
break;

void BlendFilterContents::SetBlendMode(BlendMode blend_mode) {
Expand Down Expand Up @@ -305,6 +313,10 @@ void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
foreground_color_ = color;
}

void BlendFilterContents::SetAbsorbOpacity(bool absorb_opacity) {
absorb_opacity_ = absorb_opacity;
}

std::optional<Snapshot> BlendFilterContents::RenderFilter(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
Expand All @@ -318,17 +330,17 @@ std::optional<Snapshot> BlendFilterContents::RenderFilter(
if (inputs.size() == 1 && !foreground_color_.has_value()) {
// Nothing to blend.
return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
std::nullopt);
std::nullopt, absorb_opacity_);
}

if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
foreground_color_);
foreground_color_, absorb_opacity_);
}

if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
return advanced_blend_proc_(inputs, renderer, entity, coverage,
foreground_color_);
foreground_color_, absorb_opacity_);
}
FML_UNREACHABLE();
}
Expand Down
6 changes: 5 additions & 1 deletion impeller/entity/contents/filters/blend_filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class BlendFilterContents : public FilterContents {
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
std::optional<Color> foreground_color)>;
std::optional<Color> foreground_color,
bool absorb_opacity)>;

BlendFilterContents();

Expand All @@ -28,6 +29,8 @@ class BlendFilterContents : public FilterContents {
/// been blended.
void SetForegroundColor(std::optional<Color> color);

void SetAbsorbOpacity(bool absorb_opacity);

private:
// |FilterContents|
std::optional<Snapshot> RenderFilter(const FilterInput::Vector& inputs,
Expand All @@ -39,6 +42,7 @@ class BlendFilterContents : public FilterContents {
BlendMode blend_mode_ = BlendMode::kSourceOver;
AdvancedBlendProc advanced_blend_proc_;
std::optional<Color> foreground_color_;
bool absorb_opacity_ = false;

FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ std::optional<Snapshot> BorderMaskBlurFilterContents::RenderFilter(
out_texture->SetLabel("BorderMaskBlurFilter Texture");

return Snapshot{.texture = out_texture,
.transform = Matrix::MakeTranslation(coverage.origin)};
.transform = Matrix::MakeTranslation(coverage.origin),
.opacity = input_snapshot->opacity};
}

std::optional<Rect> BorderMaskBlurFilterContents::GetFilterCoverage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ void ColorMatrixFilterContents::SetMatrix(const ColorMatrix& matrix) {
matrix_ = matrix;
}

void ColorMatrixFilterContents::SetAbsorbOpacity(bool absorb_opacity) {
absorb_opacity_ = absorb_opacity;
}

std::optional<Snapshot> ColorMatrixFilterContents::RenderFilter(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
Expand Down Expand Up @@ -87,6 +91,7 @@ std::optional<Snapshot> ColorMatrixFilterContents::RenderFilter(
matrix[3], matrix[8], matrix[13], matrix[18]
);
// clang-format on
frag_info.input_alpha = absorb_opacity_ ? input_snapshot->opacity : 1.0f;
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
FS::BindInputTexture(cmd, input_snapshot->texture, sampler);
FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
Expand All @@ -105,7 +110,8 @@ std::optional<Snapshot> ColorMatrixFilterContents::RenderFilter(

return Snapshot{.texture = out_texture,
.transform = input_snapshot->transform,
.sampler_descriptor = input_snapshot->sampler_descriptor};
.sampler_descriptor = input_snapshot->sampler_descriptor,
.opacity = absorb_opacity_ ? 1.0f : input_snapshot->opacity};
}

} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class ColorMatrixFilterContents final : public FilterContents {

void SetMatrix(const ColorMatrix& matrix);

void SetAbsorbOpacity(bool absorb_opacity);

private:
// |FilterContents|
std::optional<Snapshot> RenderFilter(
Expand All @@ -32,6 +34,7 @@ class ColorMatrixFilterContents final : public FilterContents {
const Rect& coverage) const override;

ColorMatrix matrix_;
bool absorb_opacity_ = false;

FML_DISALLOW_COPY_AND_ASSIGN(ColorMatrixFilterContents);
};
Expand Down
Loading