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 = 50;
28constexpr Scalar kMaxSigma = 500.0f;
43 std::initializer_list<typename T::PerVertexData>&& vertices) {
54 if (
renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
78 const std::shared_ptr<CommandBuffer>& command_buffer,
79 std::shared_ptr<Texture> input_texture,
82 const ISize& subpass_size,
93 TextureFillVertexShader::FrameInfo frame_info;
95 frame_info.texture_sampler_y_coord_scale = 1.0;
97 TextureFillFragmentShader::FragInfo frag_info;
98 frag_info.alpha = 1.0;
100 BindVertices<TextureFillVertexShader>(pass, host_buffer,
102 {
Point(0, 0), uvs[0]},
103 {
Point(1, 0), uvs[1]},
104 {
Point(0, 1), uvs[2]},
105 {
Point(1, 1), uvs[3]},
109 SetTileMode(&linear_sampler_descriptor,
renderer, tile_mode);
112 TextureFillVertexShader::BindFrameInfo(
114 TextureFillFragmentShader::BindFragInfo(
116 TextureFillFragmentShader::BindTextureSampler(
118 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
119 linear_sampler_descriptor));
124 "Gaussian Blur Filter", subpass_size, command_buffer, subpass_callback);
125 return render_target;
130 const std::shared_ptr<CommandBuffer>& command_buffer,
135 std::optional<RenderTarget> destination_target,
136 const Quad& blur_uvs) {
145 ISize subpass_size = input_texture->GetSize();
148 GaussianBlurVertexShader::FrameInfo frame_info{
150 .texture_sampler_y_coord_scale = 1.0};
158 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
160 {blur_uvs[0], blur_uvs[0]},
161 {blur_uvs[1], blur_uvs[1]},
162 {blur_uvs[2], blur_uvs[2]},
163 {blur_uvs[3], blur_uvs[3]},
169 GaussianBlurFragmentShader::BindTextureSampler(
171 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
172 linear_sampler_descriptor));
173 GaussianBlurVertexShader::BindFrameInfo(
175 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
177 FML_CHECK(kernel_samples.sample_count < kMaxKernelSize);
178 GaussianBlurFragmentShader::BindKernelSamples(
182 if (destination_target.has_value()) {
183 return renderer.MakeSubpass(
"Gaussian Blur Filter",
184 destination_target.value(), command_buffer,
187 return renderer.MakeSubpass(
"Gaussian Blur Filter", subpass_size,
188 command_buffer, subpass_callback);
201 return static_cast<int>(
std::round(radius * scalar));
206 const std::shared_ptr<FilterInput>& input,
209 const std::shared_ptr<Geometry>& geometry) {
210 auto clip_contents = std::make_shared<ClipContents>();
211 clip_contents->SetClipOperation(clip_operation);
212 clip_contents->SetGeometry(geometry);
215 auto restore = std::make_unique<ClipRestoreContents>();
219 [blur_entity = blur_entity.
Clone(), clipper = std::move(clipper),
220 restore = std::move(restore), entity_transform,
224 clipper.SetClipDepth(entity.GetClipDepth());
225 clipper.SetTransform(entity.GetTransform() * entity_transform);
226 result = clipper.Render(renderer, pass) && result;
227 blur_entity.SetClipDepth(entity.GetClipDepth());
228 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
229 result = blur_entity.Render(renderer, pass) && result;
234 blur_transform](
const Entity& entity)
mutable {
245 const std::shared_ptr<FilterInput>& input,
248 const std::shared_ptr<Geometry>& geometry,
250 switch (blur_style) {
255 input, input_snapshot,
256 std::move(blur_entity), geometry);
260 input, input_snapshot,
261 std::move(blur_entity), geometry);
267 Matrix snapshot_transform =
271 [blur_entity = blur_entity.
Clone(), blurred_transform,
272 source_space_scalar, snapshot_transform,
273 snapshot_entity = std::move(snapshot_entity)](
277 blur_entity.SetClipDepth(entity.GetClipDepth());
278 blur_entity.SetTransform(entity.GetTransform() *
280 result = result && blur_entity.Render(renderer, pass);
281 snapshot_entity.SetTransform(
282 entity.GetTransform() *
283 Matrix::MakeScale(1.f / source_space_scalar) *
285 snapshot_entity.SetClipDepth(entity.GetClipDepth());
286 result = result && snapshot_entity.Render(renderer, pass);
290 blurred_transform](
const Entity& entity)
mutable {
291 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
292 return blur_entity.GetCoverage();
301 "Applying gaussian blur without mipmap.";
308 const std::shared_ptr<Geometry>& mask_geometry)
311 tile_mode_(tile_mode),
312 mask_blur_style_(mask_blur_style),
313 mask_geometry_(mask_geometry) {
325 Scalar raw_result = 4.0 / sigma;
329 exponent =
std::max(-4.0f, exponent);
330 Scalar rounded = powf(2.0f, exponent);
334 if (rounded < 0.125f) {
335 Scalar rounded_plus = powf(2.0f, exponent + 1);
337 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
341 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
342 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
349 const Matrix& effect_transform,
350 const Rect& output_limit)
const {
355 effect_transform.
Basis() *
Vector3{blur_radius.
x, blur_radius.
y, 0.0};
356 return output_limit.
Expand(
Point(blur_radii.
x, blur_radii.
y));
370 const Matrix& effect_transform)
const {
374 std::optional<Rect> input_coverage =
inputs[0]->GetCoverage(entity);
375 if (!input_coverage.has_value()) {
379 const Vector2 source_space_scalar =
384 scaled_sigma.
x =
std::min(scaled_sigma.
x, kMaxSigma);
385 scaled_sigma.
y =
std::min(scaled_sigma.
y, kMaxSigma);
391 return input_coverage.value().Expand(
Point(local_padding.
x, local_padding.
y));
402std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
406 const Matrix& effect_transform,
408 const std::optional<Rect>& coverage_hint)
const {
417 const Vector2 source_space_scalar =
424 scaled_sigma.
x =
std::min(scaled_sigma.x, kMaxSigma);
425 scaled_sigma.y =
std::min(scaled_sigma.y, kMaxSigma);
435 std::optional<Rect> expanded_coverage_hint;
436 if (coverage_hint.has_value()) {
437 expanded_coverage_hint = coverage_hint->Expand(local_padding);
441 if (
renderer.GetContext()->GetBackendType() ==
448 Entity snapshot_entity = entity.
Clone();
450 std::optional<Rect> source_expanded_coverage_hint;
451 if (expanded_coverage_hint.has_value()) {
452 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
457 std::optional<Snapshot> input_snapshot =
459 source_expanded_coverage_hint,
461 if (!input_snapshot.has_value()) {
471 input_snapshot->transform);
476 if (input_snapshot->texture->GetMipCount() <= 1) {
479 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
486 Vector2 downsample_scalar(desired_scalar, desired_scalar);
488 Rect source_rect_padded = source_rect.
Expand(padding);
498 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
502 Vector2(subpass_size) / source_rect_padded.GetSize();
505 input_snapshot->texture->GetSize());
507 std::shared_ptr<CommandBuffer> command_buffer =
508 renderer.GetContext()->CreateCommandBuffer();
509 if (!command_buffer) {
514 renderer, command_buffer, input_snapshot->texture,
515 input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_);
517 if (!pass1_out.
ok()) {
522 1.0 /
Vector2(pass1_out.
value().GetRenderTargetTexture()->GetSize());
524 std::optional<Rect> input_snapshot_coverage = input_snapshot->GetCoverage();
526 FML_DCHECK(input_snapshot.value().transform.IsTranslationScaleOnly());
527 if (source_expanded_coverage_hint.has_value() &&
528 input_snapshot_coverage.has_value()) {
530 std::optional<Rect> uvs =
531 MakeReferenceUVs(input_snapshot_coverage.value(),
532 source_expanded_coverage_hint.value())
535 if (uvs.has_value()) {
536 blur_uvs[0] = uvs->GetLeftTop();
537 blur_uvs[1] = uvs->GetRightTop();
538 blur_uvs[2] = uvs->GetLeftBottom();
539 blur_uvs[3] = uvs->GetRightBottom();
545 input_snapshot->sampler_descriptor, tile_mode_,
547 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
548 .blur_sigma = scaled_sigma.y * effective_scalar.y,
549 .blur_radius = ScaleBlurRadius(blur_radius.y, effective_scalar.y),
552 std::nullopt, blur_uvs);
554 if (!pass2_out.
ok()) {
559 auto pass3_destination = pass2_out.
value().GetRenderTargetTexture() !=
560 pass1_out.
value().GetRenderTargetTexture()
561 ? std::optional<RenderTarget>(pass1_out.
value())
566 input_snapshot->sampler_descriptor, tile_mode_,
568 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
569 .blur_sigma = scaled_sigma.x * effective_scalar.x,
570 .blur_radius = ScaleBlurRadius(blur_radius.x, effective_scalar.x),
573 pass3_destination, blur_uvs);
575 if (!pass3_out.
ok()) {
581 ->Submit({command_buffer})
589 pass2_out.
value().GetRenderTargetSize()) &&
590 (pass2_out.
value().GetRenderTargetSize() ==
591 pass3_out.
value().GetRenderTargetSize()));
593 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
597 Snapshot{.texture = pass3_out.
value().GetRenderTargetTexture(),
600 input_snapshot->transform *
601 padding_snapshot_adjustment *
603 .sampler_descriptor = sampler_desc,
604 .opacity = input_snapshot->opacity},
607 return ApplyBlurStyle(mask_blur_style_, entity,
inputs[0],
608 input_snapshot.value(), std::move(blur_output_entity),
609 mask_geometry_, source_space_scalar);
617 const std::shared_ptr<FilterInput>& filter_input,
619 const Rect& source_rect,
620 const ISize& texture_size) {
621 Matrix input_transform = filter_input->GetLocalTransform(entity);
625 {1.0f / texture_size.
width, 1.0f / texture_size.
height, 1.0f});
626 return uv_transform.
Transform(coverage_quad);
638 Scalar scalar = c +
b * clamped +
a * clamped * clamped;
639 return clamped * scalar;
644 GaussianBlurPipeline::FragmentShader::KernelSamples
result;
657 for (
int i = 0;
i <
result.sample_count; ++
i) {
659 result.samples[
i] = GaussianBlurPipeline::FragmentShader::KernelSample{
661 .coefficient = expf(-0.5f * (
x *
x) /
665 tally +=
result.samples[
i].coefficient;
669 for (
auto& sample :
result.samples) {
670 sample.coefficient /= tally;
679 GaussianBlurPipeline::FragmentShader::KernelSamples parameters) {
680 GaussianBlurPipeline::FragmentShader::KernelSamples
result;
681 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
682 int32_t middle =
result.sample_count / 2;
684 for (
int i = 0;
i <
result.sample_count;
i++) {
686 result.samples[
i] = parameters.samples[j++];
688 GaussianBlurPipeline::FragmentShader::KernelSample
left =
689 parameters.samples[j];
690 GaussianBlurPipeline::FragmentShader::KernelSample
right =
691 parameters.samples[j + 1];
692 result.samples[
i] = GaussianBlurPipeline::FragmentShader::KernelSample{
693 .uv_offset = (
left.uv_offset *
left.coefficient +
696 .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...
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)
static float max(float r, float g, float b)
static float min(float r, float g, float b)
unsigned useCenter Optional< SkMatrix > matrix
sk_sp< SkBlender > blender SkRect rect
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
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...
MinMagFilter
Describes how the texture should be sampled when the texture is being shrunk (minified) or expanded (...
ContentContextOptions OptionsFromPass(const RenderPass &pass)
std::array< Point, 4 > Quad
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
SK_API sk_sp< PrecompileColorFilter > Matrix()
SIN Vec< N, float > ceil(const Vec< N, float > &x)
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.
constexpr Type GetLength() const
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...