Skip to content

Commit a08cb8b

Browse files
bderodnfield
authored andcommitted
Border mask blur (flutter#132)
1 parent fa68eb6 commit a08cb8b

12 files changed

+320
-4
lines changed

impeller/entity/BUILD.gn

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ impeller_shaders("entity_shaders") {
2020
"shaders/texture_blend_screen.vert",
2121
"shaders/gaussian_blur.frag",
2222
"shaders/gaussian_blur.vert",
23+
"shaders/border_mask_blur.frag",
24+
"shaders/border_mask_blur.vert",
2325
"shaders/texture_fill.frag",
2426
"shaders/texture_fill.vert",
2527
"shaders/glyph_atlas.frag",
@@ -45,6 +47,8 @@ impeller_component("entity") {
4547
"contents/filters/filter_input.h",
4648
"contents/filters/gaussian_blur_filter_contents.cc",
4749
"contents/filters/gaussian_blur_filter_contents.h",
50+
"contents/filters/border_mask_blur_filter_contents.cc",
51+
"contents/filters/border_mask_blur_filter_contents.h",
4852
"contents/linear_gradient_contents.cc",
4953
"contents/linear_gradient_contents.h",
5054
"contents/snapshot.cc",

impeller/entity/contents/content_context.cc

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
2929
texture_pipelines_[{}] = std::make_unique<TexturePipeline>(*context_);
3030
gaussian_blur_pipelines_[{}] =
3131
std::make_unique<GaussianBlurPipeline>(*context_);
32+
border_mask_blur_pipelines_[{}] =
33+
std::make_unique<BorderMaskBlurPipeline>(*context_);
3234
solid_stroke_pipelines_[{}] =
3335
std::make_unique<SolidStrokePipeline>(*context_);
3436
glyph_atlas_pipelines_[{}] = std::make_unique<GlyphAtlasPipeline>(*context_);

impeller/entity/contents/content_context.h

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "flutter/fml/macros.h"
1212
#include "fml/logging.h"
1313
#include "impeller/base/validation.h"
14+
#include "impeller/entity/border_mask_blur.frag.h"
15+
#include "impeller/entity/border_mask_blur.vert.h"
1416
#include "impeller/entity/entity.h"
1517
#include "impeller/entity/gaussian_blur.frag.h"
1618
#include "impeller/entity/gaussian_blur.vert.h"
@@ -44,6 +46,8 @@ using TexturePipeline =
4446
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
4547
using GaussianBlurPipeline =
4648
PipelineT<GaussianBlurVertexShader, GaussianBlurFragmentShader>;
49+
using BorderMaskBlurPipeline =
50+
PipelineT<BorderMaskBlurVertexShader, BorderMaskBlurFragmentShader>;
4751
using SolidStrokePipeline =
4852
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
4953
using GlyphAtlasPipeline =
@@ -114,6 +118,11 @@ class ContentContext {
114118
return GetPipeline(gaussian_blur_pipelines_, opts);
115119
}
116120

121+
std::shared_ptr<Pipeline> GetBorderMaskBlurPipeline(
122+
ContentContextOptions opts) const {
123+
return GetPipeline(border_mask_blur_pipelines_, opts);
124+
}
125+
117126
std::shared_ptr<Pipeline> GetSolidStrokePipeline(
118127
ContentContextOptions opts) const {
119128
return GetPipeline(solid_stroke_pipelines_, opts);
@@ -156,6 +165,7 @@ class ContentContext {
156165
mutable Variants<TextureBlendScreenPipeline> texture_blend_screen_pipelines_;
157166
mutable Variants<TexturePipeline> texture_pipelines_;
158167
mutable Variants<GaussianBlurPipeline> gaussian_blur_pipelines_;
168+
mutable Variants<BorderMaskBlurPipeline> border_mask_blur_pipelines_;
159169
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
160170
mutable Variants<ClipPipeline> clip_pipelines_;
161171
mutable Variants<GlyphAtlasPipeline> glyph_atlas_pipelines_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h"
6+
#include "impeller/entity/contents/content_context.h"
7+
8+
#include "impeller/entity/contents/contents.h"
9+
#include "impeller/renderer/render_pass.h"
10+
#include "impeller/renderer/sampler_library.h"
11+
12+
namespace impeller {
13+
14+
BorderMaskBlurFilterContents::BorderMaskBlurFilterContents() = default;
15+
16+
BorderMaskBlurFilterContents::~BorderMaskBlurFilterContents() = default;
17+
18+
void BorderMaskBlurFilterContents::SetSigma(Sigma sigma_x, Sigma sigma_y) {
19+
sigma_x_ = sigma_x;
20+
sigma_y_ = sigma_y;
21+
}
22+
23+
void BorderMaskBlurFilterContents::SetBlurStyle(BlurStyle blur_style) {
24+
blur_style_ = blur_style;
25+
26+
switch (blur_style) {
27+
case FilterContents::BlurStyle::kNormal:
28+
src_color_factor_ = false;
29+
inner_blur_factor_ = true;
30+
outer_blur_factor_ = true;
31+
break;
32+
case FilterContents::BlurStyle::kSolid:
33+
src_color_factor_ = true;
34+
inner_blur_factor_ = false;
35+
outer_blur_factor_ = true;
36+
break;
37+
case FilterContents::BlurStyle::kOuter:
38+
src_color_factor_ = false;
39+
inner_blur_factor_ = false;
40+
outer_blur_factor_ = true;
41+
break;
42+
case FilterContents::BlurStyle::kInner:
43+
src_color_factor_ = false;
44+
inner_blur_factor_ = true;
45+
outer_blur_factor_ = false;
46+
break;
47+
}
48+
}
49+
50+
bool BorderMaskBlurFilterContents::RenderFilter(
51+
const FilterInput::Vector& inputs,
52+
const ContentContext& renderer,
53+
const Entity& entity,
54+
RenderPass& pass,
55+
const Rect& coverage) const {
56+
if (inputs.empty()) {
57+
return true;
58+
}
59+
60+
using VS = BorderMaskBlurPipeline::VertexShader;
61+
using FS = BorderMaskBlurPipeline::FragmentShader;
62+
63+
auto& host_buffer = pass.GetTransientsBuffer();
64+
65+
auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity);
66+
if (!input_snapshot.has_value()) {
67+
return true;
68+
}
69+
auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage);
70+
if (!maybe_input_uvs.has_value()) {
71+
return true;
72+
}
73+
auto input_uvs = maybe_input_uvs.value();
74+
75+
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
76+
vtx_builder.AddVertices({
77+
{Point(0, 0), input_uvs[0]},
78+
{Point(1, 0), input_uvs[1]},
79+
{Point(1, 1), input_uvs[3]},
80+
{Point(0, 0), input_uvs[0]},
81+
{Point(1, 1), input_uvs[3]},
82+
{Point(0, 1), input_uvs[2]},
83+
});
84+
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
85+
86+
Command cmd;
87+
cmd.label = "Border Mask Blur Filter";
88+
auto options = OptionsFromPass(pass);
89+
options.blend_mode = Entity::BlendMode::kSource;
90+
cmd.pipeline = renderer.GetBorderMaskBlurPipeline(options);
91+
cmd.BindVertices(vtx_buffer);
92+
93+
VS::FrameInfo frame_info;
94+
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
95+
auto scale = entity.GetTransformation().GetScale();
96+
frame_info.sigma_uv = Vector2(scale.x, scale.y) *
97+
Vector2(sigma_x_.sigma, sigma_y_.sigma).Abs() /
98+
input_snapshot->texture->GetSize();
99+
frame_info.src_factor = src_color_factor_;
100+
frame_info.inner_blur_factor = inner_blur_factor_;
101+
frame_info.outer_blur_factor = outer_blur_factor_;
102+
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
103+
VS::BindFrameInfo(cmd, uniform_view);
104+
105+
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
106+
FS::BindTextureSampler(cmd, input_snapshot->texture, sampler);
107+
108+
return pass.AddCommand(std::move(cmd));
109+
}
110+
111+
std::optional<Rect> BorderMaskBlurFilterContents::GetCoverage(
112+
const Entity& entity) const {
113+
auto coverage = FilterContents::GetCoverage(entity);
114+
if (!coverage.has_value()) {
115+
return std::nullopt;
116+
}
117+
118+
// Technically this works with all of our current filters, but this should be
119+
// using the input[0] transform, not the entity transform!
120+
// See: https://github.com/flutter/impeller/pull/130#issuecomment-1098892423
121+
auto transformed_blur_vector =
122+
entity.GetTransformation()
123+
.TransformDirection(
124+
Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius))
125+
.Abs();
126+
auto extent = coverage->size + transformed_blur_vector * 2;
127+
return Rect(coverage->origin - transformed_blur_vector,
128+
Size(extent.x, extent.y));
129+
}
130+
131+
} // namespace impeller
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <memory>
8+
#include <optional>
9+
#include "impeller/entity/contents/filters/filter_contents.h"
10+
#include "impeller/entity/contents/filters/filter_input.h"
11+
12+
namespace impeller {
13+
14+
class BorderMaskBlurFilterContents final : public FilterContents {
15+
public:
16+
BorderMaskBlurFilterContents();
17+
18+
~BorderMaskBlurFilterContents() override;
19+
20+
void SetSigma(Sigma sigma_x, Sigma sigma_y);
21+
22+
void SetBlurStyle(BlurStyle blur_style);
23+
24+
// |Contents|
25+
std::optional<Rect> GetCoverage(const Entity& entity) const override;
26+
27+
private:
28+
// |FilterContents|
29+
bool RenderFilter(const FilterInput::Vector& input_textures,
30+
const ContentContext& renderer,
31+
const Entity& entity,
32+
RenderPass& pass,
33+
const Rect& coverage) const override;
34+
Sigma sigma_x_;
35+
Sigma sigma_y_;
36+
BlurStyle blur_style_ = BlurStyle::kNormal;
37+
bool src_color_factor_ = false;
38+
bool inner_blur_factor_ = true;
39+
bool outer_blur_factor_ = true;
40+
41+
FML_DISALLOW_COPY_AND_ASSIGN(BorderMaskBlurFilterContents);
42+
};
43+
44+
} // namespace impeller

impeller/entity/contents/filters/filter_contents.cc

+13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "impeller/base/validation.h"
1616
#include "impeller/entity/contents/content_context.h"
1717
#include "impeller/entity/contents/filters/blend_filter_contents.h"
18+
#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h"
1819
#include "impeller/entity/contents/filters/filter_input.h"
1920
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
2021
#include "impeller/entity/contents/texture_contents.h"
@@ -88,6 +89,18 @@ std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
8889
return y_blur;
8990
}
9091

92+
std::shared_ptr<FilterContents> FilterContents::MakeBorderMaskBlur(
93+
FilterInput::Ref input,
94+
Sigma sigma_x,
95+
Sigma sigma_y,
96+
BlurStyle blur_style) {
97+
auto filter = std::make_shared<BorderMaskBlurFilterContents>();
98+
filter->SetInputs({input});
99+
filter->SetSigma(sigma_x, sigma_y);
100+
filter->SetBlurStyle(blur_style);
101+
return filter;
102+
}
103+
91104
FilterContents::FilterContents() = default;
92105

93106
FilterContents::~FilterContents() = default;

impeller/entity/contents/filters/filter_contents.h

+6
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ class FilterContents : public Contents {
9696
Sigma sigma_y,
9797
BlurStyle blur_style = BlurStyle::kNormal);
9898

99+
static std::shared_ptr<FilterContents> MakeBorderMaskBlur(
100+
FilterInput::Ref input,
101+
Sigma sigma_x,
102+
Sigma sigma_y,
103+
BlurStyle blur_style = BlurStyle::kNormal);
104+
99105
FilterContents();
100106

101107
~FilterContents() override;

impeller/entity/contents/filters/gaussian_blur_filter_contents.h

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include <optional>
99
#include "impeller/entity/contents/filters/filter_contents.h"
1010
#include "impeller/entity/contents/filters/filter_input.h"
11-
#include "impeller/geometry/matrix.h"
1211

1312
namespace impeller {
1413

impeller/entity/entity_unittests.cc

+12-3
Original file line numberDiff line numberDiff line change
@@ -698,16 +698,18 @@ TEST_F(EntityTest, GaussianBlurFilter) {
698698
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
699699
if (first_frame) {
700700
first_frame = false;
701-
ImGui::SetNextWindowSize({500, 220});
702-
ImGui::SetNextWindowPos({300, 550});
701+
ImGui::SetNextWindowSize({500, 250});
702+
ImGui::SetNextWindowPos({300, 500});
703703
}
704704

705+
const char* blur_type_names[] = {"Image blur", "Mask blur"};
705706
const char* blur_style_names[] = {"Normal", "Solid", "Outer", "Inner"};
706707
const FilterContents::BlurStyle blur_styles[] = {
707708
FilterContents::BlurStyle::kNormal, FilterContents::BlurStyle::kSolid,
708709
FilterContents::BlurStyle::kOuter, FilterContents::BlurStyle::kInner};
709710

710711
// UI state.
712+
static int selected_blur_type = 0;
711713
static float blur_amount[2] = {20, 20};
712714
static int selected_blur_style = 0;
713715
static Color cover_color(1, 0, 0, 0.2);
@@ -719,6 +721,8 @@ TEST_F(EntityTest, GaussianBlurFilter) {
719721

720722
ImGui::Begin("Controls");
721723
{
724+
ImGui::Combo("Blur type", &selected_blur_type, blur_type_names,
725+
sizeof(blur_type_names) / sizeof(char*));
722726
ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200);
723727
ImGui::Combo("Blur style", &selected_blur_style, blur_style_names,
724728
sizeof(blur_style_names) / sizeof(char*));
@@ -742,14 +746,19 @@ TEST_F(EntityTest, GaussianBlurFilter) {
742746
FilterContents::Sigma{blur_amount[1]},
743747
blur_styles[selected_blur_style]);
744748

749+
auto mask_blur = FilterContents::MakeBorderMaskBlur(
750+
FilterInput::Make(boston), FilterContents::Sigma{blur_amount[0]},
751+
FilterContents::Sigma{blur_amount[1]},
752+
blur_styles[selected_blur_style]);
753+
745754
ISize input_size = boston->GetSize();
746755
auto rect = Rect(-Point(input_size) / 2, Size(input_size));
747756
auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
748757
Matrix::MakeRotationZ(Radians(rotation)) *
749758
Matrix::MakeScale(Vector2(scale[0], scale[1])) *
750759
Matrix::MakeSkew(skew[0], skew[1]);
751760

752-
auto target_contents = blur;
761+
auto target_contents = selected_blur_type == 0 ? blur : mask_blur;
753762

754763
Entity entity;
755764
entity.SetPath(PathBuilder{}.AddRect(rect).TakePath());

0 commit comments

Comments
 (0)