forked from firka/flutter
[Impeller] deleted the old blur (flutter/engine#50470)
fixes https://github.com/flutter/flutter/issues/131579 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
This commit is contained in:
@@ -144,7 +144,6 @@
|
||||
../../../flutter/impeller/docs
|
||||
../../../flutter/impeller/entity/contents/checkerboard_contents_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/content_context_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/host_buffer_unittests.cc
|
||||
|
||||
@@ -5102,8 +5102,6 @@ ORIGIN: ../../../flutter/impeller/entity/contents/filters/color_filter_contents.
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/color_filter_contents.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/color_matrix_filter_contents.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/filter_contents.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/filter_contents.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc + ../../../flutter/LICENSE
|
||||
@@ -7935,8 +7933,6 @@ FILE: ../../../flutter/impeller/entity/contents/filters/color_filter_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/color_filter_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/color_matrix_filter_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/filter_contents.cc
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/filter_contents.h
|
||||
FILE: ../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
|
||||
|
||||
@@ -134,8 +134,6 @@ impeller_component("entity") {
|
||||
"contents/filters/color_filter_contents.h",
|
||||
"contents/filters/color_matrix_filter_contents.cc",
|
||||
"contents/filters/color_matrix_filter_contents.h",
|
||||
"contents/filters/directional_gaussian_blur_filter_contents.cc",
|
||||
"contents/filters/directional_gaussian_blur_filter_contents.h",
|
||||
"contents/filters/filter_contents.cc",
|
||||
"contents/filters/filter_contents.h",
|
||||
"contents/filters/gaussian_blur_filter_contents.cc",
|
||||
@@ -268,7 +266,6 @@ impeller_component("entity_unittests") {
|
||||
sources = [
|
||||
"contents/checkerboard_contents_unittests.cc",
|
||||
"contents/content_context_unittests.cc",
|
||||
"contents/filters/directional_gaussian_blur_filter_contents_unittests.cc",
|
||||
"contents/filters/gaussian_blur_filter_contents_unittests.cc",
|
||||
"contents/filters/inputs/filter_input_unittests.cc",
|
||||
"contents/host_buffer_unittests.cc",
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
// 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/directional_gaussian_blur_filter_contents.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
#include "impeller/base/strings.h"
|
||||
#include "impeller/core/formats.h"
|
||||
#include "impeller/core/sampler_descriptor.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
#include "impeller/geometry/scalar.h"
|
||||
#include "impeller/renderer/command_buffer.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
#include "impeller/renderer/render_target.h"
|
||||
#include "impeller/renderer/vertex_buffer_builder.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
// This function was calculated by observing Skia's behavior. Its blur at 500
|
||||
// seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
|
||||
// that puts the minima there and a f(0)=1.
|
||||
Sigma ScaleSigma(Sigma sigma) {
|
||||
// Limit the kernel size to 1000x1000 pixels, like Skia does.
|
||||
Scalar clamped = std::min(sigma.sigma, 500.0f);
|
||||
Scalar scalar = 1.0 - 3.4e-3 * clamped + 3.4e-06 * clamped * clamped;
|
||||
return Sigma(clamped * scalar);
|
||||
}
|
||||
|
||||
DirectionalGaussianBlurFilterContents::DirectionalGaussianBlurFilterContents() =
|
||||
default;
|
||||
|
||||
DirectionalGaussianBlurFilterContents::
|
||||
~DirectionalGaussianBlurFilterContents() = default;
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetSigma(Sigma sigma) {
|
||||
blur_sigma_ = sigma;
|
||||
}
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetSecondarySigma(Sigma sigma) {
|
||||
secondary_blur_sigma_ = sigma;
|
||||
}
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetDirection(Vector2 direction) {
|
||||
blur_direction_ = direction.Normalize();
|
||||
if (blur_direction_.IsZero()) {
|
||||
blur_direction_ = Vector2(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetBlurStyle(BlurStyle blur_style) {
|
||||
blur_style_ = blur_style;
|
||||
}
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetTileMode(
|
||||
Entity::TileMode tile_mode) {
|
||||
tile_mode_ = tile_mode;
|
||||
}
|
||||
|
||||
void DirectionalGaussianBlurFilterContents::SetIsSecondPass(
|
||||
bool is_second_pass) {
|
||||
is_second_pass_ = is_second_pass;
|
||||
}
|
||||
|
||||
std::optional<Entity> DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
const Matrix& effect_transform,
|
||||
const Rect& coverage,
|
||||
const std::optional<Rect>& coverage_hint) const {
|
||||
using VS = GaussianBlurPipeline::VertexShader;
|
||||
using FS = GaussianBlurPipeline::FragmentShader;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Handle inputs.
|
||||
///
|
||||
|
||||
if (inputs.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto radius = Radius{ScaleSigma(blur_sigma_)}.radius;
|
||||
|
||||
auto transform = entity.GetTransform() * effect_transform.Basis();
|
||||
auto transformed_blur_radius =
|
||||
transform.TransformDirection(blur_direction_ * radius);
|
||||
|
||||
auto transformed_blur_radius_length = transformed_blur_radius.GetLength();
|
||||
|
||||
// Input 0 snapshot.
|
||||
|
||||
std::optional<Rect> expanded_coverage_hint;
|
||||
if (coverage_hint.has_value()) {
|
||||
auto r =
|
||||
Point(transformed_blur_radius_length, transformed_blur_radius_length)
|
||||
.Abs();
|
||||
expanded_coverage_hint =
|
||||
is_second_pass_ ? coverage_hint : coverage_hint->Expand(r);
|
||||
}
|
||||
auto input_snapshot = inputs[0]->GetSnapshot("GaussianBlur", renderer, entity,
|
||||
expanded_coverage_hint);
|
||||
if (!input_snapshot.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (blur_sigma_.sigma < kEhCloseEnough) {
|
||||
return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(),
|
||||
entity.GetClipDepth()); // No blur to render.
|
||||
}
|
||||
|
||||
// If the radius length is < .5, the shader will take at most 1 sample,
|
||||
// resulting in no blur.
|
||||
if (transformed_blur_radius_length < .5) {
|
||||
return Entity::FromSnapshot(input_snapshot.value(), entity.GetBlendMode(),
|
||||
entity.GetClipDepth()); // No blur to render.
|
||||
}
|
||||
|
||||
// A matrix that rotates the snapshot space such that the blur direction is
|
||||
// +X.
|
||||
auto texture_rotate = Matrix::MakeRotationZ(
|
||||
transformed_blur_radius.Normalize().AngleTo({1, 0}));
|
||||
|
||||
// Converts local pass space to screen space. This is just the snapshot space
|
||||
// rotated such that the blur direction is +X.
|
||||
auto pass_transform = texture_rotate * input_snapshot->transform;
|
||||
|
||||
// The pass texture coverage, but rotated such that the blur is in the +X
|
||||
// direction, and expanded to include the blur radius. This is used for UV
|
||||
// projection and as a source for the pass size. Note that it doesn't matter
|
||||
// which direction the space is rotated in when grabbing the pass size.
|
||||
auto pass_texture_rect = Rect::MakeSize(input_snapshot->texture->GetSize())
|
||||
.TransformBounds(pass_transform)
|
||||
.Expand(transformed_blur_radius_length, 0);
|
||||
|
||||
// UV mapping.
|
||||
|
||||
auto pass_uv_project = [&texture_rotate,
|
||||
&pass_texture_rect](Snapshot& input) {
|
||||
auto uv_matrix = Matrix::MakeScale(1 / Vector2(input.texture->GetSize())) *
|
||||
(texture_rotate * input.transform).Invert();
|
||||
return pass_texture_rect.GetTransformedPoints(uv_matrix);
|
||||
};
|
||||
|
||||
auto input_uvs = pass_uv_project(input_snapshot.value());
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Render to texture.
|
||||
///
|
||||
|
||||
ContentContext::SubpassCallback subpass_callback = [&](const ContentContext&
|
||||
renderer,
|
||||
RenderPass& pass) {
|
||||
auto& host_buffer = renderer.GetTransientsBuffer();
|
||||
|
||||
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
|
||||
vtx_builder.AddVertices({
|
||||
{Point(0, 0), input_uvs[0]},
|
||||
{Point(1, 0), input_uvs[1]},
|
||||
{Point(0, 1), input_uvs[2]},
|
||||
{Point(1, 1), input_uvs[3]},
|
||||
});
|
||||
|
||||
VS::FrameInfo frame_info;
|
||||
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
|
||||
frame_info.texture_sampler_y_coord_scale =
|
||||
input_snapshot->texture->GetYCoordScale();
|
||||
|
||||
FS::BlurInfo frag_info;
|
||||
auto r = Radius{transformed_blur_radius_length};
|
||||
frag_info.blur_sigma = Sigma{r}.sigma;
|
||||
frag_info.blur_radius = std::round(r.radius);
|
||||
frag_info.step_size = 2.0;
|
||||
|
||||
// The blur direction is in input UV space.
|
||||
frag_info.blur_uv_offset =
|
||||
pass_transform.Invert().TransformDirection(Vector2(1, 0)).Normalize() /
|
||||
Point(input_snapshot->GetCoverage().value().GetSize());
|
||||
|
||||
#ifdef IMPELLER_DEBUG
|
||||
pass.SetCommandLabel(SPrintF("Gaussian Blur Filter (Radius=%.2f)",
|
||||
transformed_blur_radius_length));
|
||||
#endif // IMPELLER_DEBUG
|
||||
pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
|
||||
|
||||
auto options = OptionsFromPass(pass);
|
||||
options.primitive_type = PrimitiveType::kTriangleStrip;
|
||||
options.blend_mode = BlendMode::kSource;
|
||||
auto input_descriptor = input_snapshot->sampler_descriptor;
|
||||
switch (tile_mode_) {
|
||||
case Entity::TileMode::kDecal:
|
||||
if (renderer.GetDeviceCapabilities()
|
||||
.SupportsDecalSamplerAddressMode()) {
|
||||
input_descriptor.width_address_mode = SamplerAddressMode::kDecal;
|
||||
input_descriptor.height_address_mode = SamplerAddressMode::kDecal;
|
||||
}
|
||||
break;
|
||||
case Entity::TileMode::kClamp:
|
||||
input_descriptor.width_address_mode = SamplerAddressMode::kClampToEdge;
|
||||
input_descriptor.height_address_mode = SamplerAddressMode::kClampToEdge;
|
||||
break;
|
||||
case Entity::TileMode::kMirror:
|
||||
input_descriptor.width_address_mode = SamplerAddressMode::kMirror;
|
||||
input_descriptor.height_address_mode = SamplerAddressMode::kMirror;
|
||||
break;
|
||||
case Entity::TileMode::kRepeat:
|
||||
input_descriptor.width_address_mode = SamplerAddressMode::kRepeat;
|
||||
input_descriptor.height_address_mode = SamplerAddressMode::kRepeat;
|
||||
break;
|
||||
}
|
||||
input_descriptor.mag_filter = MinMagFilter::kLinear;
|
||||
input_descriptor.min_filter = MinMagFilter::kLinear;
|
||||
|
||||
bool has_decal_specialization =
|
||||
tile_mode_ == Entity::TileMode::kDecal &&
|
||||
!renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode();
|
||||
|
||||
if (has_decal_specialization) {
|
||||
pass.SetPipeline(renderer.GetGaussianBlurDecalPipeline(options));
|
||||
} else {
|
||||
pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
|
||||
}
|
||||
|
||||
FS::BindTextureSampler(
|
||||
pass, input_snapshot->texture,
|
||||
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
|
||||
input_descriptor));
|
||||
VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
|
||||
FS::BindBlurInfo(pass, host_buffer.EmplaceUniform(frag_info));
|
||||
|
||||
return pass.Draw().ok();
|
||||
};
|
||||
|
||||
Vector2 scale;
|
||||
auto scale_curve = [](Scalar radius) {
|
||||
constexpr Scalar decay = 4.0; // Larger is more gradual.
|
||||
constexpr Scalar limit = 0.95; // The maximum percentage of the scaledown.
|
||||
const Scalar curve =
|
||||
std::min(1.0, decay / (std::max(1.0f, radius) + decay - 1.0));
|
||||
return (curve - 1) * limit + 1;
|
||||
};
|
||||
{
|
||||
scale.x = scale_curve(transformed_blur_radius_length);
|
||||
|
||||
Scalar y_radius = std::abs(pass_transform.GetDirectionScale(Vector2(
|
||||
0, !is_second_pass_ ? 1 : Radius{secondary_blur_sigma_}.radius)));
|
||||
scale.y = scale_curve(y_radius);
|
||||
}
|
||||
|
||||
Vector2 scaled_size = pass_texture_rect.GetSize() * scale;
|
||||
ISize floored_size = ISize(scaled_size.x, scaled_size.y);
|
||||
|
||||
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
|
||||
"Directional Gaussian Blur Filter", floored_size, subpass_callback);
|
||||
|
||||
if (!render_target.ok()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SamplerDescriptor sampler_desc;
|
||||
sampler_desc.min_filter = MinMagFilter::kLinear;
|
||||
sampler_desc.mag_filter = MinMagFilter::kLinear;
|
||||
sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
|
||||
sampler_desc.width_address_mode = SamplerAddressMode::kClampToEdge;
|
||||
|
||||
return Entity::FromSnapshot(
|
||||
Snapshot{
|
||||
.texture = render_target.value().GetRenderTargetTexture(),
|
||||
.transform =
|
||||
texture_rotate.Invert() *
|
||||
Matrix::MakeTranslation(pass_texture_rect.GetOrigin()) *
|
||||
Matrix::MakeScale((1 / scale) * (scaled_size / floored_size)),
|
||||
.sampler_descriptor = sampler_desc,
|
||||
.opacity = input_snapshot->opacity},
|
||||
entity.GetBlendMode(), entity.GetClipDepth());
|
||||
}
|
||||
|
||||
std::optional<Rect>
|
||||
DirectionalGaussianBlurFilterContents::GetFilterSourceCoverage(
|
||||
const Matrix& effect_transform,
|
||||
const Rect& output_limit) const {
|
||||
auto transform = effect_transform.Basis();
|
||||
auto transformed_blur_vector =
|
||||
transform.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
|
||||
.Abs();
|
||||
return output_limit.Expand(transformed_blur_vector);
|
||||
}
|
||||
|
||||
std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity,
|
||||
const Matrix& effect_transform) const {
|
||||
if (inputs.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto coverage = inputs[0]->GetCoverage(entity);
|
||||
if (!coverage.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto transform = inputs[0]->GetTransform(entity) * effect_transform.Basis();
|
||||
auto transformed_blur_vector =
|
||||
transform
|
||||
.TransformDirection(blur_direction_ *
|
||||
Radius{ScaleSigma(blur_sigma_)}.radius)
|
||||
.Abs();
|
||||
return coverage->Expand(transformed_blur_vector);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@@ -1,112 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_DIRECTIONAL_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
|
||||
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_DIRECTIONAL_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/inputs/filter_input.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
/// A gaussian blur filter that performs the work for one dimension of a
|
||||
/// multi-dimensional Gaussian blur.
|
||||
///
|
||||
/// This filter takes a snapshot of its provided FilterInput, creates a new
|
||||
/// render pass and blurs the contents. Two of these are chained together to
|
||||
/// perform a full 2D blur effect.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// Input Pass 1
|
||||
/// +-------------+ +-----+
|
||||
/// | | | |
|
||||
/// | | | | Pass 2
|
||||
/// | | | | +----+
|
||||
/// | | | | | |
|
||||
/// | | -> | | -> | |
|
||||
/// | | | | | |
|
||||
/// | | | | +----+
|
||||
/// | | | | 87x102
|
||||
/// +-------------+ +-----+
|
||||
/// 586x678 97x678
|
||||
///
|
||||
/// The math for determining how much of the input should be processed for a
|
||||
/// given sigma (aka radius) is found in `Sigma::operator Radius`. The math for
|
||||
/// determining how much to scale down the input based on the radius is inside
|
||||
/// the curve function in this implementation.
|
||||
///
|
||||
/// See also:
|
||||
/// - `FilterContents::MakeGaussianBlur`
|
||||
/// - //flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.glsl
|
||||
///
|
||||
///\deprecated Previously 2 of these were chained to do 2D blurs, use
|
||||
/// \ref GaussianBlurFilterContents instead since it has better
|
||||
/// performance.
|
||||
class DirectionalGaussianBlurFilterContents final : public FilterContents {
|
||||
public:
|
||||
DirectionalGaussianBlurFilterContents();
|
||||
|
||||
~DirectionalGaussianBlurFilterContents() override;
|
||||
|
||||
/// Set sigma (stddev) used for 'direction_'.
|
||||
void SetSigma(Sigma sigma);
|
||||
|
||||
/// Set sigma (stddev) used for direction 90 degrees from 'direction_'.
|
||||
/// Not used if `!is_second_pass_`.
|
||||
void SetSecondarySigma(Sigma sigma);
|
||||
|
||||
void SetDirection(Vector2 direction);
|
||||
|
||||
void SetBlurStyle(BlurStyle blur_style);
|
||||
|
||||
void SetTileMode(Entity::TileMode tile_mode);
|
||||
|
||||
/// Determines if this filter represents the second pass in a chained
|
||||
/// 2D gaussian blur.
|
||||
/// If `is_second_pass_ == true` then the `secondary_sigma_` is used to
|
||||
/// determine the blur radius in the 90 degree rotation of direction_. Its
|
||||
/// output aspect-ratio will closely match the FilterInput snapshot at the
|
||||
/// beginning of the chain.
|
||||
void SetIsSecondPass(bool is_second_pass);
|
||||
|
||||
// |FilterContents|
|
||||
std::optional<Rect> GetFilterSourceCoverage(
|
||||
const Matrix& effect_transform,
|
||||
const Rect& output_limit) const override;
|
||||
|
||||
// |FilterContents|
|
||||
std::optional<Rect> GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity,
|
||||
const Matrix& effect_transform) const override;
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
std::optional<Entity> RenderFilter(
|
||||
const FilterInput::Vector& input_textures,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
const Matrix& effect_transform,
|
||||
const Rect& coverage,
|
||||
const std::optional<Rect>& coverage_hint) const override;
|
||||
Sigma blur_sigma_;
|
||||
Sigma secondary_blur_sigma_;
|
||||
Vector2 blur_direction_;
|
||||
BlurStyle blur_style_ = BlurStyle::kNormal;
|
||||
Entity::TileMode tile_mode_ = Entity::TileMode::kDecal;
|
||||
bool is_second_pass_ = false;
|
||||
|
||||
DirectionalGaussianBlurFilterContents(
|
||||
const DirectionalGaussianBlurFilterContents&) = delete;
|
||||
|
||||
DirectionalGaussianBlurFilterContents& operator=(
|
||||
const DirectionalGaussianBlurFilterContents&) = delete;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_DIRECTIONAL_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
|
||||
@@ -1,226 +0,0 @@
|
||||
// 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 "flutter/testing/testing.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
#include "impeller/entity/entity_playground.h"
|
||||
#include "impeller/geometry/geometry_asserts.h"
|
||||
#include "impeller/renderer/testing/mocks.h"
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
|
||||
using ::testing::Return;
|
||||
|
||||
namespace {
|
||||
|
||||
Scalar CalculateSigmaForBlurRadius(Scalar blur_radius) {
|
||||
// See Sigma.h
|
||||
return (blur_radius / kKernelRadiusPerSigma) + 0.5;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class DirectionalGaussianBlurFilterContentsTest : public EntityPlayground {
|
||||
public:
|
||||
// Stubs in the minimal support to make rendering pass.
|
||||
void SetupMinimalMockContext() {
|
||||
// This mocking code was removed since it wasn't strictly needed yet. If it
|
||||
// is needed you can find it here:
|
||||
// https://gist.github.com/gaaclarke/c2f6bf5fc6ecb10678da03789abc5843.
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_PLAYGROUND_SUITE(DirectionalGaussianBlurFilterContentsTest);
|
||||
|
||||
TEST_P(DirectionalGaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
|
||||
TextureDescriptor desc = {
|
||||
.format = PixelFormat::kB8G8R8A8UNormInt,
|
||||
.size = ISize(100, 100),
|
||||
};
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
std::shared_ptr<Texture> texture =
|
||||
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
|
||||
desc);
|
||||
FilterInput::Vector inputs = {FilterInput::Make(texture)};
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 100, 0}));
|
||||
std::optional<Rect> coverage = contents->GetFilterCoverage(
|
||||
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
|
||||
EXPECT_TRUE(coverage.has_value());
|
||||
if (coverage.has_value()) {
|
||||
EXPECT_NEAR(coverage->GetLeft(), 100 - 2,
|
||||
0.5); // Higher tolerance for sigma scaling.
|
||||
EXPECT_NEAR(coverage->GetTop(), 100, 0.01);
|
||||
EXPECT_NEAR(coverage->GetRight(), 200 + 2,
|
||||
0.5); // Higher tolerance for sigma scaling.
|
||||
EXPECT_NEAR(coverage->GetBottom(), 200, 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DirectionalGaussianBlurFilterContentsTest, FilterSourceCoverage) {
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
|
||||
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
|
||||
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
|
||||
ASSERT_EQ(coverage, Rect::MakeLTRB(100 - 2, 100, 200 + 2, 200));
|
||||
}
|
||||
|
||||
TEST_P(DirectionalGaussianBlurFilterContentsTest, RenderNoCoverage) {
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
Entity entity;
|
||||
Rect coverage_hint = Rect::MakeLTRB(0, 0, 0, 0);
|
||||
std::optional<Entity> result =
|
||||
contents->GetEntity(*renderer, entity, coverage_hint);
|
||||
ASSERT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
TEST_P(DirectionalGaussianBlurFilterContentsTest,
|
||||
RenderCoverageMatchesGetCoverage) {
|
||||
TextureDescriptor desc = {
|
||||
.storage_mode = StorageMode::kDevicePrivate,
|
||||
.format = PixelFormat::kB8G8R8A8UNormInt,
|
||||
.size = ISize(100, 100),
|
||||
};
|
||||
std::shared_ptr<Texture> texture =
|
||||
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
|
||||
desc);
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
contents->SetInputs({FilterInput::Make(texture)});
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
|
||||
Entity entity;
|
||||
std::optional<Entity> result =
|
||||
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
EXPECT_TRUE(result.has_value());
|
||||
if (result.has_value()) {
|
||||
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
|
||||
std::optional<Rect> result_coverage = result.value().GetCoverage();
|
||||
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
|
||||
EXPECT_TRUE(result_coverage.has_value());
|
||||
EXPECT_TRUE(contents_coverage.has_value());
|
||||
if (result_coverage.has_value() && contents_coverage.has_value()) {
|
||||
EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
|
||||
// The precision on this blur is kind of off, thus the tolerance.
|
||||
EXPECT_NEAR(result_coverage.value().GetLeft(), -1.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetTop(), 0.f, 0.001f);
|
||||
EXPECT_NEAR(result_coverage.value().GetRight(), 101.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetBottom(), 100.f, 0.001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DirectionalGaussianBlurFilterContentsTest,
|
||||
TextureContentsWithDestinationRect) {
|
||||
TextureDescriptor desc = {
|
||||
.storage_mode = StorageMode::kDevicePrivate,
|
||||
.format = PixelFormat::kB8G8R8A8UNormInt,
|
||||
.size = ISize(100, 100),
|
||||
};
|
||||
|
||||
std::shared_ptr<Texture> texture =
|
||||
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
|
||||
desc);
|
||||
auto texture_contents = std::make_shared<TextureContents>();
|
||||
texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
|
||||
texture_contents->SetTexture(texture);
|
||||
texture_contents->SetDestinationRect(Rect::MakeXYWH(
|
||||
50, 40, texture->GetSize().width, texture->GetSize().height));
|
||||
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
contents->SetInputs({FilterInput::Make(texture_contents)});
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
|
||||
Entity entity;
|
||||
std::optional<Entity> result =
|
||||
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
EXPECT_TRUE(result.has_value());
|
||||
if (result.has_value()) {
|
||||
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
|
||||
std::optional<Rect> result_coverage = result.value().GetCoverage();
|
||||
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
|
||||
EXPECT_TRUE(result_coverage.has_value());
|
||||
EXPECT_TRUE(contents_coverage.has_value());
|
||||
if (result_coverage.has_value() && contents_coverage.has_value()) {
|
||||
EXPECT_TRUE(RectNear(result_coverage.value(), contents_coverage.value()));
|
||||
// The precision on this blur is kind of off, thus the tolerance.
|
||||
EXPECT_NEAR(result_coverage.value().GetLeft(), 49.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetTop(), 40.f, 0.001f);
|
||||
EXPECT_NEAR(result_coverage.value().GetRight(), 151.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetBottom(), 140.f, 0.001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DirectionalGaussianBlurFilterContentsTest,
|
||||
TextureContentsWithDestinationRectScaled) {
|
||||
TextureDescriptor desc = {
|
||||
.storage_mode = StorageMode::kDevicePrivate,
|
||||
.format = PixelFormat::kB8G8R8A8UNormInt,
|
||||
.size = ISize(100, 100),
|
||||
};
|
||||
|
||||
std::shared_ptr<Texture> texture =
|
||||
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
|
||||
desc);
|
||||
auto texture_contents = std::make_shared<TextureContents>();
|
||||
texture_contents->SetSourceRect(Rect::MakeSize(texture->GetSize()));
|
||||
texture_contents->SetTexture(texture);
|
||||
texture_contents->SetDestinationRect(Rect::MakeXYWH(
|
||||
50, 40, texture->GetSize().width, texture->GetSize().height));
|
||||
|
||||
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
|
||||
auto contents = std::make_unique<DirectionalGaussianBlurFilterContents>();
|
||||
contents->SetSigma(Sigma{sigma_radius_1});
|
||||
contents->SetDirection({1.0, 0.0});
|
||||
contents->SetInputs({FilterInput::Make(texture_contents)});
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeScale({2.0, 2.0, 1.0}));
|
||||
std::optional<Entity> result =
|
||||
contents->GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
EXPECT_TRUE(result.has_value());
|
||||
if (result.has_value()) {
|
||||
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
|
||||
std::optional<Rect> result_coverage = result.value().GetCoverage();
|
||||
std::optional<Rect> contents_coverage = contents->GetCoverage(entity);
|
||||
EXPECT_TRUE(result_coverage.has_value());
|
||||
EXPECT_TRUE(contents_coverage.has_value());
|
||||
if (result_coverage.has_value() && contents_coverage.has_value()) {
|
||||
// The precision on this blur is kind of off, thus the tolerance.
|
||||
|
||||
EXPECT_NEAR(result_coverage.value().GetLeft(), 98.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetTop(), 80.f, 0.001f);
|
||||
EXPECT_NEAR(result_coverage.value().GetRight(), 302.f, 0.1f);
|
||||
EXPECT_NEAR(result_coverage.value().GetBottom(), 280.f, 0.001f);
|
||||
|
||||
EXPECT_NEAR(contents_coverage.value().GetLeft(), 98.f, 0.1f);
|
||||
EXPECT_NEAR(contents_coverage.value().GetTop(), 80.f, 0.001f);
|
||||
EXPECT_NEAR(contents_coverage.value().GetRight(), 302.f, 0.1f);
|
||||
EXPECT_NEAR(contents_coverage.value().GetBottom(), 280.f, 0.001f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "impeller/core/formats.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/filters/border_mask_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/directional_gaussian_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
|
||||
#include "impeller/entity/contents/filters/inputs/filter_input.h"
|
||||
#include "impeller/entity/contents/filters/local_matrix_filter_contents.h"
|
||||
@@ -31,25 +30,6 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
std::shared_ptr<FilterContents> FilterContents::MakeDirectionalGaussianBlur(
|
||||
FilterInput::Ref input,
|
||||
Sigma sigma,
|
||||
Vector2 direction,
|
||||
BlurStyle blur_style,
|
||||
Entity::TileMode tile_mode,
|
||||
bool is_second_pass,
|
||||
Sigma secondary_sigma) {
|
||||
auto blur = std::make_shared<DirectionalGaussianBlurFilterContents>();
|
||||
blur->SetInputs({std::move(input)});
|
||||
blur->SetSigma(sigma);
|
||||
blur->SetDirection(direction);
|
||||
blur->SetBlurStyle(blur_style);
|
||||
blur->SetTileMode(tile_mode);
|
||||
blur->SetIsSecondPass(is_second_pass);
|
||||
blur->SetSecondarySigma(secondary_sigma);
|
||||
return blur;
|
||||
}
|
||||
|
||||
const int32_t FilterContents::kBlurFilterRequiredMipCount =
|
||||
GaussianBlurFilterContents::kBlurFilterRequiredMipCount;
|
||||
|
||||
|
||||
@@ -35,17 +35,6 @@ class FilterContents : public Contents {
|
||||
|
||||
enum class MorphType { kDilate, kErode };
|
||||
|
||||
/// Creates a gaussian blur that operates in one direction.
|
||||
/// See also: `MakeGaussianBlur`
|
||||
static std::shared_ptr<FilterContents> MakeDirectionalGaussianBlur(
|
||||
FilterInput::Ref input,
|
||||
Sigma sigma,
|
||||
Vector2 direction,
|
||||
BlurStyle blur_style = BlurStyle::kNormal,
|
||||
Entity::TileMode tile_mode = Entity::TileMode::kDecal,
|
||||
bool is_second_pass = false,
|
||||
Sigma secondary_sigma = {});
|
||||
|
||||
/// Creates a gaussian blur that operates in 2 dimensions.
|
||||
/// See also: `MakeDirectionalGaussianBlur`
|
||||
static std::shared_ptr<FilterContents> MakeGaussianBlur(
|
||||
|
||||
@@ -994,8 +994,7 @@ TEST_P(EntityTest, GaussianBlurFilter) {
|
||||
auto callback = [&](ContentContext& context, RenderPass& pass) -> bool {
|
||||
const char* input_type_names[] = {"Texture", "Solid Color"};
|
||||
const char* blur_type_names[] = {"Image blur", "Mask blur"};
|
||||
const char* pass_variation_names[] = {"New", "2D Directional",
|
||||
"Directional"};
|
||||
const char* pass_variation_names[] = {"New"};
|
||||
const char* blur_style_names[] = {"Normal", "Solid", "Outer", "Inner"};
|
||||
const char* tile_mode_names[] = {"Clamp", "Repeat", "Mirror", "Decal"};
|
||||
const FilterContents::BlurStyle blur_styles[] = {
|
||||
@@ -1108,13 +1107,6 @@ TEST_P(EntityTest, GaussianBlurFilter) {
|
||||
FilterInput::Make(input), blur_sigma_x, blur_sigma_y,
|
||||
blur_styles[selected_blur_style], tile_modes[selected_tile_mode]);
|
||||
break;
|
||||
case 2: {
|
||||
Vector2 blur_vector(blur_sigma_x.sigma, blur_sigma_y.sigma);
|
||||
blur = FilterContents::MakeDirectionalGaussianBlur(
|
||||
FilterInput::Make(input), Sigma{blur_vector.GetLength()},
|
||||
blur_vector.Normalize());
|
||||
break;
|
||||
}
|
||||
};
|
||||
FML_CHECK(blur);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user