Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
gaussian_blur_filter_contents.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cmath>
8
9#include "flutter/fml/make_copyable.h"
12#include "impeller/entity/texture_fill.frag.h"
13#include "impeller/entity/texture_fill.vert.h"
16
17namespace impeller {
18
21
23
24namespace {
25
26// 48 comes from gaussian.frag.
27const int32_t kMaxKernelSize = 48;
28
29SamplerDescriptor MakeSamplerDescriptor(MinMagFilter filter,
30 SamplerAddressMode address_mode) {
31 SamplerDescriptor sampler_desc;
32 sampler_desc.min_filter = filter;
33 sampler_desc.mag_filter = filter;
34 sampler_desc.width_address_mode = address_mode;
35 sampler_desc.height_address_mode = address_mode;
36 return sampler_desc;
37}
38
39template <typename T>
40void BindVertices(RenderPass& pass,
41 HostBuffer& host_buffer,
42 std::initializer_list<typename T::PerVertexData>&& vertices) {
44 vtx_builder.AddVertices(vertices);
45 pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
46}
47
48void SetTileMode(SamplerDescriptor* descriptor,
49 const ContentContext& renderer,
50 Entity::TileMode tile_mode) {
51 switch (tile_mode) {
53 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
56 }
57 break;
61 break;
65 break;
69 break;
70 }
71}
72
73/// Makes a subpass that will render the scaled down input and add the
74/// transparent gutter required for the blur halo.
75fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
76 const ContentContext& renderer,
77 const std::shared_ptr<CommandBuffer>& command_buffer,
78 std::shared_ptr<Texture> input_texture,
79 const SamplerDescriptor& sampler_descriptor,
80 const Quad& uvs,
81 const ISize& subpass_size,
82 Entity::TileMode tile_mode) {
83 ContentContext::SubpassCallback subpass_callback =
84 [&](const ContentContext& renderer, RenderPass& pass) {
85 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
86
87 pass.SetCommandLabel("Gaussian blur downsample");
88 auto pipeline_options = OptionsFromPass(pass);
89 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
90 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
91
92 TextureFillVertexShader::FrameInfo frame_info;
93 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
94 frame_info.texture_sampler_y_coord_scale = 1.0;
95
96 TextureFillFragmentShader::FragInfo frag_info;
97 frag_info.alpha = 1.0;
98
99 BindVertices<TextureFillVertexShader>(pass, host_buffer,
100 {
101 {Point(0, 0), uvs[0]},
102 {Point(1, 0), uvs[1]},
103 {Point(0, 1), uvs[2]},
104 {Point(1, 1), uvs[3]},
105 });
106
107 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
108 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
109 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
110 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
111 TextureFillVertexShader::BindFrameInfo(
112 pass, host_buffer.EmplaceUniform(frame_info));
113 TextureFillFragmentShader::BindFragInfo(
114 pass, host_buffer.EmplaceUniform(frag_info));
115 TextureFillFragmentShader::BindTextureSampler(
116 pass, input_texture,
117 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
118 linear_sampler_descriptor));
119
120 return pass.Draw().ok();
121 };
122 fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
123 "Gaussian Blur Filter", subpass_size, command_buffer, subpass_callback);
124 return render_target;
125}
126
127fml::StatusOr<RenderTarget> MakeBlurSubpass(
128 const ContentContext& renderer,
129 const std::shared_ptr<CommandBuffer>& command_buffer,
130 const RenderTarget& input_pass,
131 const SamplerDescriptor& sampler_descriptor,
132 Entity::TileMode tile_mode,
133 const BlurParameters& blur_info,
134 std::optional<RenderTarget> destination_target,
135 const Quad& blur_uvs) {
136 if (blur_info.blur_sigma < kEhCloseEnough) {
137 return input_pass;
138 }
139
140 std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture();
141
142 // TODO(gaaclarke): This blurs the whole image, but because we know the clip
143 // region we could focus on just blurring that.
144 ISize subpass_size = input_texture->GetSize();
145 ContentContext::SubpassCallback subpass_callback =
146 [&](const ContentContext& renderer, RenderPass& pass) {
147 GaussianBlurVertexShader::FrameInfo frame_info{
148 .mvp = Matrix::MakeOrthographic(ISize(1, 1)),
149 .texture_sampler_y_coord_scale = 1.0};
150
151 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
152
154 options.primitive_type = PrimitiveType::kTriangleStrip;
155 pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
156
157 BindVertices<GaussianBlurVertexShader>(pass, host_buffer,
158 {
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]},
163 });
164
165 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
166 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
167 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
168 GaussianBlurFragmentShader::BindTextureSampler(
169 pass, input_texture,
170 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
171 linear_sampler_descriptor));
172 GaussianBlurVertexShader::BindFrameInfo(
173 pass, host_buffer.EmplaceUniform(frame_info));
174 GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples =
176 FML_CHECK(kernel_samples.sample_count < kMaxKernelSize);
177 GaussianBlurFragmentShader::BindKernelSamples(
178 pass, host_buffer.EmplaceUniform(kernel_samples));
179 return pass.Draw().ok();
180 };
181 if (destination_target.has_value()) {
182 return renderer.MakeSubpass("Gaussian Blur Filter",
183 destination_target.value(), command_buffer,
184 subpass_callback);
185 } else {
186 return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
187 command_buffer, subpass_callback);
188 }
189}
190
191/// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
192/// be returned when `rect` == `reference`.
193Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
194 Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
195 rect.GetSize());
196 return result.Scale(1.0f / Vector2(reference.GetSize()));
197}
198
199int ScaleBlurRadius(Scalar radius, Scalar scalar) {
200 return static_cast<int>(std::round(radius * scalar));
201}
202
203Entity ApplyClippedBlurStyle(Entity::ClipOperation clip_operation,
204 const Entity& entity,
205 const std::shared_ptr<FilterInput>& input,
206 const Snapshot& input_snapshot,
207 Entity blur_entity,
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);
212 Entity clipper;
213 clipper.SetContents(clip_contents);
214 auto restore = std::make_unique<ClipRestoreContents>();
215 Matrix entity_transform = entity.GetTransform();
216 Matrix blur_transform = blur_entity.GetTransform();
217 auto renderer = fml::MakeCopyable(
218 [blur_entity = blur_entity.Clone(), clipper = std::move(clipper),
219 restore = std::move(restore), entity_transform,
220 blur_transform](const ContentContext& renderer, const Entity& entity,
221 RenderPass& pass) mutable {
222 bool result = true;
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;
229 return result;
230 });
231 auto coverage =
232 fml::MakeCopyable([blur_entity = std::move(blur_entity),
233 blur_transform](const Entity& entity) mutable {
234 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
235 return blur_entity.GetCoverage();
236 });
238 result.SetContents(Contents::MakeAnonymous(renderer, coverage));
239 return result;
240}
241
242Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
243 const Entity& entity,
244 const std::shared_ptr<FilterInput>& input,
245 const Snapshot& input_snapshot,
246 Entity blur_entity,
247 const std::shared_ptr<Geometry>& geometry) {
248 switch (blur_style) {
250 return blur_entity;
252 return ApplyClippedBlurStyle(Entity::ClipOperation::kIntersect, entity,
253 input, input_snapshot,
254 std::move(blur_entity), geometry);
255 break;
257 return ApplyClippedBlurStyle(Entity::ClipOperation::kDifference, entity,
258 input, input_snapshot,
259 std::move(blur_entity), geometry);
261 Entity snapshot_entity =
262 Entity::FromSnapshot(input_snapshot, entity.GetBlendMode());
264 Matrix blurred_transform = blur_entity.GetTransform();
265 Matrix snapshot_transform = snapshot_entity.GetTransform();
267 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
268 blurred_transform, snapshot_transform,
269 snapshot_entity = std::move(snapshot_entity)](
270 const ContentContext& renderer,
271 const Entity& entity,
272 RenderPass& pass) mutable {
273 bool result = true;
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() *
278 snapshot_transform);
279 snapshot_entity.SetClipDepth(entity.GetClipDepth());
280 result = result && snapshot_entity.Render(renderer, pass);
281 return result;
282 }),
283 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
284 blurred_transform](const Entity& entity) mutable {
285 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
286 return blur_entity.GetCoverage();
287 })));
288 return result;
289 }
290 }
291}
292} // namespace
293
295 "Applying gaussian blur without mipmap.";
296
298 Scalar sigma_x,
299 Scalar sigma_y,
300 Entity::TileMode tile_mode,
301 BlurStyle mask_blur_style,
302 const std::shared_ptr<Geometry>& mask_geometry)
303 : sigma_x_(sigma_x),
304 sigma_y_(sigma_y),
305 tile_mode_(tile_mode),
306 mask_blur_style_(mask_blur_style),
307 mask_geometry_(mask_geometry) {
308 // This is supposed to be enforced at a higher level.
309 FML_DCHECK(mask_blur_style == BlurStyle::kNormal || mask_geometry);
310}
311
312// This value was extracted from Skia, see:
313// * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576
314// * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/BlurUtils.h#L57
316 if (sigma <= 4) {
317 return 1.0;
318 }
319 Scalar raw_result = 4.0 / sigma;
320 // Round to the nearest 1/(2^n) to get the best quality down scaling.
321 Scalar exponent = round(log2f(raw_result));
322 // Don't scale down below 1/16th to preserve signal.
323 exponent = std::max(-4.0f, exponent);
324 Scalar rounded = powf(2.0f, exponent);
325 Scalar result = rounded;
326 // Extend the range of the 1/8th downsample based on the effective kernel size
327 // for the blur.
328 if (rounded < 0.125f) {
329 Scalar rounded_plus = powf(2.0f, exponent + 1);
330 Scalar blur_radius = CalculateBlurRadius(sigma);
331 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
332 // This constant was picked by looking at the results to make sure no
333 // shimmering was introduced at the highest sigma values that downscale to
334 // 1/16th.
335 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
336 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
337 : rounded;
338 }
339 return result;
340};
341
343 const Matrix& effect_transform,
344 const Rect& output_limit) const {
345 Vector2 scaled_sigma = {ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)};
346 Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
347 CalculateBlurRadius(scaled_sigma.y)};
348 Vector3 blur_radii =
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));
351}
352
354 const FilterInput::Vector& inputs,
355 const Entity& entity,
356 const Matrix& effect_transform) const {
357 if (inputs.empty()) {
358 return {};
359 }
360
361 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
362 if (!input_coverage.has_value()) {
363 return {};
364 }
365
366 Vector2 scaled_sigma = (effect_transform.Basis() *
367 Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
368 .Abs();
369 Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
370 CalculateBlurRadius(scaled_sigma.y));
371 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
372 Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
373 return input_coverage.value().Expand(Point(local_padding.x, local_padding.y));
374}
375
377 const FilterInput::Vector& inputs,
378 const ContentContext& renderer,
379 const Entity& entity,
380 const Matrix& effect_transform,
381 const Rect& coverage,
382 const std::optional<Rect>& coverage_hint) const {
383 if (inputs.empty()) {
384 return std::nullopt;
385 }
386
387 Vector2 scaled_sigma = (effect_transform.Basis() *
388 Vector2(ScaleSigma(sigma_x_), ScaleSigma(sigma_y_)))
389 .Abs();
390 Vector2 blur_radius = Vector2(CalculateBlurRadius(scaled_sigma.x),
391 CalculateBlurRadius(scaled_sigma.y));
392 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
393 Vector2 local_padding = (entity.GetTransform().Basis() * padding).Abs();
394
395 // Apply as much of the desired padding as possible from the source. This may
396 // be ignored so must be accounted for in the downsample pass by adding a
397 // transparent gutter.
398 std::optional<Rect> expanded_coverage_hint;
399 if (coverage_hint.has_value()) {
400 expanded_coverage_hint = coverage_hint->Expand(local_padding);
401 }
402
403 int32_t mip_count = kBlurFilterRequiredMipCount;
404 if (renderer.GetContext()->GetBackendType() ==
406 // TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
407 // generation on opengles.
408 mip_count = 1;
409 }
410
411 std::optional<Snapshot> input_snapshot =
412 inputs[0]->GetSnapshot("GaussianBlur", renderer, entity,
413 /*coverage_limit=*/expanded_coverage_hint,
414 /*mip_count=*/mip_count);
415 if (!input_snapshot.has_value()) {
416 return std::nullopt;
417 }
418
419 if (scaled_sigma.x < kEhCloseEnough && scaled_sigma.y < kEhCloseEnough) {
420 return Entity::FromSnapshot(input_snapshot.value(),
421 entity.GetBlendMode()); // No blur to render.
422 }
423
424 // In order to avoid shimmering in downsampling step, we should have mips.
425 if (input_snapshot->texture->GetMipCount() <= 1) {
427 }
428 FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
429
430 Scalar desired_scalar =
431 std::min(CalculateScale(scaled_sigma.x), CalculateScale(scaled_sigma.y));
432 // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the
433 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
434 // pass.
435 Vector2 downsample_scalar(desired_scalar, desired_scalar);
436 Rect source_rect = Rect::MakeSize(input_snapshot->texture->GetSize());
437 Rect source_rect_padded = source_rect.Expand(padding);
438 Matrix padding_snapshot_adjustment = Matrix::MakeTranslation(-padding);
439 // TODO(gaaclarke): The padding could be removed if we know it's not needed or
440 // resized to account for the expanded_clip_coverage. There doesn't appear
441 // to be the math to make those calculations though. The following
442 // optimization works, but causes a shimmer as a result of
443 // https://github.com/flutter/flutter/issues/140193 so it isn't applied.
444 //
445 // !input_snapshot->GetCoverage()->Expand(-local_padding)
446 // .Contains(coverage_hint.value()))
447 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
448 ISize subpass_size =
449 ISize(round(downsampled_size.x), round(downsampled_size.y));
450 Vector2 effective_scalar =
451 Vector2(subpass_size) / source_rect_padded.GetSize();
452
453 Quad uvs = CalculateUVs(inputs[0], entity, source_rect_padded,
454 input_snapshot->texture->GetSize());
455
456 std::shared_ptr<CommandBuffer> command_buffer =
457 renderer.GetContext()->CreateCommandBuffer();
458 if (!command_buffer) {
459 return std::nullopt;
460 }
461
462 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
463 renderer, command_buffer, input_snapshot->texture,
464 input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_);
465
466 if (!pass1_out.ok()) {
467 return std::nullopt;
468 }
469
470 Vector2 pass1_pixel_size =
471 1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
472
473 std::optional<Rect> input_snapshot_coverage = input_snapshot->GetCoverage();
474 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
475 if (expanded_coverage_hint.has_value() &&
476 input_snapshot_coverage.has_value() &&
477 // TODO(https://github.com/flutter/flutter/issues/140890): Remove this
478 // condition. There is some flaw in coverage stopping us from using this
479 // today. I attempted to use source coordinates to calculate the uvs,
480 // but that didn't work either.
481 input_snapshot.has_value() &&
482 input_snapshot.value().transform.IsTranslationScaleOnly()) {
483 // Only process the uvs where the blur is happening, not the whole texture.
484 std::optional<Rect> uvs = MakeReferenceUVs(input_snapshot_coverage.value(),
485 expanded_coverage_hint.value())
486 .Intersection(Rect::MakeSize(Size(1, 1)));
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();
493 }
494 }
495
496 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
497 renderer, command_buffer, /*input_pass=*/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),
503 .step_size = 1,
504 },
505 /*destination_target=*/std::nullopt, blur_uvs);
506
507 if (!pass2_out.ok()) {
508 return std::nullopt;
509 }
510
511 // Only ping pong if the first pass actually created a render target.
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);
516
517 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
518 renderer, command_buffer, /*input_pass=*/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),
524 .step_size = 1,
525 },
526 pass3_destination, blur_uvs);
527
528 if (!pass3_out.ok()) {
529 return std::nullopt;
530 }
531
532 if (!renderer.GetContext()
533 ->GetCommandQueue()
534 ->Submit(/*buffers=*/{command_buffer})
535 .ok()) {
536 return std::nullopt;
537 }
538
539 // The ping-pong approach requires that each render pass output has the same
540 // size.
541 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
542 pass2_out.value().GetRenderTargetSize()) &&
543 (pass2_out.value().GetRenderTargetSize() ==
544 pass3_out.value().GetRenderTargetSize()));
545
546 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
548
549 Entity blur_output_entity = Entity::FromSnapshot(
550 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
551 .transform = input_snapshot->transform *
552 padding_snapshot_adjustment *
553 Matrix::MakeScale(1 / effective_scalar),
554 .sampler_descriptor = sampler_desc,
555 .opacity = input_snapshot->opacity},
556 entity.GetBlendMode());
557
558 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
559 input_snapshot.value(), std::move(blur_output_entity),
560 mask_geometry_);
561}
562
564 return static_cast<Radius>(Sigma(sigma)).radius;
565}
566
568 const std::shared_ptr<FilterInput>& filter_input,
569 const Entity& entity,
570 const Rect& source_rect,
571 const ISize& texture_size) {
572 Matrix input_transform = filter_input->GetLocalTransform(entity);
573 Quad coverage_quad = source_rect.GetTransformedPoints(input_transform);
574
575 Matrix uv_transform = Matrix::MakeScale(
576 {1.0f / texture_size.width, 1.0f / texture_size.height, 1.0f});
577 return uv_transform.Transform(coverage_quad);
578}
579
580// This function was calculated by observing Skia's behavior. Its blur at 500
581// seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
582// that puts the minima there and a f(0)=1.
584 // Limit the kernel size to 1000x1000 pixels, like Skia does.
585 Scalar clamped = std::min(sigma, 500.0f);
586 constexpr Scalar a = 3.4e-06;
587 constexpr Scalar b = -3.4e-3;
588 constexpr Scalar c = 1.f;
589 Scalar scalar = c + b * clamped + a * clamped * clamped;
590 return clamped * scalar;
591}
592
593GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
594 BlurParameters parameters) {
595 GaussianBlurPipeline::FragmentShader::KernelSamples result;
596 result.sample_count =
597 ((2 * parameters.blur_radius) / parameters.step_size) + 1;
598
599 // Chop off the last samples if the radius >= 3 where they account for < 1.56%
600 // of the result.
601 int x_offset = 0;
602 if (parameters.blur_radius >= 3) {
603 result.sample_count -= 2;
604 x_offset = 1;
605 }
606
607 Scalar tally = 0.0f;
608 for (int i = 0; i < result.sample_count; ++i) {
609 int x = x_offset + (i * parameters.step_size) - parameters.blur_radius;
610 result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{
611 .uv_offset = parameters.blur_uv_offset * x,
612 .coefficient = expf(-0.5f * (x * x) /
613 (parameters.blur_sigma * parameters.blur_sigma)) /
614 (sqrtf(2.0f * M_PI) * parameters.blur_sigma),
615 };
616 tally += result.samples[i].coefficient;
617 }
618
619 // Make sure everything adds up to 1.
620 for (auto& sample : result.samples) {
621 sample.coefficient /= tally;
622 }
623
624 return result;
625}
626
627// This works by shrinking the kernel size by 2 and relying on lerp to read
628// between the samples.
629GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(
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;
634 int32_t j = 0;
635 for (int i = 0; i < result.sample_count; i++) {
636 if (i == middle) {
637 result.samples[i] = parameters.samples[j++];
638 } else {
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 +
645 right.uv_offset * right.coefficient) /
646 (left.coefficient + right.coefficient),
647 .coefficient = left.coefficient + right.coefficient,
648 };
649 j += 2;
650 }
651 }
652
653 return result;
654}
655
656} // namespace impeller
const char * options
#define M_PI
static void round(SkPoint *p)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
const T & value() const
Definition status_or.h:77
bool ok() const
Definition status_or.h:75
bool ok() const
Definition status.h:71
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
static std::shared_ptr< Contents > MakeAnonymous(RenderProc render_proc, CoverageProc coverage_proc)
Definition contents.cc:42
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition entity.cc:62
static Entity FromSnapshot(const Snapshot &snapshot, BlendMode blend_mode=BlendMode::kSourceOver)
Create an entity that can be used to render a given snapshot.
Definition entity.cc:22
std::optional< Rect > GetCoverage() const
Definition entity.cc:66
BlendMode GetBlendMode() const
Definition entity.cc:119
void SetContents(std::shared_ptr< Contents > contents)
Definition entity.cc:90
Entity Clone() const
Definition entity.cc:195
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition entity.cc:46
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
std::vector< FilterInput::Ref > Vector
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, BlurStyle mask_blur_style, const std::shared_ptr< Geometry > &mask_geometry)
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 Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition host_buffer.h:50
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:33
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.
FragmentShader_ FragmentShader
Definition pipeline.h:106
std::shared_ptr< Texture > GetRenderTargetTexture() const
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
static bool b
struct MyStruct a[10]
GAsyncResult * result
#define FML_DLOG(severity)
Definition logging.h:102
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
double x
internal::CopyableLambda< T > MakeCopyable(T lambda)
GaussianBlurPipeline::FragmentShader::KernelSamples GenerateBlurInfo(BlurParameters parameters)
Point Vector2
Definition point.h:320
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(GaussianBlurPipeline::FragmentShader::KernelSamples parameters)
float Scalar
Definition scalar.h:18
constexpr float kEhCloseEnough
Definition constants.h:56
TPoint< Scalar > Point
Definition point.h:316
SamplerAddressMode
Definition formats.h:423
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...
TSize< int64_t > ISize
Definition size.h:138
TSize< Scalar > Size
Definition size.h:137
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition contents.cc:20
std::array< Point, 4 > Quad
Definition point.h:321
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition matrix.h:495
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition matrix.h:229
constexpr Quad Transform(const Quad &quad) const
Definition matrix.h:485
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition sigma.h:48
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...
Definition sigma.h:32
Represents a texture and its intended draw transform/sampler configuration.
Definition snapshot.h:24
std::shared_ptr< Texture > texture
Definition snapshot.h:25
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...
Definition rect.h:294
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition rect.h:140
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition rect.h:394
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:146
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:582
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...
Definition rect.h:287
Type height
Definition size.h:23
Type width
Definition size.h:22
#define ERROR(message)