9 #include "flutter/fml/make_copyable.h"
13 #include "impeller/entity/texture_fill.frag.h"
14 #include "impeller/entity/texture_fill.vert.h"
30 const int32_t kMaxKernelSize = 48;
45 std::initializer_list<typename T::PerVertexData>&& vertices) {
78 fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
80 std::shared_ptr<Texture> input_texture,
83 const ISize& subpass_size,
94 TextureFillVertexShader::FrameInfo frame_info;
96 frame_info.texture_sampler_y_coord_scale = 1.0;
97 frame_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::BindTextureSampler(
115 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
116 linear_sampler_descriptor));
118 return pass.
Draw().ok();
120 fml::StatusOr<RenderTarget> render_target = renderer.
MakeSubpass(
121 "Gaussian Blur Filter", subpass_size, subpass_callback);
122 return render_target;
125 fml::StatusOr<RenderTarget> MakeBlurSubpass(
131 std::optional<RenderTarget> destination_target,
132 const Quad& blur_uvs) {
141 ISize subpass_size = input_texture->GetSize();
144 GaussianBlurVertexShader::FrameInfo frame_info{
146 .texture_sampler_y_coord_scale = 1.0};
161 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
163 {blur_uvs[0], blur_uvs[0]},
164 {blur_uvs[1], blur_uvs[1]},
165 {blur_uvs[2], blur_uvs[2]},
166 {blur_uvs[3], blur_uvs[3]},
172 GaussianBlurFragmentShader::BindTextureSampler(
174 renderer.
GetContext()->GetSamplerLibrary()->GetSampler(
175 linear_sampler_descriptor));
176 GaussianBlurVertexShader::BindFrameInfo(
178 KernelPipeline::FragmentShader::KernelSamples kernel_samples =
180 FML_CHECK(kernel_samples.sample_count < kMaxKernelSize);
181 GaussianBlurFragmentShader::BindKernelSamples(
183 return pass.
Draw().ok();
185 if (destination_target.has_value()) {
186 return renderer.
MakeSubpass(
"Gaussian Blur Filter",
187 destination_target.value(), subpass_callback);
189 return renderer.
MakeSubpass(
"Gaussian Blur Filter", subpass_size,
196 Rect MakeReferenceUVs(
const Rect& reference,
const Rect& rect) {
203 return static_cast<int>(std::round(radius * scalar));
208 const std::shared_ptr<FilterInput>& input,
211 const std::shared_ptr<Geometry>& geometry) {
212 auto clip_contents = std::make_shared<ClipContents>();
213 clip_contents->SetClipOperation(clip_operation);
214 clip_contents->SetGeometry(geometry);
217 auto restore = std::make_unique<ClipRestoreContents>();
220 auto renderer = fml::MakeCopyable(
221 [blur_entity = blur_entity.
Clone(), clipper = std::move(clipper),
222 restore = std::move(restore), entity_transform,
226 clipper.SetNewClipDepth(entity.GetNewClipDepth());
227 clipper.SetTransform(entity.GetTransform() * entity_transform);
228 result = clipper.Render(renderer, pass) && result;
229 blur_entity.SetNewClipDepth(entity.GetNewClipDepth());
230 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
231 result = blur_entity.Render(renderer, pass) && result;
232 if constexpr (!ContentContext::kEnableStencilThenCover) {
233 result = restore->Render(renderer, entity, pass) && result;
238 fml::MakeCopyable([blur_entity = std::move(blur_entity),
239 blur_transform](
const Entity& entity)
mutable {
249 const Entity& entity,
250 const std::shared_ptr<FilterInput>& input,
251 const Snapshot& input_snapshot,
253 const std::shared_ptr<Geometry>& geometry) {
254 switch (blur_style) {
259 input, input_snapshot,
260 std::move(blur_entity), geometry);
264 input, input_snapshot,
265 std::move(blur_entity), geometry);
268 input_snapshot, entity.GetBlendMode(), entity.GetClipDepth());
270 Matrix blurred_transform = blur_entity.GetTransform();
271 Matrix snapshot_transform = snapshot_entity.GetTransform();
273 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
274 blurred_transform, snapshot_transform,
275 snapshot_entity = std::move(snapshot_entity)](
276 const ContentContext& renderer,
277 const Entity& entity,
278 RenderPass& pass)
mutable {
280 blur_entity.SetNewClipDepth(entity.GetNewClipDepth());
281 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
282 result = result && blur_entity.Render(renderer, pass);
283 snapshot_entity.SetTransform(entity.GetTransform() *
285 snapshot_entity.SetNewClipDepth(entity.GetNewClipDepth());
286 result = result && snapshot_entity.Render(renderer, pass);
289 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
290 blurred_transform](
const Entity& entity)
mutable {
291 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
292 return blur_entity.GetCoverage();
300 std::string_view GaussianBlurFilterContents::kNoMipsError =
301 "Applying gaussian blur without mipmap.";
303 GaussianBlurFilterContents::GaussianBlurFilterContents(
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;
327 Scalar exponent = round(log2f(raw_result));
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));
362 const Matrix& effect_transform)
const {
363 if (inputs.empty()) {
367 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
368 if (!input_coverage.has_value()) {
377 Vector2 padding(ceil(blur_radius.
x), ceil(blur_radius.
y));
379 return input_coverage.value().Expand(
Point(local_padding.
x, local_padding.
y));
382 std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
386 const Matrix& effect_transform,
387 const Rect& coverage,
388 const std::optional<Rect>& coverage_hint)
const {
389 if (inputs.empty()) {
398 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
404 std::optional<Rect> expanded_coverage_hint;
405 if (coverage_hint.has_value()) {
406 expanded_coverage_hint = coverage_hint->Expand(local_padding);
410 if (renderer.
GetContext()->GetBackendType() ==
417 std::optional<Snapshot> input_snapshot =
418 inputs[0]->GetSnapshot(
"GaussianBlur", renderer, entity,
419 expanded_coverage_hint,
421 if (!input_snapshot.has_value()) {
431 if (input_snapshot->texture->GetMipCount() <= 1) {
434 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
441 Vector2 downsample_scalar(desired_scalar, desired_scalar);
443 Rect source_rect_padded = source_rect.
Expand(padding);
453 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
455 ISize(round(downsampled_size.x), round(downsampled_size.y));
457 Vector2(subpass_size) / source_rect_padded.GetSize();
460 input_snapshot->texture->GetSize());
462 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
463 renderer, input_snapshot->texture, input_snapshot->sampler_descriptor,
464 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())
487 FML_DCHECK(uvs.has_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();
496 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
497 renderer, 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);
517 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
518 renderer, 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()) {
534 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
535 pass2_out.value().GetRenderTargetSize()) &&
536 (pass2_out.value().GetRenderTargetSize() ==
537 pass3_out.value().GetRenderTargetSize()));
539 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
543 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
544 .transform = input_snapshot->transform *
545 padding_snapshot_adjustment *
547 .sampler_descriptor = sampler_desc,
548 .opacity = input_snapshot->opacity},
551 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
552 input_snapshot.value(), std::move(blur_output_entity),
561 const std::shared_ptr<FilterInput>& filter_input,
563 const Rect& source_rect,
564 const ISize& texture_size) {
565 Matrix input_transform = filter_input->GetLocalTransform(entity);
569 {1.0f / texture_size.
width, 1.0f / texture_size.
height, 1.0f});
570 return uv_transform.
Transform(coverage_quad);
578 Scalar clamped = std::min(sigma, 500.0f);
579 constexpr
Scalar a = 3.4e-06;
582 Scalar scalar = c +
b * clamped + a * clamped * clamped;
583 return clamped * scalar;
588 KernelPipeline::FragmentShader::KernelSamples result;
589 result.sample_count =
596 result.sample_count -= 2;
601 for (
int i = 0; i < result.sample_count; ++i) {
603 result.samples[i] = KernelPipeline::FragmentShader::KernelSample{
605 .coefficient = expf(-0.5f * (x * x) /
609 tally += result.samples[i].coefficient;
613 for (
auto& sample : result.samples) {
614 sample.coefficient /= tally;
623 KernelPipeline::FragmentShader::KernelSamples parameters) {
624 KernelPipeline::FragmentShader::KernelSamples result;
625 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
626 int32_t middle = result.sample_count / 2;
628 for (
int i = 0; i < result.sample_count; i++) {
630 result.samples[i] = parameters.samples[j++];
632 KernelPipeline::FragmentShader::KernelSample left = parameters.samples[j];
633 KernelPipeline::FragmentShader::KernelSample right =
634 parameters.samples[j + 1];
635 result.samples[i] = KernelPipeline::FragmentShader::KernelSample{
636 .uv_offset = (left.uv_offset * left.coefficient +
637 right.uv_offset * right.coefficient) /
638 (left.coefficient + right.coefficient),
639 .coefficient = left.coefficient + right.coefficient,