mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Filters: Add local transforms (flutter/engine#140)
This commit is contained in:
parent
04bbef5e1b
commit
63aac1a68c
@ -40,7 +40,7 @@ class Contents {
|
||||
virtual std::optional<Rect> GetCoverage(const Entity& entity) const;
|
||||
|
||||
/// @brief Render this contents to a snapshot, respecting the entity's
|
||||
/// transform, path, stencil depth, blend mode, etc.
|
||||
/// transform, path, stencil depth, and blend mode.
|
||||
/// The result texture size is always the size of
|
||||
/// `GetCoverage(entity)`.
|
||||
virtual std::optional<Snapshot> RenderToSnapshot(
|
||||
|
||||
@ -108,18 +108,21 @@ bool BorderMaskBlurFilterContents::RenderFilter(
|
||||
return pass.AddCommand(std::move(cmd));
|
||||
}
|
||||
|
||||
std::optional<Rect> BorderMaskBlurFilterContents::GetCoverage(
|
||||
std::optional<Rect> BorderMaskBlurFilterContents::GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const {
|
||||
auto coverage = FilterContents::GetCoverage(entity);
|
||||
if (inputs.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto coverage = inputs[0]->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()
|
||||
inputs[0]
|
||||
->GetTransform(entity)
|
||||
.TransformDirection(
|
||||
Vector2(Radius{sigma_x_}.radius, Radius{sigma_y_}.radius))
|
||||
.Abs();
|
||||
|
||||
@ -21,8 +21,9 @@ class BorderMaskBlurFilterContents final : public FilterContents {
|
||||
|
||||
void SetBlurStyle(BlurStyle blur_style);
|
||||
|
||||
// |Contents|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
// |FilterContents|
|
||||
std::optional<Rect> GetFilterCoverage(const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const override;
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
@ -31,6 +32,7 @@ class BorderMaskBlurFilterContents final : public FilterContents {
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Rect& coverage) const override;
|
||||
|
||||
Sigma sigma_x_;
|
||||
Sigma sigma_y_;
|
||||
BlurStyle blur_style_ = BlurStyle::kNormal;
|
||||
|
||||
@ -52,7 +52,8 @@ std::shared_ptr<FilterContents> FilterContents::MakeBlend(
|
||||
new_blend->SetInputs({blend_input, *in_i});
|
||||
new_blend->SetBlendMode(blend_mode);
|
||||
if (in_i < inputs.end() - 1) {
|
||||
blend_input = FilterInput::Make(new_blend);
|
||||
blend_input = FilterInput::Make(
|
||||
std::static_pointer_cast<FilterContents>(new_blend));
|
||||
}
|
||||
}
|
||||
// new_blend will always be assigned because inputs.size() >= 2.
|
||||
@ -139,6 +140,15 @@ bool FilterContents::Render(const ContentContext& renderer,
|
||||
}
|
||||
|
||||
std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
|
||||
Entity entity_with_local_transform = entity;
|
||||
entity_with_local_transform.SetTransformation(
|
||||
GetTransform(entity.GetTransformation()));
|
||||
return GetFilterCoverage(inputs_, entity_with_local_transform);
|
||||
}
|
||||
|
||||
std::optional<Rect> FilterContents::GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const {
|
||||
// The default coverage of FilterContents is just the union of its inputs'
|
||||
// coverage. FilterContents implementations may choose to adjust this
|
||||
// coverage depending on the use case.
|
||||
@ -148,7 +158,7 @@ std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
|
||||
}
|
||||
|
||||
std::optional<Rect> result;
|
||||
for (const auto& input : inputs_) {
|
||||
for (const auto& input : inputs) {
|
||||
auto coverage = input->GetCoverage(entity);
|
||||
if (!coverage.has_value()) {
|
||||
continue;
|
||||
@ -157,7 +167,7 @@ std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
|
||||
result = coverage;
|
||||
continue;
|
||||
}
|
||||
result = result->Union(result.value());
|
||||
result = result->Union(coverage.value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -165,16 +175,21 @@ std::optional<Rect> FilterContents::GetCoverage(const Entity& entity) const {
|
||||
std::optional<Snapshot> FilterContents::RenderToSnapshot(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
auto bounds = GetCoverage(entity);
|
||||
if (!bounds.has_value() || bounds->IsEmpty()) {
|
||||
Entity entity_with_local_transform = entity;
|
||||
entity_with_local_transform.SetTransformation(
|
||||
GetTransform(entity.GetTransformation()));
|
||||
|
||||
auto coverage = GetFilterCoverage(inputs_, entity_with_local_transform);
|
||||
if (!coverage.has_value() || coverage->IsEmpty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Render the filter into a new texture.
|
||||
auto texture = renderer.MakeSubpass(
|
||||
ISize(bounds->size),
|
||||
ISize(coverage->size),
|
||||
[=](const ContentContext& renderer, RenderPass& pass) -> bool {
|
||||
return RenderFilter(inputs_, renderer, entity, pass, bounds.value());
|
||||
return RenderFilter(inputs_, renderer, entity_with_local_transform,
|
||||
pass, coverage.value());
|
||||
});
|
||||
|
||||
if (!texture) {
|
||||
@ -182,7 +197,15 @@ std::optional<Snapshot> FilterContents::RenderToSnapshot(
|
||||
}
|
||||
|
||||
return Snapshot{.texture = texture,
|
||||
.transform = Matrix::MakeTranslation(bounds->origin)};
|
||||
.transform = Matrix::MakeTranslation(coverage->origin)};
|
||||
}
|
||||
|
||||
Matrix FilterContents::GetLocalTransform() const {
|
||||
return Matrix();
|
||||
}
|
||||
|
||||
Matrix FilterContents::GetTransform(const Matrix& parent_transform) const {
|
||||
return parent_transform * GetLocalTransform();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -122,23 +122,31 @@ class FilterContents : public Contents {
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
// |Contents|
|
||||
virtual std::optional<Snapshot> RenderToSnapshot(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const override;
|
||||
std::optional<Snapshot> RenderToSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const override;
|
||||
|
||||
virtual Matrix GetLocalTransform() const;
|
||||
|
||||
Matrix GetTransform(const Matrix& parent_transform) const;
|
||||
|
||||
private:
|
||||
/// @brief Takes a set of zero or more input textures and writes to an output
|
||||
/// texture.
|
||||
virtual std::optional<Rect> GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const;
|
||||
|
||||
/// @brief Takes a set of zero or more input textures and writes to an output
|
||||
/// texture.
|
||||
virtual bool RenderFilter(const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Rect& bounds) const = 0;
|
||||
const Rect& coverage) const = 0;
|
||||
|
||||
FilterInput::Vector inputs_;
|
||||
Rect destination_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(FilterContents);
|
||||
|
||||
friend FilterContentsFilterInput;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -8,14 +8,38 @@
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "fml/logging.h"
|
||||
#include "impeller/entity/contents/filters/filter_contents.h"
|
||||
#include "impeller/entity/contents/snapshot.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
/*******************************************************************************
|
||||
******* FilterInput
|
||||
******************************************************************************/
|
||||
|
||||
FilterInput::Ref FilterInput::Make(Variant input) {
|
||||
return std::shared_ptr<FilterInput>(new FilterInput(input));
|
||||
if (auto filter = std::get_if<std::shared_ptr<FilterContents>>(&input)) {
|
||||
return std::static_pointer_cast<FilterInput>(
|
||||
std::shared_ptr<FilterContentsFilterInput>(
|
||||
new FilterContentsFilterInput(*filter)));
|
||||
}
|
||||
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input)) {
|
||||
return std::static_pointer_cast<FilterInput>(
|
||||
std::shared_ptr<ContentsFilterInput>(
|
||||
new ContentsFilterInput(*contents)));
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input)) {
|
||||
return std::static_pointer_cast<FilterInput>(
|
||||
std::shared_ptr<TextureFilterInput>(new TextureFilterInput(*texture)));
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
|
||||
FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
|
||||
@ -27,63 +51,118 @@ FilterInput::Vector FilterInput::Make(std::initializer_list<Variant> inputs) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FilterInput::Variant FilterInput::GetInput() const {
|
||||
return input_;
|
||||
Matrix FilterInput::GetLocalTransform(const Entity& entity) const {
|
||||
return Matrix();
|
||||
}
|
||||
|
||||
std::optional<Rect> FilterInput::GetCoverage(const Entity& entity) const {
|
||||
if (snapshot_) {
|
||||
return snapshot_->GetCoverage();
|
||||
}
|
||||
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
|
||||
return contents->get()->GetCoverage(entity);
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
|
||||
return entity.GetPathCoverage();
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
Matrix FilterInput::GetTransform(const Entity& entity) const {
|
||||
return entity.GetTransformation() * GetLocalTransform(entity);
|
||||
}
|
||||
|
||||
std::optional<Snapshot> FilterInput::GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
if (snapshot_) {
|
||||
return snapshot_;
|
||||
}
|
||||
snapshot_ = MakeSnapshot(renderer, entity);
|
||||
|
||||
return snapshot_;
|
||||
}
|
||||
|
||||
FilterInput::FilterInput(Variant input) : input_(input) {}
|
||||
|
||||
FilterInput::~FilterInput() = default;
|
||||
|
||||
std::optional<Snapshot> FilterInput::MakeSnapshot(
|
||||
/*******************************************************************************
|
||||
******* FilterContentsFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
FilterContentsFilterInput::FilterContentsFilterInput(
|
||||
std::shared_ptr<FilterContents> filter)
|
||||
: filter_(filter) {}
|
||||
|
||||
FilterContentsFilterInput::~FilterContentsFilterInput() = default;
|
||||
|
||||
FilterInput::Variant FilterContentsFilterInput::GetInput() const {
|
||||
return filter_;
|
||||
}
|
||||
|
||||
std::optional<Snapshot> FilterContentsFilterInput::GetSnapshot(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
if (auto contents = std::get_if<std::shared_ptr<Contents>>(&input_)) {
|
||||
return contents->get()->RenderToSnapshot(renderer, entity);
|
||||
if (!snapshot_.has_value()) {
|
||||
snapshot_ = filter_->RenderToSnapshot(renderer, entity);
|
||||
}
|
||||
return snapshot_;
|
||||
}
|
||||
|
||||
if (auto texture = std::get_if<std::shared_ptr<Texture>>(&input_)) {
|
||||
// Rendered textures stretch to fit the entity path coverage, so we
|
||||
// incorporate this behavior by translating and scaling the snapshot
|
||||
// transform.
|
||||
auto path_bounds = entity.GetPath().GetBoundingBox();
|
||||
if (!path_bounds.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto transform = entity.GetTransformation() *
|
||||
Matrix::MakeTranslation(path_bounds->origin) *
|
||||
Matrix::MakeScale(Vector2(path_bounds->size) /
|
||||
texture->get()->GetSize());
|
||||
return Snapshot{.texture = *texture, .transform = transform};
|
||||
std::optional<Rect> FilterContentsFilterInput::GetCoverage(
|
||||
const Entity& entity) const {
|
||||
return filter_->GetCoverage(entity);
|
||||
}
|
||||
|
||||
Matrix FilterContentsFilterInput::GetLocalTransform(
|
||||
const Entity& entity) const {
|
||||
return filter_->GetLocalTransform();
|
||||
}
|
||||
|
||||
Matrix FilterContentsFilterInput::GetTransform(const Entity& entity) const {
|
||||
return filter_->GetTransform(entity.GetTransformation());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
******* ContentsFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
ContentsFilterInput::ContentsFilterInput(std::shared_ptr<Contents> contents)
|
||||
: contents_(contents) {}
|
||||
|
||||
ContentsFilterInput::~ContentsFilterInput() = default;
|
||||
|
||||
FilterInput::Variant ContentsFilterInput::GetInput() const {
|
||||
return contents_;
|
||||
}
|
||||
|
||||
std::optional<Snapshot> ContentsFilterInput::GetSnapshot(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
if (!snapshot_.has_value()) {
|
||||
snapshot_ = contents_->RenderToSnapshot(renderer, entity);
|
||||
}
|
||||
return snapshot_;
|
||||
}
|
||||
|
||||
FML_UNREACHABLE();
|
||||
std::optional<Rect> ContentsFilterInput::GetCoverage(
|
||||
const Entity& entity) const {
|
||||
return contents_->GetCoverage(entity);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
******* TextureFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
TextureFilterInput::TextureFilterInput(std::shared_ptr<Texture> texture)
|
||||
: texture_(texture) {}
|
||||
|
||||
TextureFilterInput::~TextureFilterInput() = default;
|
||||
|
||||
FilterInput::Variant TextureFilterInput::GetInput() const {
|
||||
return texture_;
|
||||
}
|
||||
|
||||
std::optional<Snapshot> TextureFilterInput::GetSnapshot(
|
||||
const ContentContext& renderer,
|
||||
const Entity& entity) const {
|
||||
return Snapshot{.texture = texture_, .transform = GetTransform(entity)};
|
||||
}
|
||||
|
||||
std::optional<Rect> TextureFilterInput::GetCoverage(
|
||||
const Entity& entity) const {
|
||||
auto path_bounds = entity.GetPath().GetBoundingBox();
|
||||
if (!path_bounds.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return Rect::MakeSize(Size(texture_->GetSize()))
|
||||
.TransformBounds(GetTransform(entity));
|
||||
}
|
||||
|
||||
Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const {
|
||||
// Compute the local transform such that the texture will cover the entity
|
||||
// path bounding box.
|
||||
auto path_bounds = entity.GetPath().GetBoundingBox();
|
||||
if (!path_bounds.has_value()) {
|
||||
return Matrix();
|
||||
}
|
||||
return Matrix::MakeTranslation(path_bounds->origin) *
|
||||
Matrix::MakeScale(Vector2(path_bounds->size) / texture_->GetSize());
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -17,46 +17,137 @@ namespace impeller {
|
||||
|
||||
class ContentContext;
|
||||
class Entity;
|
||||
class FilterContents;
|
||||
|
||||
/*******************************************************************************
|
||||
******* FilterInput
|
||||
******************************************************************************/
|
||||
|
||||
/// `FilterInput` is a lazy/single eval `Snapshot` which may be shared across
|
||||
/// filter parameters and used to evaluate input bounds.
|
||||
/// filter parameters and used to evaluate input coverage.
|
||||
///
|
||||
/// A `FilterInput` can be created from either a `Texture` or any `Contents`
|
||||
/// class (including `FilterContents`), and can be re-used for any filter inputs
|
||||
/// across an entity's filter graph without repeating subpasses unnecessarily.
|
||||
/// A `FilterInput` can be re-used for any filter inputs across an entity's
|
||||
/// filter graph without repeating subpasses unnecessarily.
|
||||
///
|
||||
/// Filters may decide to not evaluate inputs in situations where they won't
|
||||
/// contribute to the filter's output texture.
|
||||
class FilterInput final {
|
||||
class FilterInput {
|
||||
public:
|
||||
using Ref = std::shared_ptr<FilterInput>;
|
||||
using Vector = std::vector<FilterInput::Ref>;
|
||||
using Variant =
|
||||
std::variant<std::shared_ptr<Texture>, std::shared_ptr<Contents>>;
|
||||
using Variant = std::variant<std::shared_ptr<FilterContents>,
|
||||
std::shared_ptr<Contents>,
|
||||
std::shared_ptr<Texture>>;
|
||||
|
||||
~FilterInput();
|
||||
virtual ~FilterInput();
|
||||
|
||||
static FilterInput::Ref Make(Variant input);
|
||||
|
||||
static FilterInput::Vector Make(std::initializer_list<Variant> inputs);
|
||||
|
||||
Variant GetInput() const;
|
||||
virtual Variant GetInput() const = 0;
|
||||
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const;
|
||||
virtual std::optional<Snapshot> GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const = 0;
|
||||
|
||||
virtual std::optional<Rect> GetCoverage(const Entity& entity) const = 0;
|
||||
|
||||
/// @brief Get the local transform of this filter input. This transform is
|
||||
/// relative to the `Entity` transform space.
|
||||
virtual Matrix GetLocalTransform(const Entity& entity) const;
|
||||
|
||||
/// @brief Get the transform of this `FilterInput`. This is equivalent to
|
||||
/// calling `entity.GetTransformation() * GetLocalTransform()`.
|
||||
virtual Matrix GetTransform(const Entity& entity) const;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
******* FilterContentsFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
class FilterContentsFilterInput final : public FilterInput {
|
||||
public:
|
||||
~FilterContentsFilterInput() override;
|
||||
|
||||
// |FilterInput|
|
||||
Variant GetInput() const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Snapshot> GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const;
|
||||
const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
Matrix GetLocalTransform(const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
Matrix GetTransform(const Entity& entity) const override;
|
||||
|
||||
private:
|
||||
FilterInput(Variant input);
|
||||
FilterContentsFilterInput(std::shared_ptr<FilterContents> filter);
|
||||
|
||||
std::optional<Snapshot> MakeSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const;
|
||||
|
||||
std::optional<Snapshot> MakeSnapshotForTexture(const Entity& entity) const;
|
||||
|
||||
Variant input_;
|
||||
std::shared_ptr<FilterContents> filter_;
|
||||
mutable std::optional<Snapshot> snapshot_;
|
||||
|
||||
friend FilterInput;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
******* ContentsFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
class ContentsFilterInput final : public FilterInput {
|
||||
public:
|
||||
~ContentsFilterInput() override;
|
||||
|
||||
// |FilterInput|
|
||||
Variant GetInput() const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Snapshot> GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
private:
|
||||
ContentsFilterInput(std::shared_ptr<Contents> contents);
|
||||
|
||||
std::shared_ptr<Contents> contents_;
|
||||
mutable std::optional<Snapshot> snapshot_;
|
||||
|
||||
friend FilterInput;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
******* TextureFilterInput
|
||||
******************************************************************************/
|
||||
|
||||
class TextureFilterInput final : public FilterInput {
|
||||
public:
|
||||
~TextureFilterInput() override;
|
||||
|
||||
// |FilterInput|
|
||||
Variant GetInput() const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Snapshot> GetSnapshot(const ContentContext& renderer,
|
||||
const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
// |FilterInput|
|
||||
Matrix GetLocalTransform(const Entity& entity) const override;
|
||||
|
||||
private:
|
||||
TextureFilterInput(std::shared_ptr<Texture> texture);
|
||||
|
||||
std::shared_ptr<Texture> texture_;
|
||||
|
||||
friend FilterInput;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -163,17 +163,22 @@ bool DirectionalGaussianBlurFilterContents::RenderFilter(
|
||||
return pass.AddCommand(cmd);
|
||||
}
|
||||
|
||||
std::optional<Rect> DirectionalGaussianBlurFilterContents::GetCoverage(
|
||||
std::optional<Rect> DirectionalGaussianBlurFilterContents::GetFilterCoverage(
|
||||
const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const {
|
||||
auto coverage = FilterContents::GetCoverage(entity);
|
||||
if (inputs.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto coverage = inputs[0]->GetCoverage(entity);
|
||||
if (!coverage.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto transformed_blur_vector =
|
||||
entity.GetTransformation()
|
||||
.TransformDirection(blur_direction_ *
|
||||
ceil(Radius{blur_sigma_}.radius))
|
||||
inputs[0]
|
||||
->GetTransform(entity)
|
||||
.TransformDirection(blur_direction_ * Radius{blur_sigma_}.radius)
|
||||
.Abs();
|
||||
auto extent = coverage->size + transformed_blur_vector * 2;
|
||||
return Rect(coverage->origin - transformed_blur_vector,
|
||||
|
||||
@ -25,8 +25,9 @@ class DirectionalGaussianBlurFilterContents final : public FilterContents {
|
||||
|
||||
void SetSourceOverride(FilterInput::Ref alpha_mask);
|
||||
|
||||
// |Contents|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
// |FilterContents|
|
||||
std::optional<Rect> GetFilterCoverage(const FilterInput::Vector& inputs,
|
||||
const Entity& entity) const override;
|
||||
|
||||
private:
|
||||
// |FilterContents|
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user