9#include "flutter/fml/make_copyable.h"
12#include "impeller/entity/texture_fill.frag.h"
13#include "impeller/entity/texture_fill.vert.h"
27const int32_t kMaxKernelSize = 48;
42 std::initializer_list<typename T::PerVertexData>&& vertices) {
53 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
77 const std::shared_ptr<CommandBuffer>& command_buffer,
78 std::shared_ptr<Texture> input_texture,
81 const ISize& subpass_size,
85 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
90 pass.
SetPipeline(renderer.GetTexturePipeline(pipeline_options));
92 TextureFillVertexShader::FrameInfo frame_info;
94 frame_info.texture_sampler_y_coord_scale = 1.0;
96 TextureFillFragmentShader::FragInfo frag_info;
97 frag_info.alpha = 1.0;
99 BindVertices<TextureFillVertexShader>(pass, host_buffer,
101 {
Point(0, 0), uvs[0]},
102 {
Point(1, 0), uvs[1]},
103 {
Point(0, 1), uvs[2]},
104 {
Point(1, 1), uvs[3]},
108 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
111 TextureFillVertexShader::BindFrameInfo(
113 TextureFillFragmentShader::BindFragInfo(
115 TextureFillFragmentShader::BindTextureSampler(
117 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
118 linear_sampler_descriptor));
123 "Gaussian Blur Filter", subpass_size, command_buffer, subpass_callback);
124 return render_target;
129 const std::shared_ptr<CommandBuffer>& command_buffer,
134 std::optional<RenderTarget> destination_target,
135 const Quad& blur_uvs) {
144 ISize subpass_size = input_texture->GetSize();
147 GaussianBlurVertexShader::FrameInfo frame_info{
149 .texture_sampler_y_coord_scale = 1.0};
151 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
157 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
159 {blur_uvs[0], blur_uvs[0]},
160 {blur_uvs[1], blur_uvs[1]},
161 {blur_uvs[2], blur_uvs[2]},
162 {blur_uvs[3], blur_uvs[3]},
168 GaussianBlurFragmentShader::BindTextureSampler(
170 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
171 linear_sampler_descriptor));
172 GaussianBlurVertexShader::BindFrameInfo(
174 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
176 FML_CHECK(kernel_samples.sample_count < kMaxKernelSize);
177 GaussianBlurFragmentShader::BindKernelSamples(
181 if (destination_target.has_value()) {
182 return renderer.MakeSubpass(
"Gaussian Blur Filter",
183 destination_target.value(), command_buffer,
186 return renderer.MakeSubpass(
"Gaussian Blur Filter", subpass_size,
187 command_buffer, subpass_callback);
193Rect MakeReferenceUVs(
const Rect& reference,
const Rect& rect) {
200 return static_cast<int>(std::round(radius * scalar));
205 const std::shared_ptr<FilterInput>& input,
208 const std::shared_ptr<Geometry>& geometry) {
209 auto clip_contents = std::make_shared<ClipContents>();
210 clip_contents->SetClipOperation(clip_operation);
211 clip_contents->SetGeometry(geometry);
214 auto restore = std::make_unique<ClipRestoreContents>();
218 [blur_entity = blur_entity.
Clone(), clipper = std::move(clipper),
219 restore = std::move(restore), entity_transform,
223 clipper.SetClipDepth(entity.GetClipDepth());
224 clipper.SetTransform(entity.GetTransform() * entity_transform);
225 result = clipper.Render(renderer, pass) && result;
226 blur_entity.SetClipDepth(entity.GetClipDepth());
227 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
228 result = blur_entity.Render(renderer, pass) && result;
233 blur_transform](
const Entity& entity)
mutable {
244 const std::shared_ptr<FilterInput>& input,
247 const std::shared_ptr<Geometry>& geometry) {
248 switch (blur_style) {
253 input, input_snapshot,
254 std::move(blur_entity), geometry);
258 input, input_snapshot,
259 std::move(blur_entity), geometry);
268 blurred_transform, snapshot_transform,
269 snapshot_entity = std::move(snapshot_entity)](
274 blur_entity.SetClipDepth(entity.GetClipDepth());
275 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
276 result = result && blur_entity.Render(renderer, pass);
277 snapshot_entity.SetTransform(entity.GetTransform() *
279 snapshot_entity.SetClipDepth(entity.GetClipDepth());
280 result = result && snapshot_entity.Render(renderer, pass);
284 blurred_transform](
const Entity& entity)
mutable {
285 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
286 return blur_entity.GetCoverage();
295 "Applying gaussian blur without mipmap.";
302 const std::shared_ptr<Geometry>& mask_geometry)
305 tile_mode_(tile_mode),
306 mask_blur_style_(mask_blur_style),
307 mask_geometry_(mask_geometry) {
319 Scalar raw_result = 4.0 / sigma;
323 exponent = std::max(-4.0f, exponent);
324 Scalar rounded = powf(2.0f, exponent);
328 if (rounded < 0.125f) {
329 Scalar rounded_plus = powf(2.0f, exponent + 1);
331 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
335 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
336 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
343 const Matrix& effect_transform,
344 const Rect& output_limit)
const {
349 effect_transform.
Basis() *
Vector3{blur_radius.
x, blur_radius.
y, 0.0};
350 return output_limit.
Expand(
Point(blur_radii.
x, blur_radii.
y));
356 const Matrix& effect_transform)
const {
357 if (inputs.empty()) {
361 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
362 if (!input_coverage.has_value()) {
371 Vector2 padding(ceil(blur_radius.
x), ceil(blur_radius.
y));
373 return input_coverage.value().Expand(
Point(local_padding.
x, local_padding.
y));
380 const Matrix& effect_transform,
381 const Rect& coverage,
382 const std::optional<Rect>& coverage_hint)
const {
383 if (inputs.empty()) {
392 Vector2 padding(ceil(blur_radius.
x), ceil(blur_radius.
y));
398 std::optional<Rect> expanded_coverage_hint;
399 if (coverage_hint.has_value()) {
400 expanded_coverage_hint = coverage_hint->Expand(local_padding);
404 if (renderer.GetContext()->GetBackendType() ==
411 std::optional<Snapshot> input_snapshot =
412 inputs[0]->GetSnapshot(
"GaussianBlur", renderer, entity,
413 expanded_coverage_hint,
415 if (!input_snapshot.has_value()) {
425 if (input_snapshot->texture->GetMipCount() <= 1) {
428 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
435 Vector2 downsample_scalar(desired_scalar, desired_scalar);
437 Rect source_rect_padded = source_rect.
Expand(padding);
447 Vector2 downsampled_size = source_rect_padded.
GetSize() * downsample_scalar;
454 input_snapshot->texture->
GetSize());
456 std::shared_ptr<CommandBuffer> command_buffer =
457 renderer.GetContext()->CreateCommandBuffer();
458 if (!command_buffer) {
463 renderer, command_buffer, input_snapshot->texture,
464 input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_);
466 if (!pass1_out.
ok()) {
471 1.0 /
Vector2(pass1_out.
value().GetRenderTargetTexture()->GetSize());
473 std::optional<Rect> input_snapshot_coverage = input_snapshot->GetCoverage();
475 if (expanded_coverage_hint.has_value() &&
476 input_snapshot_coverage.has_value() &&
481 input_snapshot.has_value() &&
482 input_snapshot.value().transform.IsTranslationScaleOnly()) {
484 std::optional<Rect> uvs = MakeReferenceUVs(input_snapshot_coverage.value(),
485 expanded_coverage_hint.value())
488 if (uvs.has_value()) {
489 blur_uvs[0] = uvs->GetLeftTop();
490 blur_uvs[1] = uvs->GetRightTop();
491 blur_uvs[2] = uvs->GetLeftBottom();
492 blur_uvs[3] = uvs->GetRightBottom();
497 renderer, command_buffer, pass1_out.
value(),
498 input_snapshot->sampler_descriptor, tile_mode_,
500 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
501 .blur_sigma = scaled_sigma.y * effective_scalar.y,
502 .blur_radius = ScaleBlurRadius(blur_radius.y, effective_scalar.y),
505 std::nullopt, blur_uvs);
507 if (!pass2_out.
ok()) {
512 auto pass3_destination = pass2_out.
value().GetRenderTargetTexture() !=
513 pass1_out.
value().GetRenderTargetTexture()
514 ? std::optional<RenderTarget>(pass1_out.
value())
515 : std::optional<RenderTarget>(std::nullopt);
518 renderer, command_buffer, pass2_out.
value(),
519 input_snapshot->sampler_descriptor, tile_mode_,
521 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
522 .blur_sigma = scaled_sigma.x * effective_scalar.x,
523 .blur_radius = ScaleBlurRadius(blur_radius.x, effective_scalar.x),
526 pass3_destination, blur_uvs);
528 if (!pass3_out.
ok()) {
532 if (!renderer.GetContext()
534 ->Submit({command_buffer})
542 pass2_out.
value().GetRenderTargetSize()) &&
543 (pass2_out.
value().GetRenderTargetSize() ==
544 pass3_out.
value().GetRenderTargetSize()));
552 padding_snapshot_adjustment *
554 .sampler_descriptor = sampler_desc,
555 .opacity = input_snapshot->opacity},
558 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
559 input_snapshot.value(), std::move(blur_output_entity),
568 const std::shared_ptr<FilterInput>& filter_input,
570 const Rect& source_rect,
571 const ISize& texture_size) {
572 Matrix input_transform = filter_input->GetLocalTransform(entity);
576 {1.0f / texture_size.
width, 1.0f / texture_size.
height, 1.0f});
577 return uv_transform.
Transform(coverage_quad);
585 Scalar clamped = std::min(sigma, 500.0f);
589 Scalar scalar = c +
b * clamped +
a * clamped * clamped;
590 return clamped * scalar;
595 GaussianBlurPipeline::FragmentShader::KernelSamples
result;
608 for (
int i = 0; i <
result.sample_count; ++i) {
610 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
612 .coefficient = expf(-0.5f * (
x *
x) /
616 tally +=
result.samples[i].coefficient;
620 for (
auto& sample :
result.samples) {
621 sample.coefficient /= tally;
630 GaussianBlurPipeline::FragmentShader::KernelSamples parameters) {
631 GaussianBlurPipeline::FragmentShader::KernelSamples
result;
632 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
633 int32_t middle =
result.sample_count / 2;
635 for (
int i = 0; i <
result.sample_count; i++) {
637 result.samples[i] = parameters.samples[j++];
639 GaussianBlurPipeline::FragmentShader::KernelSample
left =
640 parameters.samples[j];
641 GaussianBlurPipeline::FragmentShader::KernelSample
right =
642 parameters.samples[j + 1];
643 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
644 .uv_offset = (
left.uv_offset *
left.coefficient +
647 .coefficient =
left.coefficient +
right.coefficient,
static void round(SkPoint *p)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
static std::shared_ptr< Contents > MakeAnonymous(RenderProc render_proc, CoverageProc coverage_proc)
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
static Entity FromSnapshot(const Snapshot &snapshot, BlendMode blend_mode=BlendMode::kSourceOver)
Create an entity that can be used to render a given snapshot.
std::optional< Rect > GetCoverage() const
BlendMode GetBlendMode() const
void SetContents(std::shared_ptr< Contents > contents)
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, BlurStyle mask_blur_style, const std::shared_ptr< Geometry > &mask_geometry)
static const int32_t kBlurFilterRequiredMipCount
static Scalar CalculateBlurRadius(Scalar sigma)
static Scalar ScaleSigma(Scalar sigma)
std::optional< Rect > GetFilterSourceCoverage(const Matrix &effect_transform, const Rect &output_limit) const override
Internal utility method for |GetSourceCoverage| that computes the inverse effect of this transform on...
std::optional< Rect > GetFilterCoverage(const FilterInput::Vector &inputs, const Entity &entity, const Matrix &effect_transform) const override
Internal utility method for |GetLocalCoverage| that computes the output coverage of this filter acros...
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
Converts zero or more filter inputs into a render instruction.
static Scalar CalculateScale(Scalar sigma)
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
static std::string_view kNoMipsError
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Render passes encode render commands directed as one specific render target into an underlying comman...
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
virtual void SetPipeline(const std::shared_ptr< Pipeline< PipelineDescriptor > > &pipeline)
The pipeline to use for this command.
virtual fml::Status Draw()
Record the currently pending command.
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
VertexShader_ VertexShader
FragmentShader_ FragmentShader
std::shared_ptr< Texture > GetRenderTargetTexture() const
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
#define FML_DLOG(severity)
#define FML_CHECK(condition)
#define FML_DCHECK(condition)
internal::CopyableLambda< T > MakeCopyable(T lambda)
GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(BlurParameters parameters)
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(GaussianBlurPipeline::FragmentShader::KernelSamples parameters)
constexpr float kEhCloseEnough
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...
ContentContextOptions OptionsFromPass(const RenderPass &pass)
std::array< Point, 4 > Quad
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
A 4x4 matrix using column-major storage.
static constexpr Matrix MakeOrthographic(TSize< T > size)
static constexpr Matrix MakeTranslation(const Vector3 &t)
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
constexpr Quad Transform(const Quad &quad) const
static constexpr Matrix MakeScale(const Vector3 &s)
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
SamplerAddressMode width_address_mode
SamplerAddressMode height_address_mode
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Represents a texture and its intended draw transform/sampler configuration.
std::shared_ptr< Texture > texture
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
static constexpr TRect MakeSize(const TSize< U > &size)
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...