Skip to content

Commit 503dff6

Browse files
bderodnfield
authored andcommitted
Add directional + 2D gaussian blur (flutter#84)
1 parent 396ef7d commit 503dff6

File tree

9 files changed

+313
-9
lines changed

9 files changed

+313
-9
lines changed

impeller/entity/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ impeller_shaders("entity_shaders") {
1818
"shaders/texture_blend.vert",
1919
"shaders/texture_blend_screen.frag",
2020
"shaders/texture_blend_screen.vert",
21+
"shaders/gaussian_blur.frag",
22+
"shaders/gaussian_blur.vert",
2123
"shaders/texture_fill.frag",
2224
"shaders/texture_fill.vert",
2325
"shaders/glyph_atlas.frag",

impeller/entity/contents/content_context.cc

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
2323
texture_blend_screen_pipelines_[{}] =
2424
std::make_unique<TextureBlendScreenPipeline>(*context_);
2525
texture_pipelines_[{}] = std::make_unique<TexturePipeline>(*context_);
26+
gaussian_blur_pipelines_[{}] =
27+
std::make_unique<GaussianBlurPipeline>(*context_);
2628
solid_stroke_pipelines_[{}] =
2729
std::make_unique<SolidStrokePipeline>(*context_);
2830
glyph_atlas_pipelines_[{}] = std::make_unique<GlyphAtlasPipeline>(*context_);

impeller/entity/contents/content_context.h

+14-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "fml/logging.h"
1313
#include "impeller/base/validation.h"
1414
#include "impeller/entity/entity.h"
15+
#include "impeller/entity/gaussian_blur.frag.h"
16+
#include "impeller/entity/gaussian_blur.vert.h"
1517
#include "impeller/entity/glyph_atlas.frag.h"
1618
#include "impeller/entity/glyph_atlas.vert.h"
1719
#include "impeller/entity/gradient_fill.frag.h"
@@ -20,13 +22,13 @@
2022
#include "impeller/entity/solid_fill.vert.h"
2123
#include "impeller/entity/solid_stroke.frag.h"
2224
#include "impeller/entity/solid_stroke.vert.h"
25+
#include "impeller/entity/texture_blend.frag.h"
26+
#include "impeller/entity/texture_blend.vert.h"
27+
#include "impeller/entity/texture_blend_screen.frag.h"
28+
#include "impeller/entity/texture_blend_screen.vert.h"
2329
#include "impeller/entity/texture_fill.frag.h"
2430
#include "impeller/entity/texture_fill.vert.h"
2531
#include "impeller/renderer/formats.h"
26-
#include "texture_blend.frag.h"
27-
#include "texture_blend.vert.h"
28-
#include "texture_blend_screen.frag.h"
29-
#include "texture_blend_screen.vert.h"
3032

3133
namespace impeller {
3234

@@ -40,6 +42,8 @@ using TextureBlendScreenPipeline =
4042
PipelineT<TextureBlendScreenVertexShader, TextureBlendScreenFragmentShader>;
4143
using TexturePipeline =
4244
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
45+
using GaussianBlurPipeline =
46+
PipelineT<GaussianBlurVertexShader, GaussianBlurFragmentShader>;
4347
using SolidStrokePipeline =
4448
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
4549
using GlyphAtlasPipeline =
@@ -100,6 +104,11 @@ class ContentContext {
100104
return GetPipeline(texture_pipelines_, opts);
101105
}
102106

107+
std::shared_ptr<Pipeline> GetGaussianBlurPipeline(
108+
ContentContextOptions opts) const {
109+
return GetPipeline(gaussian_blur_pipelines_, opts);
110+
}
111+
103112
std::shared_ptr<Pipeline> GetSolidStrokePipeline(
104113
ContentContextOptions opts) const {
105114
return GetPipeline(solid_stroke_pipelines_, opts);
@@ -138,6 +147,7 @@ class ContentContext {
138147
mutable Variants<TextureBlendPipeline> texture_blend_pipelines_;
139148
mutable Variants<TextureBlendScreenPipeline> texture_blend_screen_pipelines_;
140149
mutable Variants<TexturePipeline> texture_pipelines_;
150+
mutable Variants<GaussianBlurPipeline> gaussian_blur_pipelines_;
141151
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
142152
mutable Variants<ClipPipeline> clip_pipelines_;
143153
mutable Variants<ClipPipeline> clip_restoration_pipelines_;

impeller/entity/contents/filter_contents.cc

+125-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "filter_contents.h"
66

7+
#include <algorithm>
8+
#include <cmath>
79
#include <memory>
810
#include <optional>
911
#include <variant>
@@ -16,7 +18,9 @@
1618
#include "impeller/entity/entity.h"
1719
#include "impeller/geometry/path_builder.h"
1820
#include "impeller/renderer/command_buffer.h"
21+
#include "impeller/renderer/formats.h"
1922
#include "impeller/renderer/render_pass.h"
23+
#include "impeller/renderer/sampler_descriptor.h"
2024
#include "impeller/renderer/sampler_library.h"
2125

2226
namespace impeller {
@@ -57,6 +61,28 @@ std::shared_ptr<FilterContents> FilterContents::MakeBlend(
5761
FML_UNREACHABLE();
5862
}
5963

64+
std::shared_ptr<FilterContents> FilterContents::MakeDirectionalGaussianBlur(
65+
InputVariant input_texture,
66+
Scalar radius,
67+
Vector2 direction,
68+
bool clip_border) {
69+
auto blur = std::make_shared<DirectionalGaussianBlurFilterContents>();
70+
blur->SetInputTextures({input_texture});
71+
blur->SetRadius(radius);
72+
blur->SetDirection(direction);
73+
blur->SetClipBorder(clip_border);
74+
return blur;
75+
}
76+
77+
std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
78+
InputVariant input_texture,
79+
Scalar radius,
80+
bool clip_border) {
81+
auto x_blur = MakeDirectionalGaussianBlur(input_texture, radius, Point(1, 0),
82+
clip_border);
83+
return MakeDirectionalGaussianBlur(x_blur, radius, Point(0, 1), false);
84+
}
85+
6086
FilterContents::FilterContents() = default;
6187

6288
FilterContents::~FilterContents() = default;
@@ -90,7 +116,7 @@ std::optional<std::shared_ptr<Texture>> FilterContents::RenderFilterToTexture(
90116
const ContentContext& renderer,
91117
const Entity& entity,
92118
RenderPass& pass) const {
93-
auto output_size = GetOutputSize();
119+
auto output_size = GetOutputSize(input_textures_);
94120
if (output_size.IsZero()) {
95121
return std::nullopt;
96122
}
@@ -155,14 +181,17 @@ ISize FilterContents::GetOutputSize() const {
155181
if (input_textures_.empty()) {
156182
return {};
157183
}
184+
return GetOutputSize(input_textures_);
185+
}
158186

187+
ISize FilterContents::GetOutputSize(const InputTextures& input_textures) const {
159188
if (auto filter =
160-
std::get_if<std::shared_ptr<FilterContents>>(&input_textures_[0])) {
161-
return filter->get()->GetOutputSize();
189+
std::get_if<std::shared_ptr<FilterContents>>(&input_textures[0])) {
190+
return filter->get()->GetOutputSize(input_textures);
162191
}
163192

164193
if (auto texture =
165-
std::get_if<std::shared_ptr<Texture>>(&input_textures_[0])) {
194+
std::get_if<std::shared_ptr<Texture>>(&input_textures[0])) {
166195
return texture->get()->GetSize();
167196
}
168197

@@ -348,4 +377,96 @@ bool BlendFilterContents::RenderFilter(
348377
FML_UNREACHABLE();
349378
}
350379

380+
/*******************************************************************************
381+
******* DirectionalGaussianBlurFilterContents
382+
******************************************************************************/
383+
384+
DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() =
385+
default;
386+
387+
DirectionalGaussianBlurFilterContents::
388+
~DirectionalGaussianBlurFilterContents() = default;
389+
390+
void DirectionalGaussianBlurFilterContents::SetRadius(Scalar radius) {
391+
radius_ = std::max(radius, 1e-3f);
392+
}
393+
394+
void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) {
395+
direction_ = direction.Normalize();
396+
}
397+
398+
void DirectionalGaussianBlurFilterContents::SetClipBorder(bool clip) {
399+
clip_ = clip;
400+
}
401+
402+
bool DirectionalGaussianBlurFilterContents::RenderFilter(
403+
const std::vector<std::shared_ptr<Texture>>& input_textures,
404+
const ContentContext& renderer,
405+
RenderPass& pass) const {
406+
using VS = GaussianBlurPipeline::VertexShader;
407+
using FS = GaussianBlurPipeline::FragmentShader;
408+
409+
auto& host_buffer = pass.GetTransientsBuffer();
410+
411+
ISize size = FilterContents::GetOutputSize();
412+
Point uv_offset = clip_ ? (Point(radius_, radius_) / size) : Point();
413+
// LTRB
414+
Scalar uv[4] = {
415+
-uv_offset.x,
416+
-uv_offset.y,
417+
1 + uv_offset.x,
418+
1 + uv_offset.y,
419+
};
420+
421+
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
422+
vtx_builder.AddVertices({
423+
{Point(0, 0), Point(uv[0], uv[1])},
424+
{Point(size.width, 0), Point(uv[2], uv[1])},
425+
{Point(size.width, size.height), Point(uv[2], uv[3])},
426+
{Point(0, 0), Point(uv[0], uv[1])},
427+
{Point(size.width, size.height), Point(uv[2], uv[3])},
428+
{Point(0, size.height), Point(uv[0], uv[3])},
429+
});
430+
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
431+
432+
VS::FrameInfo frame_info;
433+
frame_info.mvp = Matrix::MakeOrthographic(size);
434+
frame_info.texture_size = Point(size);
435+
frame_info.blur_radius = radius_;
436+
frame_info.blur_direction = direction_;
437+
438+
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
439+
auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
440+
441+
Command cmd;
442+
cmd.label = "Gaussian Blur Filter";
443+
auto options = OptionsFromPass(pass);
444+
options.blend_mode = Entity::BlendMode::kSource;
445+
cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
446+
cmd.BindVertices(vtx_buffer);
447+
VS::BindFrameInfo(cmd, uniform_view);
448+
for (const auto& texture : input_textures) {
449+
FS::BindTextureSampler(cmd, texture, sampler);
450+
pass.AddCommand(cmd);
451+
}
452+
453+
return true;
454+
}
455+
456+
ISize DirectionalGaussianBlurFilterContents::GetOutputSize(
457+
const InputTextures& input_textures) const {
458+
ISize size;
459+
if (auto filter =
460+
std::get_if<std::shared_ptr<FilterContents>>(&input_textures[0])) {
461+
size = filter->get()->GetOutputSize();
462+
} else if (auto texture =
463+
std::get_if<std::shared_ptr<Texture>>(&input_textures[0])) {
464+
size = texture->get()->GetSize();
465+
} else {
466+
FML_UNREACHABLE();
467+
}
468+
469+
return size + (clip_ ? ISize(radius_ * 2, radius_ * 2) : ISize());
470+
}
471+
351472
} // namespace impeller

impeller/entity/contents/filter_contents.h

+49-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ class FilterContents : public Contents {
3030
Entity::BlendMode blend_mode,
3131
InputTextures input_textures);
3232

33+
static std::shared_ptr<FilterContents> MakeDirectionalGaussianBlur(
34+
InputVariant input_texture,
35+
Scalar radius,
36+
Vector2 direction,
37+
bool clip_border = false);
38+
39+
static std::shared_ptr<FilterContents> MakeGaussianBlur(
40+
InputVariant input_texture,
41+
Scalar radius,
42+
bool clip_border = false);
43+
3344
FilterContents();
3445

3546
~FilterContents() override;
@@ -54,6 +65,9 @@ class FilterContents : public Contents {
5465
const Entity& entity,
5566
RenderPass& pass) const;
5667

68+
/// @brief Fetch the size of the output texture.
69+
ISize GetOutputSize() const;
70+
5771
private:
5872
/// @brief Takes a set of zero or more input textures and writes to an output
5973
/// texture.
@@ -63,7 +77,7 @@ class FilterContents : public Contents {
6377
RenderPass& pass) const = 0;
6478

6579
/// @brief Determines the size of the output texture.
66-
virtual ISize GetOutputSize() const;
80+
virtual ISize GetOutputSize(const InputTextures& input_textures) const;
6781

6882
InputTextures input_textures_;
6983
Rect destination_;
@@ -89,6 +103,7 @@ class BlendFilterContents : public FilterContents {
89103
void SetBlendMode(Entity::BlendMode blend_mode);
90104

91105
private:
106+
// |FilterContents|
92107
bool RenderFilter(const std::vector<std::shared_ptr<Texture>>& input_textures,
93108
const ContentContext& renderer,
94109
RenderPass& pass) const override;
@@ -99,4 +114,37 @@ class BlendFilterContents : public FilterContents {
99114
FML_DISALLOW_COPY_AND_ASSIGN(BlendFilterContents);
100115
};
101116

117+
/*******************************************************************************
118+
******* DirectionalGaussionBlurFilterContents
119+
******************************************************************************/
120+
121+
class DirectionalGaussianBlurFilterContents final : public FilterContents {
122+
public:
123+
DirectionalGaussianBlurFilterContents();
124+
125+
~DirectionalGaussianBlurFilterContents() override;
126+
127+
void SetRadius(Scalar radius);
128+
129+
void SetDirection(Vector2 direction);
130+
131+
void SetClipBorder(bool clip);
132+
133+
private:
134+
// |FilterContents|
135+
bool RenderFilter(const std::vector<std::shared_ptr<Texture>>& input_textures,
136+
const ContentContext& renderer,
137+
RenderPass& pass) const override;
138+
139+
// |FilterContents|
140+
virtual ISize GetOutputSize(
141+
const InputTextures& input_textures) const override;
142+
143+
Scalar radius_ = 0;
144+
Vector2 direction_;
145+
bool clip_ = false;
146+
147+
FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents);
148+
};
149+
102150
} // namespace impeller

impeller/entity/entity_unittests.cc

+44
Original file line numberDiff line numberDiff line change
@@ -671,5 +671,49 @@ TEST_F(EntityTest, Filters) {
671671
ASSERT_TRUE(OpenPlaygroundHere(callback));
672672
}
673673

674+
TEST_F(EntityTest, GaussianBlurFilter) {
675+
auto bridge = CreateTextureForFixture("bay_bridge.jpg");
676+
auto boston = CreateTextureForFixture("boston.jpg");
677+
auto kalimba = CreateTextureForFixture("kalimba.jpg");
678+
ASSERT_TRUE(bridge && boston && kalimba);
679+
680+
bool first_frame = true;
681+
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
682+
if (first_frame) {
683+
first_frame = false;
684+
ImGui::SetNextWindowSize({450, 150});
685+
ImGui::SetNextWindowPos({200, 450});
686+
}
687+
688+
ImGui::Begin("Controls");
689+
static float offset[2] = {500, 400};
690+
ImGui::SliderFloat2("Position offset", &offset[0], 0, 1000);
691+
static float scale = 1;
692+
ImGui::SliderFloat("Scale", &scale, 0, 1);
693+
static float blur_radius = 20;
694+
ImGui::SliderFloat("Blur radius", &blur_radius, 0, 200);
695+
static bool clip_border = true;
696+
ImGui::Checkbox("Clip", &clip_border);
697+
698+
auto blend = FilterContents::MakeBlend(Entity::BlendMode::kPlus,
699+
{boston, bridge, bridge});
700+
701+
auto blur =
702+
FilterContents::MakeGaussianBlur(blend, blur_radius, clip_border);
703+
704+
auto output_size = Size(blur->GetOutputSize());
705+
Rect bounds(Point(offset[0], offset[1]) - output_size / 2 * scale,
706+
output_size * scale);
707+
708+
ImGui::End();
709+
710+
Entity entity;
711+
entity.SetPath(PathBuilder{}.AddRect(bounds).TakePath());
712+
entity.SetContents(blur);
713+
return entity.Render(context, pass);
714+
};
715+
ASSERT_TRUE(OpenPlaygroundHere(callback));
716+
}
717+
674718
} // namespace testing
675719
} // namespace impeller

0 commit comments

Comments
 (0)