diff --git a/impeller/entity/contents/filters/filter_contents.cc b/impeller/entity/contents/filters/filter_contents.cc index a038b0e8d6316..59a7149fc0021 100644 --- a/impeller/entity/contents/filters/filter_contents.cc +++ b/impeller/entity/contents/filters/filter_contents.cc @@ -61,20 +61,26 @@ std::shared_ptr FilterContents::MakeBlend( std::shared_ptr FilterContents::MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector) { + Vector2 blur_vector, + BlurStyle blur_style, + FilterInput::Ref source_override) { auto blur = std::make_shared(); blur->SetInputs({input}); blur->SetBlurVector(blur_vector); + blur->SetBlurStyle(blur_style); + blur->SetSourceOverride(source_override); return blur; } std::shared_ptr FilterContents::MakeGaussianBlur( FilterInput::Ref input, Scalar sigma_x, - Scalar sigma_y) { - auto x_blur = MakeDirectionalGaussianBlur(input, Point(sigma_x, 0)); - auto y_blur = - MakeDirectionalGaussianBlur(FilterInput::Make(x_blur), Point(0, sigma_y)); + Scalar sigma_y, + BlurStyle blur_style) { + auto x_blur = + MakeDirectionalGaussianBlur(input, Point(sigma_x, 0), BlurStyle::kNormal); + auto y_blur = MakeDirectionalGaussianBlur( + FilterInput::Make(x_blur), Point(0, sigma_y), blur_style, input); return y_blur; } diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 2088163277735..63c60bcc44eac 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -18,15 +18,31 @@ class Pipeline; class FilterContents : public Contents { public: + enum class BlurStyle { + /// Blurred inside and outside. + kNormal, + /// Solid inside, blurred outside. + kSolid, + /// Nothing inside, blurred outside. + kOuter, + /// Blurred inside, nothing outside. + kInner, + }; + static std::shared_ptr MakeBlend(Entity::BlendMode blend_mode, FilterInput::Vector inputs); static std::shared_ptr MakeDirectionalGaussianBlur( FilterInput::Ref input, - Vector2 blur_vector); + Vector2 blur_vector, + BlurStyle blur_style = BlurStyle::kNormal, + FilterInput::Ref alpha_mask = nullptr); - static std::shared_ptr - MakeGaussianBlur(FilterInput::Ref input, Scalar sigma_x, Scalar sigma_y); + static std::shared_ptr MakeGaussianBlur( + FilterInput::Ref input, + Scalar sigma_x, + Scalar sigma_y, + BlurStyle blur_style = BlurStyle::kNormal); FilterContents(); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 3f7f572ffa662..34441ddff9fd4 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -30,6 +30,38 @@ void DirectionalGaussianBlurFilterContents::SetBlurVector(Vector2 blur_vector) { blur_vector_ = blur_vector; } +void DirectionalGaussianBlurFilterContents::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; + } +} + +void DirectionalGaussianBlurFilterContents::SetSourceOverride( + FilterInput::Ref alpha_mask) { + source_override_ = alpha_mask; +} + bool DirectionalGaussianBlurFilterContents::RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, @@ -45,43 +77,59 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( auto& host_buffer = pass.GetTransientsBuffer(); - // Because this filter is intended to be used with only one input parameter, - // and GetBounds just increases the input size by a factor of the direction, - // we we can just scale up the UVs by the same amount and don't need to worry - // about mapping the UVs to the destination rect (like we do in - // BlendFilterContents). - auto input = inputs[0]->GetSnapshot(renderer, entity); - auto input_size = input->texture->GetSize(); + auto input_bounds = inputs[0]->GetBounds(entity); + auto filter_bounds = GetBounds(entity); auto transformed_blur = entity.GetTransformation().TransformDirection(blur_vector_); - auto uv_offset = transformed_blur.Abs() / input_size; // LTRB Scalar uv[4] = { - -uv_offset.x, - -uv_offset.y, - 1 + uv_offset.x, - 1 + uv_offset.y, + (filter_bounds.GetLeft() - input_bounds.GetLeft()) / + input_bounds.size.width, + (filter_bounds.GetTop() - input_bounds.GetTop()) / + input_bounds.size.height, + 1 + (filter_bounds.GetRight() - input_bounds.GetRight()) / + input_bounds.size.width, + 1 + (filter_bounds.GetBottom() - input_bounds.GetBottom()) / + input_bounds.size.height, + }; + + auto source = source_override_ ? source_override_ : inputs[0]; + auto source_texture = source->GetSnapshot(renderer, entity); + auto source_bounds = source->GetBounds(entity); + + // LTRB + Scalar uv_src[4] = { + (filter_bounds.GetLeft() - source_bounds.GetLeft()) / + source_bounds.size.width, + (filter_bounds.GetTop() - source_bounds.GetTop()) / + source_bounds.size.height, + 1 + (filter_bounds.GetRight() - source_bounds.GetRight()) / + source_bounds.size.width, + 1 + (filter_bounds.GetBottom() - source_bounds.GetBottom()) / + source_bounds.size.height, }; VertexBufferBuilder vtx_builder; - auto size = pass.GetRenderTargetSize(); vtx_builder.AddVertices({ - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, 0), Point(uv[2], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, 0), Point(uv[0], uv[1])}, - {Point(size.width, size.height), Point(uv[2], uv[3])}, - {Point(0, size.height), Point(uv[0], uv[3])}, + {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, + {Point(1, 0), Point(uv[2], uv[1]), Point(uv_src[2], uv_src[1])}, + {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, + {Point(0, 0), Point(uv[0], uv[1]), Point(uv_src[0], uv_src[1])}, + {Point(1, 1), Point(uv[2], uv[3]), Point(uv_src[2], uv_src[3])}, + {Point(0, 1), Point(uv[0], uv[3]), Point(uv_src[0], uv_src[3])}, }); auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); VS::FrameInfo frame_info; - frame_info.texture_size = Point(input_size); + frame_info.texture_size = Point(input_bounds.size); frame_info.blur_radius = transformed_blur.GetLength(); frame_info.blur_direction = transformed_blur.Normalize(); + frame_info.src_factor = src_color_factor_; + frame_info.inner_blur_factor = inner_blur_factor_; + frame_info.outer_blur_factor = outer_blur_factor_; auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); @@ -93,8 +141,9 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter( cmd.BindVertices(vtx_buffer); FS::BindTextureSampler(cmd, input->texture, sampler); + FS::BindAlphaMaskSampler(cmd, source_texture->texture, sampler); - frame_info.mvp = Matrix::MakeOrthographic(size); + frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); auto uniform_view = host_buffer.EmplaceUniform(frame_info); VS::BindFrameInfo(cmd, uniform_view); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 17aa92af792fe..0b401d2cc0c43 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/filter_input.h" #include "impeller/geometry/matrix.h" @@ -18,6 +20,10 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { void SetBlurVector(Vector2 blur_vector); + void SetBlurStyle(BlurStyle blur_style); + + void SetSourceOverride(FilterInput::Ref alpha_mask); + // |Contents| Rect GetBounds(const Entity& entity) const override; @@ -30,6 +36,11 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents { const Rect& bounds) const override; Vector2 blur_vector_; + BlurStyle blur_style_ = BlurStyle::kNormal; + bool src_color_factor_ = false; + bool inner_blur_factor_ = true; + bool outer_blur_factor_ = true; + FilterInput::Ref source_override_; FML_DISALLOW_COPY_AND_ASSIGN(DirectionalGaussianBlurFilterContents); }; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b2c9779df029c..c4804d0567700 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -689,11 +689,18 @@ TEST_F(EntityTest, GaussianBlurFilter) { auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; - ImGui::SetNextWindowSize({500, 190}); + ImGui::SetNextWindowSize({500, 220}); ImGui::SetNextWindowPos({300, 550}); } + 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 float blur_amount[2] = {20, 20}; + static int selected_blur_style = 0; static Color cover_color(1, 0, 0, 0.2); static Color bounds_color(0, 1, 0, 0.1); static float offset[2] = {500, 400}; @@ -702,21 +709,27 @@ TEST_F(EntityTest, GaussianBlurFilter) { static float skew[2] = {0, 0}; ImGui::Begin("Controls"); - ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); - ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); - ImGui::ColorEdit4("Bounds color", reinterpret_cast(&bounds_color)); - ImGui::SliderFloat2("Translation", &offset[0], 0, - pass.GetRenderTargetSize().width); - ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); - ImGui::SliderFloat2("Scale", &scale[0], 0, 3); - ImGui::SliderFloat2("Skew", &skew[0], -3, 3); + { + ImGui::SliderFloat2("Blur", &blur_amount[0], 0, 200); + ImGui::Combo("Blur style", &selected_blur_style, blur_style_names, + sizeof(blur_style_names) / sizeof(char*)); + ImGui::ColorEdit4("Cover color", reinterpret_cast(&cover_color)); + ImGui::ColorEdit4("Bounds color", + reinterpret_cast(&bounds_color)); + ImGui::SliderFloat2("Translation", &offset[0], 0, + pass.GetRenderTargetSize().width); + ImGui::SliderFloat("Rotation", &rotation, 0, kPi * 2); + ImGui::SliderFloat2("Scale", &scale[0], 0, 3); + ImGui::SliderFloat2("Skew", &skew[0], -3, 3); + } ImGui::End(); auto blend = FilterContents::MakeBlend( Entity::BlendMode::kPlus, FilterInput::Make({boston, bridge, bridge})); auto blur = FilterContents::MakeGaussianBlur( - FilterInput::Make(blend), blur_amount[0], blur_amount[1]); + FilterInput::Make(blend), blur_amount[0], blur_amount[1], + blur_styles[selected_blur_style]); ISize input_size = boston->GetSize(); auto rect = Rect(-Point(input_size) / 2, Size(input_size)); diff --git a/impeller/entity/shaders/gaussian_blur.frag b/impeller/entity/shaders/gaussian_blur.frag index 61fa0f060d5b9..939ecf9e20c78 100644 --- a/impeller/entity/shaders/gaussian_blur.frag +++ b/impeller/entity/shaders/gaussian_blur.frag @@ -5,16 +5,21 @@ // 1D (directional) gaussian blur. // // Paths for future optimization: -// * Remove the uv bounds check branch in SampleColor by adding optional +// * Remove the uv bounds multiplier in SampleColor by adding optional // support for SamplerAddressMode::ClampToBorder in the texture sampler. // * Sample from higher mipmap levels when the blur radius is high enough. uniform sampler2D texture_sampler; +uniform sampler2D alpha_mask_sampler; in vec2 v_texture_coords; +in vec2 v_src_texture_coords; in vec2 v_texture_size; in vec2 v_blur_direction; in float v_blur_radius; +in float v_src_factor; +in float v_inner_blur_factor; +in float v_outer_blur_factor; out vec4 frag_color; @@ -27,21 +32,27 @@ float Gaussian(float x) { } // Emulate SamplerAddressMode::ClampToBorder. -vec4 SampleWithBorder(vec2 uv) { - if (uv.x > 0 && uv.y > 0 && uv.x < 1 && uv.y < 1) { - return texture(texture_sampler, uv); - } - return vec4(0); +vec4 SampleWithBorder(sampler2D tex, vec2 uv) { + float within_bounds = float(uv.x >= 0 && uv.y >= 0 && uv.x < 1 && uv.y < 1); + return texture(tex, uv) * within_bounds; } void main() { vec4 total = vec4(0); float total_gaussian = 0; + vec2 blur_uv_offset = v_blur_direction / v_texture_size; for (float i = -v_blur_radius; i <= v_blur_radius; i++) { float gaussian = Gaussian(i); total_gaussian += gaussian; - total += gaussian * SampleWithBorder(v_texture_coords + - v_blur_direction * i / v_texture_size); + total += gaussian * SampleWithBorder(texture_sampler, + v_texture_coords + blur_uv_offset * i); } - frag_color = total / total_gaussian; + + vec4 blur_color = total / total_gaussian; + + vec4 src_color = SampleWithBorder(alpha_mask_sampler, v_src_texture_coords); + float blur_factor = v_inner_blur_factor * float(src_color.a > 0) + + v_outer_blur_factor * float(src_color.a == 0); + + frag_color = blur_color * blur_factor + src_color * v_src_factor; } diff --git a/impeller/entity/shaders/gaussian_blur.vert b/impeller/entity/shaders/gaussian_blur.vert index 8d7d1db931de1..a358a2b5719be 100644 --- a/impeller/entity/shaders/gaussian_blur.vert +++ b/impeller/entity/shaders/gaussian_blur.vert @@ -5,23 +5,37 @@ uniform FrameInfo { mat4 mvp; vec2 texture_size; + vec2 blur_direction; float blur_radius; + + float src_factor; + float inner_blur_factor; + float outer_blur_factor; } frame_info; in vec2 vertices; in vec2 texture_coords; +in vec2 src_texture_coords; out vec2 v_texture_coords; +out vec2 v_src_texture_coords; out vec2 v_texture_size; out vec2 v_blur_direction; out float v_blur_radius; +out float v_src_factor; +out float v_inner_blur_factor; +out float v_outer_blur_factor; void main() { gl_Position = frame_info.mvp * vec4(vertices, 0.0, 1.0); v_texture_coords = texture_coords; + v_src_texture_coords = src_texture_coords; v_texture_size = frame_info.texture_size; v_blur_direction = frame_info.blur_direction; v_blur_radius = frame_info.blur_radius; + v_src_factor = frame_info.src_factor; + v_inner_blur_factor = frame_info.inner_blur_factor; + v_outer_blur_factor = frame_info.outer_blur_factor; }