| Flutter Impeller |

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
5 #include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
6
7 #include <cmath>
8
9 #include "flutter/fml/make_copyable.h"
10 #include "impeller/entity/contents/clip_contents.h"
11 #include "impeller/entity/contents/content_context.h"
12 #include "impeller/entity/texture_downsample.frag.h"
13 #include "impeller/entity/texture_fill.frag.h"
14 #include "impeller/entity/texture_fill.vert.h"
15 #include "impeller/geometry/color.h"
16 #include "impeller/renderer/render_pass.h"
17 #include "impeller/renderer/vertex_buffer_builder.h"
18
19 namespace impeller {
20
21 using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader;
22 using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
23
24 namespace {
25
26 constexpr Scalar kMaxSigma = 500.0f;
27
28 SamplerDescriptor MakeSamplerDescriptor(MinMagFilter filter,
29 SamplerAddressMode address_mode) {
30 SamplerDescriptor sampler_desc;
31 sampler_desc.min_filter = filter;
32 sampler_desc.mag_filter = filter;
33 sampler_desc.width_address_mode = address_mode;
34 sampler_desc.height_address_mode = address_mode;
35 return sampler_desc;
36 }
37
38 void SetTileMode(SamplerDescriptor* descriptor,
39 const ContentContext& renderer,
40 Entity::TileMode tile_mode) {
41 switch (tile_mode) {
42 case Entity::TileMode::kDecal:
43 if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
44 descriptor->width_address_mode = SamplerAddressMode::kDecal;
45 descriptor->height_address_mode = SamplerAddressMode::kDecal;
46 }
47 break;
48 case Entity::TileMode::kClamp:
49 descriptor->width_address_mode = SamplerAddressMode::kClampToEdge;
50 descriptor->height_address_mode = SamplerAddressMode::kClampToEdge;
51 break;
52 case Entity::TileMode::kMirror:
53 descriptor->width_address_mode = SamplerAddressMode::kMirror;
54 descriptor->height_address_mode = SamplerAddressMode::kMirror;
55 break;
56 case Entity::TileMode::kRepeat:
57 descriptor->width_address_mode = SamplerAddressMode::kRepeat;
58 descriptor->height_address_mode = SamplerAddressMode::kRepeat;
59 break;
60 }
61 }
62
63 Vector2 Clamp(Vector2 vec2, Scalar min, Scalar max) {
64 return Vector2(std::clamp(vec2.x, /*lo=*/min, /*hi=*/max),
65 std::clamp(vec2.y, /*lo=*/min, /*hi=*/max));
66 }
67
68 Vector2 ExtractScale(const Matrix& matrix) {
69 Vector2 entity_scale_x = matrix * Vector2(1.0, 0.0);
70 Vector2 entity_scale_y = matrix * Vector2(0.0, 1.0);
71 return Vector2(entity_scale_x.GetLength(), entity_scale_y.GetLength());
72 }
73
74 struct BlurInfo {
75 /// The scalar that is used to get from source space to unrotated local space.
76 Vector2 source_space_scalar;
77 /// The translation that is used to get from source space to unrotated local
78 /// space.
79 Vector2 source_space_offset;
80 /// Sigma when considering an entity's scale and the effect transform.
81 Vector2 scaled_sigma;
82 /// Blur radius in source pixels based on scaled_sigma.
83 Vector2 blur_radius;
84 /// The halo padding in source space.
85 Vector2 padding;
86 /// Padding in unrotated local space.
87 Vector2 local_padding;
88 };
89
90 /// Calculates sigma derivatives necessary for rendering or calculating
91 /// coverage.
92 BlurInfo CalculateBlurInfo(const Entity& entity,
93 const Matrix& effect_transform,
94 Vector2 sigma) {
95 // Source space here is scaled by the entity's transform. This is a
96 // requirement for text to be rendered correctly. You can think of this as
97 // "scaled source space" or "un-rotated local space". The entity's rotation is
98 // applied to the result of the blur as part of the result's transform.
99 const Vector2 source_space_scalar =
100 ExtractScale(entity.GetTransform().Basis());
101 const Vector2 source_space_offset =
102 Vector2(entity.GetTransform().m[12], entity.GetTransform().m[13]);
103
104 Vector2 scaled_sigma =
105 (effect_transform.Basis() * Matrix::MakeScale(source_space_scalar) * //
106 Vector2(GaussianBlurFilterContents::ScaleSigma(sigma.x),
107 GaussianBlurFilterContents::ScaleSigma(sigma.y)))
108 .Abs();
109 scaled_sigma = Clamp(scaled_sigma, 0, kMaxSigma);
110 Vector2 blur_radius =
111 Vector2(GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.x),
112 GaussianBlurFilterContents::CalculateBlurRadius(scaled_sigma.y));
113 Vector2 padding(ceil(blur_radius.x), ceil(blur_radius.y));
114 Vector2 local_padding =
115 (Matrix::MakeScale(source_space_scalar) * padding).Abs();
116 return {
117 .source_space_scalar = source_space_scalar,
118 .source_space_offset = source_space_offset,
119 .scaled_sigma = scaled_sigma,
120 .blur_radius = blur_radius,
121 .padding = padding,
122 .local_padding = local_padding,
123 };
124 }
125
126 /// Perform FilterInput::GetSnapshot with safety checks.
127 std::optional<Snapshot> GetSnapshot(const std::shared_ptr<FilterInput>& input,
128 const ContentContext& renderer,
129 const Entity& entity,
130 const std::optional<Rect>& coverage_hint) {
131 std::optional<Snapshot> input_snapshot =
132 input->GetSnapshot("GaussianBlur", renderer, entity,
133 /*coverage_limit=*/coverage_hint);
134 if (!input_snapshot.has_value()) {
135 return std::nullopt;
136 }
137
138 return input_snapshot;
139 }
140
141 /// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
142 /// be returned when `rect` == `reference`.
143 Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
144 Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
145 rect.GetSize());
146 return result.Scale(1.0f / Vector2(reference.GetSize()));
147 }
148
149 Quad CalculateSnapshotUVs(
150 const Snapshot& input_snapshot,
151 const std::optional<Rect>& source_expanded_coverage_hint) {
152 std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage();
153 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
154 FML_DCHECK(input_snapshot.transform.IsTranslationScaleOnly());
155 if (source_expanded_coverage_hint.has_value() &&
156 input_snapshot_coverage.has_value()) {
157 // Only process the uvs where the blur is happening, not the whole texture.
158 std::optional<Rect> uvs =
159 MakeReferenceUVs(input_snapshot_coverage.value(),
160 source_expanded_coverage_hint.value())
161 .Intersection(Rect::MakeSize(Size(1, 1)));
162 FML_DCHECK(uvs.has_value());
163 if (uvs.has_value()) {
164 blur_uvs[0] = uvs->GetLeftTop();
165 blur_uvs[1] = uvs->GetRightTop();
166 blur_uvs[2] = uvs->GetLeftBottom();
167 blur_uvs[3] = uvs->GetRightBottom();
168 }
169 }
170 return blur_uvs;
171 }
172
173 Scalar CeilToDivisible(Scalar val, Scalar divisor) {
174 if (divisor == 0.0f) {
175 return val;
176 }
177
178 Scalar remainder = fmod(val, divisor);
179 if (remainder != 0.0f) {
180 return val + (divisor - remainder);
181 } else {
182 return val;
183 }
184 }
185
186 Scalar FloorToDivisible(Scalar val, Scalar divisor) {
187 if (divisor == 0.0f) {
188 return val;
189 }
190
191 Scalar remainder = fmod(val, divisor);
192 if (remainder != 0.0f) {
193 return val - remainder;
194 } else {
195 return val;
196 }
197 }
198
199 struct DownsamplePassArgs {
200 /// The output size of the down-sampling pass.
201 ISize subpass_size;
202 /// The UVs that will be used for drawing to the down-sampling pass.
203 /// This effectively is chopping out a region of the input.
204 Quad uvs;
205 /// The effective scalar of the down-sample pass.
206 /// This isn't usually exactly as we'd calculate because it has to be rounded
207 /// to integer boundaries for generating the texture for the output.
208 Vector2 effective_scalar;
209 /// Transforms from unrotated local space to position the output from the
210 /// down-sample pass.
211 /// This can differ if we request a coverage hint but it is rejected, as is
212 /// the case with backdrop filters.
213 Matrix transform;
214 };
215
216 /// Calculates info required for the down-sampling pass.
217 DownsamplePassArgs CalculateDownsamplePassArgs(
218 Vector2 scaled_sigma,
219 Vector2 padding,
220 const Snapshot& input_snapshot,
221 const std::optional<Rect>& source_expanded_coverage_hint,
222 const std::shared_ptr<FilterInput>& input,
223 const Entity& snapshot_entity) {
224 Scalar desired_scalar =
225 std::min(GaussianBlurFilterContents::CalculateScale(scaled_sigma.x),
226 GaussianBlurFilterContents::CalculateScale(scaled_sigma.y));
227
228 // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the
229 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
230 // pass.
231 Vector2 downsample_scalar(desired_scalar, desired_scalar);
232 // TODO(gaaclarke): The padding could be removed if we know it's not needed or
233 // resized to account for the expanded_clip_coverage. There doesn't appear
234 // to be the math to make those calculations though. The following
235 // optimization works, but causes a shimmer as a result of
236 // https://github.com/flutter/flutter/issues/140193 so it isn't applied.
237 //
238 // !input_snapshot->GetCoverage()->Expand(-local_padding)
239 // .Contains(coverage_hint.value()))
240
241 std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage();
242 if (input_snapshot.transform.Equals(snapshot_entity.GetTransform()) &&
243 source_expanded_coverage_hint.has_value() &&
244 snapshot_coverage.has_value() &&
245 snapshot_coverage->Contains(source_expanded_coverage_hint.value())) {
246 // If the snapshot's transform is the identity transform and we have
247 // coverage hint that fits inside of the snapshots coverage that means the
248 // coverage hint was ignored so we will trim out the area we are interested
249 // in the down-sample pass. This usually means we have a backdrop image
250 // filter.
251 //
252 // The region we cut out will be aligned with the down-sample divisor to
253 // avoid pixel alignment problems that create shimmering.
254 int32_t divisor = std::round(1.0f / desired_scalar);
255 Rect aligned_coverage_hint = Rect::MakeLTRB(
256 FloorToDivisible(source_expanded_coverage_hint->GetLeft(), divisor),
257 FloorToDivisible(source_expanded_coverage_hint->GetTop(), divisor),
258 source_expanded_coverage_hint->GetRight(),
259 source_expanded_coverage_hint->GetBottom());
260 aligned_coverage_hint = Rect::MakeXYWH(
261 aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(),
262 CeilToDivisible(aligned_coverage_hint.GetWidth(), divisor),
263 CeilToDivisible(aligned_coverage_hint.GetHeight(), divisor));
264 ISize source_size = ISize(aligned_coverage_hint.GetSize().width,
265 aligned_coverage_hint.GetSize().height);
266 Vector2 downsampled_size = source_size * downsample_scalar;
267 Scalar int_part;
268 FML_DCHECK(std::modf(downsampled_size.x, &int_part) == 0.0f);
269 FML_DCHECK(std::modf(downsampled_size.y, &int_part) == 0.0f);
270 (void)int_part;
271 ISize subpass_size = ISize(downsampled_size.x, downsampled_size.y);
272 Vector2 effective_scalar = Vector2(subpass_size) / source_size;
273 FML_DCHECK(effective_scalar == downsample_scalar);
274
275 Quad uvs = CalculateSnapshotUVs(input_snapshot, aligned_coverage_hint);
276 return {
277 .subpass_size = subpass_size,
278 .uvs = uvs,
279 .effective_scalar = effective_scalar,
280 .transform = Matrix::MakeTranslation(
281 {aligned_coverage_hint.GetX(), aligned_coverage_hint.GetY(), 0})};
282 } else {
283 //////////////////////////////////////////////////////////////////////////////
284 auto input_snapshot_size = input_snapshot.texture->GetSize();
285 Rect source_rect = Rect::MakeSize(input_snapshot_size);
286 Rect source_rect_padded = source_rect.Expand(padding);
287 Vector2 downsampled_size = source_rect_padded.GetSize() * downsample_scalar;
288 ISize subpass_size =
289 ISize(ceil(downsampled_size.x), ceil(downsampled_size.y));
290 Vector2 divisible_size(CeilToDivisible(source_rect_padded.GetSize().width,
291 1.0 / downsample_scalar.x),
292 CeilToDivisible(source_rect_padded.GetSize().height,
293 1.0 / downsample_scalar.y));
294 // Only make the padding divisible if we already have padding. If we don't
295 // have padding adding more can add artifacts to hard blur edges.
296 Vector2 divisible_padding(
297 padding.x > 0
298 ? padding.x +
299 (divisible_size.x - source_rect_padded.GetSize().width) / 2.0
300 : 0.f,
301 padding.y > 0
302 ? padding.y +
303 (divisible_size.y - source_rect_padded.GetSize().height) / 2.0
304 : 0.f);
305 source_rect_padded = source_rect.Expand(divisible_padding);
306
307 Vector2 effective_scalar =
308 Vector2(subpass_size) / source_rect_padded.GetSize();
309 Quad uvs = GaussianBlurFilterContents::CalculateUVs(
310 input, snapshot_entity, source_rect_padded, input_snapshot_size);
311 return {
312 .subpass_size = subpass_size,
313 .uvs = uvs,
314 .effective_scalar = effective_scalar,
315 .transform = input_snapshot.transform *
316 Matrix::MakeTranslation(-divisible_padding),
317 };
318 }
319 }
320
321 /// Makes a subpass that will render the scaled down input and add the
322 /// transparent gutter required for the blur halo.
323 fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
324 const ContentContext& renderer,
325 const std::shared_ptr<CommandBuffer>& command_buffer,
326 const std::shared_ptr<Texture>& input_texture,
327 const SamplerDescriptor& sampler_descriptor,
328 const DownsamplePassArgs& pass_args,
329 Entity::TileMode tile_mode) {
330 using VS = TextureFillVertexShader;
331
332 // If the texture already had mip levels generated, then we can use the
333 // original downsample shader.
334 if (pass_args.effective_scalar.x >= 0.5f ||
335 (!input_texture->NeedsMipmapGeneration() &&
336 input_texture->GetTextureDescriptor().mip_count > 1)) {
337 ContentContext::SubpassCallback subpass_callback =
338 [&](const ContentContext& renderer, RenderPass& pass) {
339 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
340
341 pass.SetCommandLabel("Gaussian blur downsample");
342 auto pipeline_options = OptionsFromPass(pass);
343 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
344 pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
345
346 TextureFillVertexShader::FrameInfo frame_info;
347 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
348 frame_info.texture_sampler_y_coord_scale =
349 input_texture->GetYCoordScale();
350
351 TextureFillFragmentShader::FragInfo frag_info;
352 frag_info.alpha = 1.0;
353
354 const Quad& uvs = pass_args.uvs;
355 std::array<VS::PerVertexData, 4> vertices = {
356 VS::PerVertexData{Point(0, 0), uvs[0]},
357 VS::PerVertexData{Point(1, 0), uvs[1]},
358 VS::PerVertexData{Point(0, 1), uvs[2]},
359 VS::PerVertexData{Point(1, 1), uvs[3]},
360 };
361 pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
362
363 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
364 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
365 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
366 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
367 TextureFillVertexShader::BindFrameInfo(
368 pass, host_buffer.EmplaceUniform(frame_info));
369 TextureFillFragmentShader::BindFragInfo(
370 pass, host_buffer.EmplaceUniform(frag_info));
371 TextureFillFragmentShader::BindTextureSampler(
372 pass, input_texture,
373 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
374 linear_sampler_descriptor));
375
376 return pass.Draw().ok();
377 };
378 return renderer.MakeSubpass("Gaussian Blur Filter", pass_args.subpass_size,
379 command_buffer, subpass_callback);
380 } else {
381 // This assumes we don't scale below 1/16.
382 Scalar edge = 1.0;
383 Scalar ratio = 0.25;
384 if (pass_args.effective_scalar.x <= 0.0625f) {
385 edge = 7.0;
386 ratio = 1.0f / 64.0f;
387 } else if (pass_args.effective_scalar.x <= 0.125f) {
388 edge = 3.0;
389 ratio = 1.0f / 16.0f;
390 }
391 ContentContext::SubpassCallback subpass_callback =
392 [&](const ContentContext& renderer, RenderPass& pass) {
393 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
394
395 pass.SetCommandLabel("Gaussian blur downsample");
396 auto pipeline_options = OptionsFromPass(pass);
397 pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
398 pass.SetPipeline(renderer.GetDownsamplePipeline(pipeline_options));
399
400 TextureFillVertexShader::FrameInfo frame_info;
401 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
402 frame_info.texture_sampler_y_coord_scale =
403 input_texture->GetYCoordScale();
404
405 TextureDownsampleFragmentShader::FragInfo frag_info;
406 frag_info.edge = edge;
407 frag_info.ratio = ratio;
408 frag_info.pixel_size = Vector2(1.0f / Size(input_texture->GetSize()));
409
410 const Quad& uvs = pass_args.uvs;
411 std::array<VS::PerVertexData, 4> vertices = {
412 VS::PerVertexData{Point(0, 0), uvs[0]},
413 VS::PerVertexData{Point(1, 0), uvs[1]},
414 VS::PerVertexData{Point(0, 1), uvs[2]},
415 VS::PerVertexData{Point(1, 1), uvs[3]},
416 };
417 pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
418
419 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
420 SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
421 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
422 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
423 TextureFillVertexShader::BindFrameInfo(
424 pass, host_buffer.EmplaceUniform(frame_info));
425 TextureDownsampleFragmentShader::BindFragInfo(
426 pass, host_buffer.EmplaceUniform(frag_info));
427 TextureDownsampleFragmentShader::BindTextureSampler(
428 pass, input_texture,
429 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
430 linear_sampler_descriptor));
431
432 return pass.Draw().ok();
433 };
434 return renderer.MakeSubpass("Gaussian Blur Filter", pass_args.subpass_size,
435 command_buffer, subpass_callback);
436 }
437 }
438
439 fml::StatusOr<RenderTarget> MakeBlurSubpass(
440 const ContentContext& renderer,
441 const std::shared_ptr<CommandBuffer>& command_buffer,
442 const RenderTarget& input_pass,
443 const SamplerDescriptor& sampler_descriptor,
444 Entity::TileMode tile_mode,
445 const BlurParameters& blur_info,
446 std::optional<RenderTarget> destination_target,
447 const Quad& blur_uvs) {
448 using VS = GaussianBlurVertexShader;
449
450 if (blur_info.blur_sigma < kEhCloseEnough) {
451 return input_pass;
452 }
453
454 const std::shared_ptr<Texture>& input_texture =
455 input_pass.GetRenderTargetTexture();
456
457 // TODO(gaaclarke): This blurs the whole image, but because we know the clip
458 // region we could focus on just blurring that.
459 ISize subpass_size = input_texture->GetSize();
460 ContentContext::SubpassCallback subpass_callback =
461 [&](const ContentContext& renderer, RenderPass& pass) {
462 GaussianBlurVertexShader::FrameInfo frame_info;
463 frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)),
464 frame_info.texture_sampler_y_coord_scale =
465 input_texture->GetYCoordScale();
466
467 HostBuffer& host_buffer = renderer.GetTransientsBuffer();
468
469 ContentContextOptions options = OptionsFromPass(pass);
470 options.primitive_type = PrimitiveType::kTriangleStrip;
471 pass.SetPipeline(renderer.GetGaussianBlurPipeline(options));
472
473 std::array<VS::PerVertexData, 4> vertices = {
474 VS::PerVertexData{blur_uvs[0], blur_uvs[0]},
475 VS::PerVertexData{blur_uvs[1], blur_uvs[1]},
476 VS::PerVertexData{blur_uvs[2], blur_uvs[2]},
477 VS::PerVertexData{blur_uvs[3], blur_uvs[3]},
478 };
479 pass.SetVertexBuffer(CreateVertexBuffer(vertices, host_buffer));
480
481 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
482 linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
483 linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
484 GaussianBlurFragmentShader::BindTextureSampler(
485 pass, input_texture,
486 renderer.GetContext()->GetSamplerLibrary()->GetSampler(
487 linear_sampler_descriptor));
488 GaussianBlurVertexShader::BindFrameInfo(
489 pass, host_buffer.EmplaceUniform(frame_info));
490 GaussianBlurFragmentShader::BindKernelSamples(
491 pass, host_buffer.EmplaceUniform(
492 LerpHackKernelSamples(GenerateBlurInfo(blur_info))));
493 return pass.Draw().ok();
494 };
495 if (destination_target.has_value()) {
496 return renderer.MakeSubpass("Gaussian Blur Filter",
497 destination_target.value(), command_buffer,
498 subpass_callback);
499 } else {
500 return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
501 command_buffer, subpass_callback);
502 }
503 }
504
505 int ScaleBlurRadius(Scalar radius, Scalar scalar) {
506 return static_cast<int>(std::round(radius * scalar));
507 }
508
509 Entity ApplyClippedBlurStyle(Entity::ClipOperation clip_operation,
510 const Entity& entity,
511 const std::shared_ptr<FilterInput>& input,
512 const Snapshot& input_snapshot,
513 Entity blur_entity,
514 const Geometry* geometry) {
515 auto clip_contents = std::make_shared<ClipContents>();
516 clip_contents->SetClipOperation(clip_operation);
517 clip_contents->SetGeometry(geometry);
518 Entity clipper;
519 clipper.SetContents(clip_contents);
520 auto restore = std::make_unique<ClipRestoreContents>();
521 Matrix entity_transform = entity.GetTransform();
522 Matrix blur_transform = blur_entity.GetTransform();
523 auto renderer = fml::MakeCopyable(
524 [blur_entity = blur_entity.Clone(), clipper = std::move(clipper),
525 restore = std::move(restore), entity_transform,
526 blur_transform](const ContentContext& renderer, const Entity& entity,
527 RenderPass& pass) mutable {
528 bool result = true;
529 clipper.SetClipDepth(entity.GetClipDepth());
530 clipper.SetTransform(entity.GetTransform() * entity_transform);
531 result = clipper.Render(renderer, pass) && result;
532 blur_entity.SetClipDepth(entity.GetClipDepth());
533 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
534 result = blur_entity.Render(renderer, pass) && result;
535 return result;
536 });
537 auto coverage =
538 fml::MakeCopyable([blur_entity = std::move(blur_entity),
539 blur_transform](const Entity& entity) mutable {
540 blur_entity.SetTransform(entity.GetTransform() * blur_transform);
541 return blur_entity.GetCoverage();
542 });
543 Entity result;
544 result.SetContents(Contents::MakeAnonymous(renderer, coverage));
545 return result;
546 }
547
548 Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
549 const Entity& entity,
550 const std::shared_ptr<FilterInput>& input,
551 const Snapshot& input_snapshot,
552 Entity blur_entity,
553 const Geometry* geometry,
554 Vector2 source_space_scalar,
555 Vector2 source_space_offset) {
556 switch (blur_style) {
557 case FilterContents::BlurStyle::kNormal:
558 return blur_entity;
559 case FilterContents::BlurStyle::kInner:
560 return ApplyClippedBlurStyle(Entity::ClipOperation::kIntersect, entity,
561 input, input_snapshot,
562 std::move(blur_entity), geometry);
563 break;
564 case FilterContents::BlurStyle::kOuter:
565 return ApplyClippedBlurStyle(Entity::ClipOperation::kDifference, entity,
566 input, input_snapshot,
567 std::move(blur_entity), geometry);
568 case FilterContents::BlurStyle::kSolid: {
569 Entity snapshot_entity =
570 Entity::FromSnapshot(input_snapshot, entity.GetBlendMode());
571 Entity result;
572 Matrix blurred_transform = blur_entity.GetTransform();
573 Matrix snapshot_transform =
574 entity.GetTransform() * //
575 Matrix::MakeScale(1.f / source_space_scalar) *
576 Matrix::MakeTranslation(-1 * source_space_offset) *
577 input_snapshot.transform;
578 result.SetContents(Contents::MakeAnonymous(
579 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
580 blurred_transform, snapshot_transform,
581 snapshot_entity = std::move(snapshot_entity)](
582 const ContentContext& renderer,
583 const Entity& entity,
584 RenderPass& pass) mutable {
585 bool result = true;
586 snapshot_entity.SetTransform(entity.GetTransform() *
587 snapshot_transform);
588 snapshot_entity.SetClipDepth(entity.GetClipDepth());
589 result = result && snapshot_entity.Render(renderer, pass);
590 blur_entity.SetClipDepth(entity.GetClipDepth());
591 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
592 result = result && blur_entity.Render(renderer, pass);
593 return result;
594 }),
595 fml::MakeCopyable([blur_entity = blur_entity.Clone(),
596 blurred_transform](const Entity& entity) mutable {
597 blur_entity.SetTransform(entity.GetTransform() * blurred_transform);
598 return blur_entity.GetCoverage();
599 })));
600 return result;
601 }
602 }
603 }
604 } // namespace
605
606 GaussianBlurFilterContents::GaussianBlurFilterContents(
607 Scalar sigma_x,
608 Scalar sigma_y,
609 Entity::TileMode tile_mode,
610 BlurStyle mask_blur_style,
611 const Geometry* mask_geometry)
612 : sigma_(sigma_x, sigma_y),
613 tile_mode_(tile_mode),
614 mask_blur_style_(mask_blur_style),
615 mask_geometry_(mask_geometry) {
616 // This is supposed to be enforced at a higher level.
617 FML_DCHECK(mask_blur_style == BlurStyle::kNormal || mask_geometry);
618 }
619
620 // This value was extracted from Skia, see:
621 // * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576
622 // * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/BlurUtils.h#L57
623 Scalar GaussianBlurFilterContents::CalculateScale(Scalar sigma) {
624 if (sigma <= 4) {
625 return 1.0;
626 }
627 Scalar raw_result = 4.0 / sigma;
628 // Round to the nearest 1/(2^n) to get the best quality down scaling.
629 Scalar exponent = round(log2f(raw_result));
630 // Don't scale down below 1/16th to preserve signal.
631 exponent = std::max(-4.0f, exponent);
632 Scalar rounded = powf(2.0f, exponent);
633 Scalar result = rounded;
634 // Extend the range of the 1/8th downsample based on the effective kernel size
635 // for the blur.
636 if (rounded < 0.125f) {
637 Scalar rounded_plus = powf(2.0f, exponent + 1);
638 Scalar blur_radius = CalculateBlurRadius(sigma);
639 int kernel_size_plus = (ScaleBlurRadius(blur_radius, rounded_plus) * 2) + 1;
640 // This constant was picked by looking at the results to make sure no
641 // shimmering was introduced at the highest sigma values that downscale to
642 // 1/16th.
643 static constexpr int32_t kEighthDownsampleKernalWidthMax = 41;
644 result = kernel_size_plus <= kEighthDownsampleKernalWidthMax ? rounded_plus
645 : rounded;
646 }
647 return result;
648 };
649
650 std::optional<Rect> GaussianBlurFilterContents::GetFilterSourceCoverage(
651 const Matrix& effect_transform,
652 const Rect& output_limit) const {
653 Vector2 scaled_sigma = {ScaleSigma(sigma_.x), ScaleSigma(sigma_.y)};
654 Vector2 blur_radius = {CalculateBlurRadius(scaled_sigma.x),
655 CalculateBlurRadius(scaled_sigma.y)};
656 Vector3 blur_radii =
657 effect_transform.Basis() * Vector3{blur_radius.x, blur_radius.y, 0.0};
658 return output_limit.Expand(Point(blur_radii.x, blur_radii.y));
659 }
660
661 std::optional<Rect> GaussianBlurFilterContents::GetFilterCoverage(
662 const FilterInput::Vector& inputs,
663 const Entity& entity,
664 const Matrix& effect_transform) const {
665 if (inputs.empty()) {
666 return {};
667 }
668 std::optional<Rect> input_coverage = inputs[0]->GetCoverage(entity);
669 if (!input_coverage.has_value()) {
670 return {};
671 }
672
673 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
674 return input_coverage.value().Expand(
675 Point(blur_info.local_padding.x, blur_info.local_padding.y));
676 }
677
678 // A brief overview how this works:
679 // 1) Snapshot the filter input.
680 // 2) Perform downsample pass. This also inserts the gutter around the input
681 // snapshot since the blur can render outside the bounds of the snapshot.
682 // 3) Perform 1D horizontal blur pass.
683 // 4) Perform 1D vertical blur pass.
684 // 5) Apply the blur style to the blur result. This may just mask the output or
685 // draw the original snapshot over the result.
686 std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
687 const FilterInput::Vector& inputs,
688 const ContentContext& renderer,
689 const Entity& entity,
690 const Matrix& effect_transform,
691 const Rect& coverage,
692 const std::optional<Rect>& coverage_hint) const {
693 if (inputs.empty()) {
694 return std::nullopt;
695 }
696
697 BlurInfo blur_info = CalculateBlurInfo(entity, effect_transform, sigma_);
698
699 // Apply as much of the desired padding as possible from the source. This may
700 // be ignored so must be accounted for in the downsample pass by adding a
701 // transparent gutter.
702 std::optional<Rect> expanded_coverage_hint;
703 if (coverage_hint.has_value()) {
704 expanded_coverage_hint = coverage_hint->Expand(blur_info.local_padding);
705 }
706
707 Entity snapshot_entity = entity.Clone();
708 snapshot_entity.SetTransform(
709 Matrix::MakeTranslation(blur_info.source_space_offset) *
710 Matrix::MakeScale(blur_info.source_space_scalar));
711
712 std::optional<Rect> source_expanded_coverage_hint;
713 if (expanded_coverage_hint.has_value()) {
714 source_expanded_coverage_hint = expanded_coverage_hint->TransformBounds(
715 Matrix::MakeTranslation(blur_info.source_space_offset) *
716 Matrix::MakeScale(blur_info.source_space_scalar) *
717 entity.GetTransform().Invert());
718 }
719
720 std::optional<Snapshot> input_snapshot = GetSnapshot(
721 inputs[0], renderer, snapshot_entity, source_expanded_coverage_hint);
722 if (!input_snapshot.has_value()) {
723 return std::nullopt;
724 }
725
726 if (blur_info.scaled_sigma.x < kEhCloseEnough &&
727 blur_info.scaled_sigma.y < kEhCloseEnough) {
728 Entity result =
729 Entity::FromSnapshot(input_snapshot.value(),
730 entity.GetBlendMode()); // No blur to render.
731 result.SetTransform(
732 entity.GetTransform() *
733 Matrix::MakeScale(1.f / blur_info.source_space_scalar) *
734 Matrix::MakeTranslation(-1 * blur_info.source_space_offset) *
735 input_snapshot->transform);
736 return result;
737 }
738
739 // Note: The code below uses three different command buffers when it would be
740 // possible to combine the operations into a single buffer. From testing and
741 // user bug reports (see https://github.com/flutter/flutter/issues/154046 ),
742 // this sometimes causes deviceLost errors on older Adreno devices. Breaking
743 // the work up into three different command buffers seems to prevent this
744 // crash.
745 std::shared_ptr<CommandBuffer> command_buffer_1 =
746 renderer.GetContext()->CreateCommandBuffer();
747 if (!command_buffer_1) {
748 return std::nullopt;
749 }
750
751 DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs(
752 blur_info.scaled_sigma, blur_info.padding, input_snapshot.value(),
753 source_expanded_coverage_hint, inputs[0], snapshot_entity);
754
755 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
756 renderer, command_buffer_1, input_snapshot->texture,
757 input_snapshot->sampler_descriptor, downsample_pass_args, tile_mode_);
758
759 if (!pass1_out.ok()) {
760 return std::nullopt;
761 }
762
763 Vector2 pass1_pixel_size =
764 1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
765
766 Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
767
768 std::shared_ptr<CommandBuffer> command_buffer_2 =
769 renderer.GetContext()->CreateCommandBuffer();
770 if (!command_buffer_2) {
771 return std::nullopt;
772 }
773
774 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
775 renderer, command_buffer_2, /*input_pass=*/pass1_out.value(),
776 input_snapshot->sampler_descriptor, tile_mode_,
777 BlurParameters{
778 .blur_uv_offset = Point(0.0, pass1_pixel_size.y),
779 .blur_sigma = blur_info.scaled_sigma.y *
780 downsample_pass_args.effective_scalar.y,
781 .blur_radius = ScaleBlurRadius(
782 blur_info.blur_radius.y, downsample_pass_args.effective_scalar.y),
783 .step_size = 1,
784 },
785 /*destination_target=*/std::nullopt, blur_uvs);
786
787 if (!pass2_out.ok()) {
788 return std::nullopt;
789 }
790
791 std::shared_ptr<CommandBuffer> command_buffer_3 =
792 renderer.GetContext()->CreateCommandBuffer();
793 if (!command_buffer_3) {
794 return std::nullopt;
795 }
796
797 // Only ping pong if the first pass actually created a render target.
798 auto pass3_destination = pass2_out.value().GetRenderTargetTexture() !=
799 pass1_out.value().GetRenderTargetTexture()
800 ? std::optional<RenderTarget>(pass1_out.value())
801 : std::optional<RenderTarget>(std::nullopt);
802
803 fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
804 renderer, command_buffer_3, /*input_pass=*/pass2_out.value(),
805 input_snapshot->sampler_descriptor, tile_mode_,
806 BlurParameters{
807 .blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
808 .blur_sigma = blur_info.scaled_sigma.x *
809 downsample_pass_args.effective_scalar.x,
810 .blur_radius = ScaleBlurRadius(
811 blur_info.blur_radius.x, downsample_pass_args.effective_scalar.x),
812 .step_size = 1,
813 },
814 pass3_destination, blur_uvs);
815
816 if (!pass3_out.ok()) {
817 return std::nullopt;
818 }
819
820 if (!renderer.GetContext()
821 ->GetCommandQueue()
822 ->Submit(/*buffers=*/{command_buffer_1, command_buffer_2,
823 command_buffer_3})
824 .ok()) {
825 return std::nullopt;
826 }
827
828 // The ping-pong approach requires that each render pass output has the same
829 // size.
830 FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==
831 pass2_out.value().GetRenderTargetSize()) &&
832 (pass2_out.value().GetRenderTargetSize() ==
833 pass3_out.value().GetRenderTargetSize()));
834
835 SamplerDescriptor sampler_desc = MakeSamplerDescriptor(
836 MinMagFilter::kLinear, SamplerAddressMode::kClampToEdge);
837
838 Entity blur_output_entity = Entity::FromSnapshot(
839 Snapshot{.texture = pass3_out.value().GetRenderTargetTexture(),
840 .transform =
841 entity.GetTransform() * //
842 Matrix::MakeScale(1.f / blur_info.source_space_scalar) * //
843 Matrix::MakeTranslation(-1 * blur_info.source_space_offset) *
844 downsample_pass_args.transform * //
845 Matrix::MakeScale(1 / downsample_pass_args.effective_scalar),
846 .sampler_descriptor = sampler_desc,
847 .opacity = input_snapshot->opacity},
848 entity.GetBlendMode());
849
850 return ApplyBlurStyle(mask_blur_style_, entity, inputs[0],
851 input_snapshot.value(), std::move(blur_output_entity),
852 mask_geometry_, blur_info.source_space_scalar,
853 blur_info.source_space_offset);
854 }
855
856 Scalar GaussianBlurFilterContents::CalculateBlurRadius(Scalar sigma) {
857 return static_cast<Radius>(Sigma(sigma)).radius;
858 }
859
860 Quad GaussianBlurFilterContents::CalculateUVs(
861 const std::shared_ptr<FilterInput>& filter_input,
862 const Entity& entity,
863 const Rect& source_rect,
864 const ISize& texture_size) {
865 Matrix input_transform = filter_input->GetLocalTransform(entity);
866 Quad coverage_quad = source_rect.GetTransformedPoints(input_transform);
867
868 Matrix uv_transform = Matrix::MakeScale(
869 {1.0f / texture_size.width, 1.0f / texture_size.height, 1.0f});
870 return uv_transform.Transform(coverage_quad);
871 }
872
873 // This function was calculated by observing Skia's behavior. Its blur at 500
874 // seemed to be 0.15. Since we clamp at 500 I solved the quadratic equation
875 // that puts the minima there and a f(0)=1.
876 Scalar GaussianBlurFilterContents::ScaleSigma(Scalar sigma) {
877 // Limit the kernel size to 1000x1000 pixels, like Skia does.
878 Scalar clamped = std::min(sigma, kMaxSigma);
879 constexpr Scalar a = 3.4e-06;
880 constexpr Scalar b = -3.4e-3;
881 constexpr Scalar c = 1.f;
882 Scalar scalar = c + b * clamped + a * clamped * clamped;
883 return clamped * scalar;
884 }
885
886 KernelSamples GenerateBlurInfo(BlurParameters parameters) {
887 KernelSamples result;
888 result.sample_count =
889 ((2 * parameters.blur_radius) / parameters.step_size) + 1;
890
891 // Chop off the last samples if the radius >= 3 where they account for < 1.56%
892 // of the result.
893 int x_offset = 0;
894 if (parameters.blur_radius >= 3) {
895 result.sample_count -= 2;
896 x_offset = 1;
897 }
898
899 // This is a safe-guard to make sure we don't overflow the fragment shader.
900 // The kernel size is multiplied by 2 since we'll use the lerp hack on the
901 // result. In practice this isn't throwing away much data since the blur radii
902 // are around 53 before the down-sampling and max sigma of 500 kick in.
903 //
904 // TODO(https://github.com/flutter/flutter/issues/150462): Come up with a more
905 // wholistic remedy for this. A proper downsample size should not make this
906 // required. Or we can increase the kernel size.
907 if (result.sample_count > KernelSamples::kMaxKernelSize) {
908 result.sample_count = KernelSamples::kMaxKernelSize;
909 }
910
911 Scalar tally = 0.0f;
912 for (int i = 0; i < result.sample_count; ++i) {
913 int x = x_offset + (i * parameters.step_size) - parameters.blur_radius;
914 result.samples[i] = KernelSample{
915 .uv_offset = parameters.blur_uv_offset * x,
916 .coefficient = expf(-0.5f * (x * x) /
917 (parameters.blur_sigma * parameters.blur_sigma)) /
918 (sqrtf(2.0f * M_PI) * parameters.blur_sigma),
919 };
920 tally += result.samples[i].coefficient;
921 }
922
923 // Make sure everything adds up to 1.
924 for (auto& sample : result.samples) {
925 sample.coefficient /= tally;
926 }
927
928 return result;
929 }
930
931 // This works by shrinking the kernel size by 2 and relying on lerp to read
932 // between the samples.
933 GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(
934 KernelSamples parameters) {
935 GaussianBlurPipeline::FragmentShader::KernelSamples result = {};
936 result.sample_count = ((parameters.sample_count - 1) / 2) + 1;
937 int32_t middle = result.sample_count / 2;
938 int32_t j = 0;
939 FML_DCHECK(result.sample_count <= kGaussianBlurMaxKernelSize);
940 static_assert(sizeof(result.sample_data) ==
941 sizeof(std::array<Vector4, kGaussianBlurMaxKernelSize>));
942
943 for (int i = 0; i < result.sample_count; i++) {
944 if (i == middle) {
945 result.sample_data[i].x = parameters.samples[j].uv_offset.x;
946 result.sample_data[i].y = parameters.samples[j].uv_offset.y;
947 result.sample_data[i].z = parameters.samples[j].coefficient;
948 j++;
949 } else {
950 KernelSample left = parameters.samples[j];
951 KernelSample right = parameters.samples[j + 1];
952
953 result.sample_data[i].z = left.coefficient + right.coefficient;
954
955 Point uv = (left.uv_offset * left.coefficient +
956 right.uv_offset * right.coefficient) /
957 (left.coefficient + right.coefficient);
958 result.sample_data[i].x = uv.x;
959 result.sample_data[i].y = uv.y;
960 j += 2;
961 }
962 }
963
964 return result;
965 }
966
967 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::RenderPipelineHandle::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:107
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
impeller::Entity::TileMode::kClamp
@ kClamp
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:19
impeller::KernelSamples
Definition: gaussian_blur_filter_contents.h:39
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::KernelSamples::sample_count
int sample_count
Definition: gaussian_blur_filter_contents.h:41
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:47
uvs
Quad uvs
Definition: gaussian_blur_filter_contents.cc:204
impeller::BlurParameters::blur_uv_offset
Point blur_uv_offset
Definition: gaussian_blur_filter_contents.h:23
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::FilterContents::BlurStyle
BlurStyle
Definition: filter_contents.h:26
impeller::KernelSample::uv_offset
Vector2 uv_offset
Definition: gaussian_blur_filter_contents.h:30
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::SamplerAddressMode
SamplerAddressMode
Definition: formats.h:441
impeller::Entity::TileMode::kDecal
@ kDecal
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::KernelSample::coefficient
float coefficient
Definition: gaussian_blur_filter_contents.h:31
padding
Vector2 padding
The halo padding in source space.
Definition: gaussian_blur_filter_contents.cc:85
impeller::SamplerAddressMode::kClampToEdge
@ kClampToEdge
gaussian_blur_filter_contents.h
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::kGaussianBlurMaxKernelSize
static constexpr int32_t kGaussianBlurMaxKernelSize
Definition: gaussian_blur_filter_contents.h:17
impeller::GaussianBlurFilterContents::CalculateBlurRadius
static Scalar CalculateBlurRadius(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:856
impeller::Entity::TileMode::kRepeat
@ kRepeat
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
subpass_size
ISize subpass_size
The output size of the down-sampling pass.
Definition: gaussian_blur_filter_contents.cc:201
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::GaussianBlurVertexShader
GaussianBlurPipeline::VertexShader GaussianBlurVertexShader
Definition: gaussian_blur_filter_contents.cc:21
impeller::Entity::TileMode::kMirror
@ kMirror
impeller::SamplerDescriptor::mag_filter
MinMagFilter mag_filter
Definition: sampler_descriptor.h:17
impeller::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:16
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::GaussianBlurFilterContents::GetFilterCoverage
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...
Definition: gaussian_blur_filter_contents.cc:661
impeller::BlurParameters::blur_radius
int blur_radius
Definition: gaussian_blur_filter_contents.h:25
impeller::Entity
Definition: entity.h:20
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize
Definition: size.h:19
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:16
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::Quad
std::array< Point, 4 > Quad
Definition: point.h:332
render_pass.h
impeller::GaussianBlurFragmentShader
GaussianBlurPipeline::FragmentShader GaussianBlurFragmentShader
Definition: gaussian_blur_filter_contents.cc:22
impeller::Radius
For convolution filters, the "radius" is the size of the convolution kernel to use on the local space...
Definition: sigma.h:48
impeller::FilterContents::BlurStyle::kSolid
@ kSolid
Solid inside, blurred outside.
impeller::TRect::GetTransformedPoints
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:420
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:213
impeller::Entity::FromSnapshot
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:21
impeller::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(std::array< VertexType, size > input, HostBuffer &host_buffer)
Create an index-less vertex buffer from a fixed size array.
Definition: vertex_buffer_builder.h:24
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:550
impeller::MinMagFilter::kLinear
@ kLinear
impeller::GaussianBlurFilterContents::GetFilterSourceCoverage
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...
Definition: gaussian_blur_filter_contents.cc:650
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:144
impeller::KernelSample
Definition: gaussian_blur_filter_contents.h:29
impeller::RenderPipelineHandle::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:106
local_padding
Vector2 local_padding
Padding in unrotated local space.
Definition: gaussian_blur_filter_contents.cc:87
impeller::SamplerDescriptor::width_address_mode
SamplerAddressMode width_address_mode
Definition: sampler_descriptor.h:20
impeller::BlurParameters::step_size
int step_size
Definition: gaussian_blur_filter_contents.h:26
impeller::GaussianBlurFilterContents::GaussianBlurFilterContents
GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode, BlurStyle mask_blur_style, const Geometry *mask_geometry=nullptr)
Definition: gaussian_blur_filter_contents.cc:606
impeller::Sigma
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
impeller::MinMagFilter
MinMagFilter
Describes how the texture should be sampled when the texture is being shrunk (minified) or expanded (...
Definition: formats.h:415
clip_contents.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::FilterContents::BlurStyle::kInner
@ kInner
Blurred inside, nothing outside.
impeller::BlurParameters
Definition: gaussian_blur_filter_contents.h:22
impeller::Entity::TileMode
TileMode
Definition: entity.h:42
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:112
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:513
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::GenerateBlurInfo
KernelSamples GenerateBlurInfo(BlurParameters parameters)
Definition: gaussian_blur_filter_contents.cc:886
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:701
content_context.h
impeller::FilterContents::BlurStyle::kOuter
@ kOuter
Nothing inside, blurred outside.
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:63
impeller::Geometry
Definition: geometry.h:55
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:554
scaled_sigma
Vector2 scaled_sigma
Sigma when considering an entity's scale and the effect transform.
Definition: gaussian_blur_filter_contents.cc:81
impeller::SamplerAddressMode::kMirror
@ kMirror
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
std
Definition: comparable.h:95
impeller::Entity::Clone
Entity Clone() const
Definition: entity.cc:170
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
blur_radius
Vector2 blur_radius
Blur radius in source pixels based on scaled_sigma.
Definition: gaussian_blur_filter_contents.cc:83
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:523
impeller::Entity::ClipOperation
ClipOperation
Definition: entity.h:61
impeller::GaussianBlurFilterContents::CalculateScale
static Scalar CalculateScale(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:623
impeller::LerpHackKernelSamples
GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(KernelSamples parameters)
Definition: gaussian_blur_filter_contents.cc:933
color.h
impeller::Contents::MakeAnonymous
static std::shared_ptr< Contents > MakeAnonymous(RenderProc render_proc, CoverageProc coverage_proc)
Definition: contents.cc:41
impeller::TSize::height
Type height
Definition: size.h:23
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::GaussianBlurFilterContents::CalculateUVs
static Quad CalculateUVs(const std::shared_ptr< FilterInput > &filter_input, const Entity &entity, const Rect &source_rect, const ISize &texture_size)
Definition: gaussian_blur_filter_contents.cc:860
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
impeller::Capabilities::SupportsDecalSamplerAddressMode
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
impeller::SamplerDescriptor::height_address_mode
SamplerAddressMode height_address_mode
Definition: sampler_descriptor.h:21
impeller
Definition: allocation.cc:12
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::BlurParameters::blur_sigma
Scalar blur_sigma
Definition: gaussian_blur_filter_contents.h:24
impeller::ContentContext
Definition: content_context.h:366
impeller::KernelSamples::kMaxKernelSize
static constexpr int kMaxKernelSize
Definition: gaussian_blur_filter_contents.h:40
effective_scalar
Vector2 effective_scalar
Definition: gaussian_blur_filter_contents.cc:208
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
impeller::KernelSamples::samples
KernelSample samples[kMaxKernelSize]
Definition: gaussian_blur_filter_contents.h:42
source_space_scalar
Vector2 source_space_scalar
The scalar that is used to get from source space to unrotated local space.
Definition: gaussian_blur_filter_contents.cc:76
impeller::TRect::Expand
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:612
vertex_buffer_builder.h
source_space_offset
Vector2 source_space_offset
Definition: gaussian_blur_filter_contents.cc:79
impeller::GaussianBlurFilterContents::ScaleSigma
static Scalar ScaleSigma(Scalar sigma)
Definition: gaussian_blur_filter_contents.cc:876
impeller::SamplerAddressMode::kRepeat
@ kRepeat
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...