@@ -19,6 +19,16 @@ using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
19
19
20
20
namespace {
21
21
22
+ std::optional<Rect> ExpandCoverageHint (const std::optional<Rect>& coverage_hint,
23
+ const Matrix& source_to_local_transform,
24
+ const Vector2& padding) {
25
+ if (!coverage_hint.has_value ()) {
26
+ return std::nullopt;
27
+ }
28
+ Vector2 transformed_padding = (source_to_local_transform * padding).Abs ();
29
+ return coverage_hint->Expand (transformed_padding);
30
+ }
31
+
22
32
SamplerDescriptor MakeSamplerDescriptor (MinMagFilter filter,
23
33
SamplerAddressMode address_mode) {
24
34
SamplerDescriptor sampler_desc;
@@ -38,6 +48,12 @@ void BindVertices(Command& cmd,
38
48
cmd.BindVertices (vtx_builder.CreateVertexBuffer (host_buffer));
39
49
}
40
50
51
+ Matrix MakeAnchorScale (const Point& anchor, Vector2 scale) {
52
+ return Matrix::MakeTranslation ({anchor.x , anchor.y , 0 }) *
53
+ Matrix::MakeScale (scale) *
54
+ Matrix::MakeTranslation ({-anchor.x , -anchor.y , 0 });
55
+ }
56
+
41
57
void SetTileMode (SamplerDescriptor* descriptor,
42
58
const ContentContext& renderer,
43
59
Entity::TileMode tile_mode) {
@@ -71,6 +87,7 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
71
87
const SamplerDescriptor& sampler_descriptor,
72
88
const Quad& uvs,
73
89
const ISize& subpass_size,
90
+ const Vector2 padding,
74
91
Entity::TileMode tile_mode) {
75
92
ContentContext::SubpassCallback subpass_callback =
76
93
[&](const ContentContext& renderer, RenderPass& pass) {
@@ -87,13 +104,23 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
87
104
frame_info.texture_sampler_y_coord_scale = 1.0 ;
88
105
frame_info.alpha = 1.0 ;
89
106
90
- BindVertices<TextureFillVertexShader>(cmd, host_buffer,
91
- {
92
- {Point (0 , 0 ), uvs[0 ]},
93
- {Point (1 , 0 ), uvs[1 ]},
94
- {Point (0 , 1 ), uvs[2 ]},
95
- {Point (1 , 1 ), uvs[3 ]},
96
- });
107
+ // Insert transparent gutter around the downsampled image so the blur
108
+ // creates a halo effect. This compensates for when the expanded clip
109
+ // region can't give us the full gutter we want.
110
+ Vector2 texture_size = Vector2 (input_texture->GetSize ());
111
+ Quad guttered_uvs =
112
+ MakeAnchorScale ({0.5 , 0.5 },
113
+ (texture_size + padding * 2 ) / texture_size)
114
+ .Transform (uvs);
115
+
116
+ BindVertices<TextureFillVertexShader>(
117
+ cmd, host_buffer,
118
+ {
119
+ {Point (0 , 0 ), guttered_uvs[0 ]},
120
+ {Point (1 , 0 ), guttered_uvs[1 ]},
121
+ {Point (0 , 1 ), guttered_uvs[2 ]},
122
+ {Point (1 , 1 ), guttered_uvs[3 ]},
123
+ });
97
124
98
125
SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
99
126
SetTileMode (&linear_sampler_descriptor, renderer, tile_mode);
@@ -243,17 +270,17 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
243
270
Vector2 blur_radius = {CalculateBlurRadius (scaled_sigma.x ),
244
271
CalculateBlurRadius (scaled_sigma.y )};
245
272
Vector2 padding (ceil (blur_radius.x ), ceil (blur_radius.y ));
246
- Vector2 local_padding =
247
- (entity.GetTransform ().Basis () * effect_transform.Basis () * padding)
248
- .Abs ();
249
273
250
274
// Apply as much of the desired padding as possible from the source. This may
251
275
// be ignored so must be accounted for in the downsample pass by adding a
252
276
// transparent gutter.
253
- std::optional<Rect> expanded_coverage_hint;
254
- if (coverage_hint.has_value ()) {
255
- expanded_coverage_hint = coverage_hint->Expand (local_padding);
256
- }
277
+ std::optional<Rect> expanded_coverage_hint = ExpandCoverageHint (
278
+ coverage_hint, entity.GetTransform () * effect_transform, padding);
279
+ // TODO(gaaclarke): How much of the gutter is thrown away can be used to
280
+ // adjust the padding that is added in the downsample pass.
281
+ // For example, if we get all the padding we requested from
282
+ // the expanded_coverage_hint, there is no need to add a
283
+ // transparent gutter.
257
284
258
285
std::optional<Snapshot> input_snapshot =
259
286
inputs[0 ]->GetSnapshot (" GaussianBlur" , renderer, entity,
@@ -273,28 +300,21 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
273
300
// gutter from the expanded_coverage_hint, we can skip the downsample pass.
274
301
// pass.
275
302
Vector2 downsample_scalar (desired_scalar, desired_scalar);
276
- Rect source_rect = Rect::MakeSize (input_snapshot->texture ->GetSize ());
277
- Rect source_rect_padded = source_rect.Expand (padding);
278
- Matrix padding_snapshot_adjustment = Matrix::MakeTranslation (-padding);
279
- // TODO(gaaclarke): The padding could be removed if we know it's not needed or
280
- // resized to account for the expanded_clip_coverage. There doesn't appear
281
- // to be the math to make those calculations though. The following
282
- // optimization works, but causes a shimmer as a result of
283
- // https://github.com/flutter/flutter/issues/140193 so it isn't applied.
284
- //
285
- // !input_snapshot->GetCoverage()->Expand(-local_padding)
286
- // .Contains(coverage_hint.value()))
287
- Vector2 downsampled_size = source_rect_padded.size * downsample_scalar;
303
+ Vector2 padded_size =
304
+ Vector2 (input_snapshot->texture ->GetSize ()) + 2.0 * padding;
305
+ Vector2 downsampled_size = padded_size * downsample_scalar;
306
+ // TODO(gaaclarke): I don't think we are correctly handling this fractional
307
+ // amount we are throwing away.
288
308
ISize subpass_size =
289
309
ISize (round (downsampled_size.x ), round (downsampled_size.y ));
290
- Vector2 effective_scalar = Vector2 ( subpass_size) / source_rect_padded. size ;
310
+ Vector2 effective_scalar = subpass_size / padded_size ;
291
311
292
- Quad uvs = CalculateUVs (inputs[ 0 ], entity, source_rect_padded,
293
- input_snapshot->texture ->GetSize ());
312
+ Quad uvs =
313
+ CalculateUVs (inputs[ 0 ], entity, input_snapshot->texture ->GetSize ());
294
314
295
315
std::shared_ptr<Texture> pass1_out_texture = MakeDownsampleSubpass (
296
316
renderer, input_snapshot->texture , input_snapshot->sampler_descriptor ,
297
- uvs, subpass_size, tile_mode_);
317
+ uvs, subpass_size, padding, tile_mode_);
298
318
299
319
Vector2 pass1_pixel_size = 1.0 / Vector2 (pass1_out_texture->GetSize ());
300
320
@@ -323,12 +343,13 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
323
343
MinMagFilter::kLinear , SamplerAddressMode::kClampToEdge );
324
344
325
345
return Entity::FromSnapshot (
326
- Snapshot{.texture = pass3_out_texture,
327
- .transform = input_snapshot->transform *
328
- padding_snapshot_adjustment *
329
- Matrix::MakeScale (1 / effective_scalar),
330
- .sampler_descriptor = sampler_desc,
331
- .opacity = input_snapshot->opacity },
346
+ Snapshot{
347
+ .texture = pass3_out_texture,
348
+ .transform = input_snapshot->transform *
349
+ Matrix::MakeTranslation ({-padding.x , -padding.y , 0 }) *
350
+ Matrix::MakeScale (1 / effective_scalar),
351
+ .sampler_descriptor = sampler_desc,
352
+ .opacity = input_snapshot->opacity },
332
353
entity.GetBlendMode (), entity.GetClipDepth ());
333
354
}
334
355
@@ -339,10 +360,11 @@ Scalar GaussianBlurFilterContents::CalculateBlurRadius(Scalar sigma) {
339
360
Quad GaussianBlurFilterContents::CalculateUVs (
340
361
const std::shared_ptr<FilterInput>& filter_input,
341
362
const Entity& entity,
342
- const Rect& source_rect,
343
363
const ISize& texture_size) {
344
364
Matrix input_transform = filter_input->GetLocalTransform (entity);
345
- Quad coverage_quad = source_rect.GetTransformedPoints (input_transform);
365
+ Rect snapshot_rect =
366
+ Rect::MakeXYWH (0 , 0 , texture_size.width , texture_size.height );
367
+ Quad coverage_quad = snapshot_rect.GetTransformedPoints (input_transform);
346
368
347
369
Matrix uv_transform = Matrix::MakeScale (
348
370
{1 .0f / texture_size.width , 1 .0f / texture_size.height , 1 .0f });
0 commit comments