Skip to content

Commit

Permalink
Border mask blur (flutter#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored and dnfield committed Apr 27, 2022
1 parent fa68eb6 commit a08cb8b
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 4 deletions.
4 changes: 4 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ impeller_shaders("entity_shaders") {
"shaders/texture_blend_screen.vert",
"shaders/gaussian_blur.frag",
"shaders/gaussian_blur.vert",
"shaders/border_mask_blur.frag",
"shaders/border_mask_blur.vert",
"shaders/texture_fill.frag",
"shaders/texture_fill.vert",
"shaders/glyph_atlas.frag",
Expand All @@ -45,6 +47,8 @@ impeller_component("entity") {
"contents/filters/filter_input.h",
"contents/filters/gaussian_blur_filter_contents.cc",
"contents/filters/gaussian_blur_filter_contents.h",
"contents/filters/border_mask_blur_filter_contents.cc",
"contents/filters/border_mask_blur_filter_contents.h",
"contents/linear_gradient_contents.cc",
"contents/linear_gradient_contents.h",
"contents/snapshot.cc",
Expand Down
2 changes: 2 additions & 0 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ ContentContext::ContentContext(std::shared_ptr<Context> context)
texture_pipelines_[{}] = std::make_unique<TexturePipeline>(*context_);
gaussian_blur_pipelines_[{}] =
std::make_unique<GaussianBlurPipeline>(*context_);
border_mask_blur_pipelines_[{}] =
std::make_unique<BorderMaskBlurPipeline>(*context_);
solid_stroke_pipelines_[{}] =
std::make_unique<SolidStrokePipeline>(*context_);
glyph_atlas_pipelines_[{}] = std::make_unique<GlyphAtlasPipeline>(*context_);
Expand Down
10 changes: 10 additions & 0 deletions impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "flutter/fml/macros.h"
#include "fml/logging.h"
#include "impeller/base/validation.h"
#include "impeller/entity/border_mask_blur.frag.h"
#include "impeller/entity/border_mask_blur.vert.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/gaussian_blur.frag.h"
#include "impeller/entity/gaussian_blur.vert.h"
Expand Down Expand Up @@ -44,6 +46,8 @@ using TexturePipeline =
PipelineT<TextureFillVertexShader, TextureFillFragmentShader>;
using GaussianBlurPipeline =
PipelineT<GaussianBlurVertexShader, GaussianBlurFragmentShader>;
using BorderMaskBlurPipeline =
PipelineT<BorderMaskBlurVertexShader, BorderMaskBlurFragmentShader>;
using SolidStrokePipeline =
PipelineT<SolidStrokeVertexShader, SolidStrokeFragmentShader>;
using GlyphAtlasPipeline =
Expand Down Expand Up @@ -114,6 +118,11 @@ class ContentContext {
return GetPipeline(gaussian_blur_pipelines_, opts);
}

std::shared_ptr<Pipeline> GetBorderMaskBlurPipeline(
ContentContextOptions opts) const {
return GetPipeline(border_mask_blur_pipelines_, opts);
}

std::shared_ptr<Pipeline> GetSolidStrokePipeline(
ContentContextOptions opts) const {
return GetPipeline(solid_stroke_pipelines_, opts);
Expand Down Expand Up @@ -156,6 +165,7 @@ class ContentContext {
mutable Variants<TextureBlendScreenPipeline> texture_blend_screen_pipelines_;
mutable Variants<TexturePipeline> texture_pipelines_;
mutable Variants<GaussianBlurPipeline> gaussian_blur_pipelines_;
mutable Variants<BorderMaskBlurPipeline> border_mask_blur_pipelines_;
mutable Variants<SolidStrokePipeline> solid_stroke_pipelines_;
mutable Variants<ClipPipeline> clip_pipelines_;
mutable Variants<GlyphAtlasPipeline> glyph_atlas_pipelines_;
Expand Down
131 changes: 131 additions & 0 deletions impeller/entity/contents/filters/border_mask_blur_filter_contents.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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 "impeller/entity/contents/filters/border_mask_blur_filter_contents.h"
#include "impeller/entity/contents/content_context.h"

#include "impeller/entity/contents/contents.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"

namespace impeller {

BorderMaskBlurFilterContents::BorderMaskBlurFilterContents() = default;

BorderMaskBlurFilterContents::~BorderMaskBlurFilterContents() = default;

void BorderMaskBlurFilterContents::SetSigma(Sigma sigma_x, Sigma sigma_y) {
sigma_x_ = sigma_x;
sigma_y_ = sigma_y;
}

void BorderMaskBlurFilterContents::SetBlurStyle(BlurStyle blur_style) {
blur_style_ = blur_style;

switch (blur_style) {
case FilterContents::BlurStyle::kNormal:
src_color_factor_ = false;
inner_blur_factor_ = true;
outer_blur_factor_ = true;
break;
case FilterContents::BlurStyle::kSolid:
src_color_factor_ = true;
inner_blur_factor_ = false;
outer_blur_factor_ = true;
break;
case FilterContents::BlurStyle::kOuter:
src_color_factor_ = false;
inner_blur_factor_ = false;
outer_blur_factor_ = true;
break;
case FilterContents::BlurStyle::kInner:
src_color_factor_ = false;
inner_blur_factor_ = true;
outer_blur_factor_ = false;
break;
}
}

bool BorderMaskBlurFilterContents::RenderFilter(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Rect& coverage) const {
if (inputs.empty()) {
return true;
}

using VS = BorderMaskBlurPipeline::VertexShader;
using FS = BorderMaskBlurPipeline::FragmentShader;

auto& host_buffer = pass.GetTransientsBuffer();

auto input_snapshot = inputs[0]->GetSnapshot(renderer, entity);
if (!input_snapshot.has_value()) {
return true;
}
auto maybe_input_uvs = input_snapshot->GetCoverageUVs(coverage);
if (!maybe_input_uvs.has_value()) {
return true;
}
auto input_uvs = maybe_input_uvs.value();

VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{Point(0, 0), input_uvs[0]},
{Point(1, 0), input_uvs[1]},
{Point(1, 1), input_uvs[3]},
{Point(0, 0), input_uvs[0]},
{Point(1, 1), input_uvs[3]},
{Point(0, 1), input_uvs[2]},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);

Command cmd;
cmd.label = "Border Mask Blur Filter";
auto options = OptionsFromPass(pass);
options.blend_mode = Entity::BlendMode::kSource;
cmd.pipeline = renderer.GetBorderMaskBlurPipeline(options);
cmd.BindVertices(vtx_buffer);

VS::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
auto scale = entity.GetTransformation().GetScale();
frame_info.sigma_uv = Vector2(scale.x, scale.y) *
Vector2(sigma_x_.sigma, sigma_y_.sigma).Abs() /
input_snapshot->texture->GetSize();
frame_info.src_factor = src_color_factor_;
frame_info.inner_blur_factor = inner_blur_factor_;
frame_info.outer_blur_factor = outer_blur_factor_;
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(cmd, uniform_view);

auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({});
FS::BindTextureSampler(cmd, input_snapshot->texture, sampler);

return pass.AddCommand(std::move(cmd));
}

std::optional<Rect> BorderMaskBlurFilterContents::GetCoverage(
const Entity& entity) const {
auto coverage = FilterContents::GetCoverage(entity);
if (!coverage.has_value()) {
return std::nullopt;
}

// Technically this works with all of our current filters, but this should be
// using the input[0] transform, not the entity transform!
// See: https://github.com/flutter/impeller/pull/130#issuecomment-1098892423
auto transformed_blur_vector =
entity.GetTransformation()
.TransformDirection(
Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius))
.Abs();
auto extent = coverage->size + transformed_blur_vector * 2;
return Rect(coverage->origin - transformed_blur_vector,
Size(extent.x, extent.y));
}

} // namespace impeller
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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.

#pragma once

#include <memory>
#include <optional>
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/filter_input.h"

namespace impeller {

class BorderMaskBlurFilterContents final : public FilterContents {
public:
BorderMaskBlurFilterContents();

~BorderMaskBlurFilterContents() override;

void SetSigma(Sigma sigma_x, Sigma sigma_y);

void SetBlurStyle(BlurStyle blur_style);

// |Contents|
std::optional<Rect> GetCoverage(const Entity& entity) const override;

private:
// |FilterContents|
bool RenderFilter(const FilterInput::Vector& input_textures,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Rect& coverage) const override;
Sigma sigma_x_;
Sigma sigma_y_;
BlurStyle blur_style_ = BlurStyle::kNormal;
bool src_color_factor_ = false;
bool inner_blur_factor_ = true;
bool outer_blur_factor_ = true;

FML_DISALLOW_COPY_AND_ASSIGN(BorderMaskBlurFilterContents);
};

} // namespace impeller
13 changes: 13 additions & 0 deletions impeller/entity/contents/filters/filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "impeller/base/validation.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/blend_filter_contents.h"
#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h"
#include "impeller/entity/contents/filters/filter_input.h"
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
#include "impeller/entity/contents/texture_contents.h"
Expand Down Expand Up @@ -88,6 +89,18 @@ std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
return y_blur;
}

std::shared_ptr<FilterContents> FilterContents::MakeBorderMaskBlur(
FilterInput::Ref input,
Sigma sigma_x,
Sigma sigma_y,
BlurStyle blur_style) {
auto filter = std::make_shared<BorderMaskBlurFilterContents>();
filter->SetInputs({input});
filter->SetSigma(sigma_x, sigma_y);
filter->SetBlurStyle(blur_style);
return filter;
}

FilterContents::FilterContents() = default;

FilterContents::~FilterContents() = default;
Expand Down
6 changes: 6 additions & 0 deletions impeller/entity/contents/filters/filter_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ class FilterContents : public Contents {
Sigma sigma_y,
BlurStyle blur_style = BlurStyle::kNormal);

static std::shared_ptr<FilterContents> MakeBorderMaskBlur(
FilterInput::Ref input,
Sigma sigma_x,
Sigma sigma_y,
BlurStyle blur_style = BlurStyle::kNormal);

FilterContents();

~FilterContents() override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <optional>
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/filter_input.h"
#include "impeller/geometry/matrix.h"

namespace impeller {

Expand Down
15 changes: 12 additions & 3 deletions impeller/entity/entity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -698,16 +698,18 @@ TEST_F(EntityTest, GaussianBlurFilter) {
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowSize({500, 220});
ImGui::SetNextWindowPos({300, 550});
ImGui::SetNextWindowSize({500, 250});
ImGui::SetNextWindowPos({300, 500});
}

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

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

ImGui::Begin("Controls");
{
ImGui::Combo("Blur type", &selected_blur_type, blur_type_names,
sizeof(blur_type_names) / sizeof(char*));
ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200);
ImGui::Combo("Blur style", &selected_blur_style, blur_style_names,
sizeof(blur_style_names) / sizeof(char*));
Expand All @@ -742,14 +746,19 @@ TEST_F(EntityTest, GaussianBlurFilter) {
FilterContents::Sigma{blur_amount[1]},
blur_styles[selected_blur_style]);

auto mask_blur = FilterContents::MakeBorderMaskBlur(
FilterInput::Make(boston), FilterContents::Sigma{blur_amount[0]},
FilterContents::Sigma{blur_amount[1]},
blur_styles[selected_blur_style]);

ISize input_size = boston->GetSize();
auto rect = Rect(-Point(input_size) / 2, Size(input_size));
auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
Matrix::MakeRotationZ(Radians(rotation)) *
Matrix::MakeScale(Vector2(scale[0], scale[1])) *
Matrix::MakeSkew(skew[0], skew[1]);

auto target_contents = blur;
auto target_contents = selected_blur_type == 0 ? blur : mask_blur;

Entity entity;
entity.SetPath(PathBuilder{}.AddRect(rect).TakePath());
Expand Down
Loading

0 comments on commit a08cb8b

Please sign in to comment.