@@ -19,6 +19,16 @@ using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
1919
2020namespace {
2121
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+
2232SamplerDescriptor MakeSamplerDescriptor (MinMagFilter filter,
2333 SamplerAddressMode address_mode) {
2434 SamplerDescriptor sampler_desc;
@@ -38,6 +48,12 @@ void BindVertices(Command& cmd,
3848 cmd.BindVertices (vtx_builder.CreateVertexBuffer (host_buffer));
3949}
4050
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+
4157void SetTileMode (SamplerDescriptor* descriptor,
4258 const ContentContext& renderer,
4359 Entity::TileMode tile_mode) {
@@ -71,6 +87,7 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
7187 const SamplerDescriptor& sampler_descriptor,
7288 const Quad& uvs,
7389 const ISize& subpass_size,
90+ const Vector2 padding,
7491 Entity::TileMode tile_mode) {
7592 ContentContext::SubpassCallback subpass_callback =
7693 [&](const ContentContext& renderer, RenderPass& pass) {
@@ -87,13 +104,23 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
87104 frame_info.texture_sampler_y_coord_scale = 1.0 ;
88105 frame_info.alpha = 1.0 ;
89106
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+ });
97124
98125 SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
99126 SetTileMode (&linear_sampler_descriptor, renderer, tile_mode);
@@ -243,17 +270,17 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
243270 Vector2 blur_radius = {CalculateBlurRadius (scaled_sigma.x ),
244271 CalculateBlurRadius (scaled_sigma.y )};
245272 Vector2 padding (ceil (blur_radius.x ), ceil (blur_radius.y ));
246- Vector2 local_padding =
247- (entity.GetTransform ().Basis () * effect_transform.Basis () * padding)
248- .Abs ();
249273
250274 // Apply as much of the desired padding as possible from the source. This may
251275 // be ignored so must be accounted for in the downsample pass by adding a
252276 // 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.
257284
258285 std::optional<Snapshot> input_snapshot =
259286 inputs[0 ]->GetSnapshot (" GaussianBlur" , renderer, entity,
@@ -273,28 +300,21 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
273300 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
274301 // pass.
275302 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.
288308 ISize subpass_size =
289309 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 ;
291311
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 ());
294314
295315 std::shared_ptr<Texture> pass1_out_texture = MakeDownsampleSubpass (
296316 renderer, input_snapshot->texture , input_snapshot->sampler_descriptor ,
297- uvs, subpass_size, tile_mode_);
317+ uvs, subpass_size, padding, tile_mode_);
298318
299319 Vector2 pass1_pixel_size = 1.0 / Vector2 (pass1_out_texture->GetSize ());
300320
@@ -323,12 +343,13 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
323343 MinMagFilter::kLinear , SamplerAddressMode::kClampToEdge );
324344
325345 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 },
332353 entity.GetBlendMode (), entity.GetClipDepth ());
333354}
334355
@@ -339,10 +360,11 @@ Scalar GaussianBlurFilterContents::CalculateBlurRadius(Scalar sigma) {
339360Quad GaussianBlurFilterContents::CalculateUVs (
340361 const std::shared_ptr<FilterInput>& filter_input,
341362 const Entity& entity,
342- const Rect& source_rect,
343363 const ISize& texture_size) {
344364 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);
346368
347369 Matrix uv_transform = Matrix::MakeScale (
348370 {1 .0f / texture_size.width , 1 .0f / texture_size.height , 1 .0f });
0 commit comments