@@ -156,6 +156,64 @@ std::optional<Snapshot> GetSnapshot(const std::shared_ptr<FilterInput>& input,
156156 return input_snapshot;
157157}
158158
159+ // / Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
160+ // / be returned when `rect` == `reference`.
161+ Rect MakeReferenceUVs (const Rect& reference, const Rect& rect) {
162+ Rect result = Rect::MakeOriginSize (rect.GetOrigin () - reference.GetOrigin (),
163+ rect.GetSize ());
164+ return result.Scale (1 .0f / Vector2 (reference.GetSize ()));
165+ }
166+
167+ Quad CalculateSnapshotUVs (
168+ const Snapshot& input_snapshot,
169+ const std::optional<Rect>& source_expanded_coverage_hint) {
170+ std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage ();
171+ Quad blur_uvs = {Point (0 , 0 ), Point (1 , 0 ), Point (0 , 1 ), Point (1 , 1 )};
172+ FML_DCHECK (input_snapshot.transform .IsTranslationScaleOnly ());
173+ if (source_expanded_coverage_hint.has_value () &&
174+ input_snapshot_coverage.has_value ()) {
175+ // Only process the uvs where the blur is happening, not the whole texture.
176+ std::optional<Rect> uvs =
177+ MakeReferenceUVs (input_snapshot_coverage.value (),
178+ source_expanded_coverage_hint.value ())
179+ .Intersection (Rect::MakeSize (Size (1 , 1 )));
180+ FML_DCHECK (uvs.has_value ());
181+ if (uvs.has_value ()) {
182+ blur_uvs[0 ] = uvs->GetLeftTop ();
183+ blur_uvs[1 ] = uvs->GetRightTop ();
184+ blur_uvs[2 ] = uvs->GetLeftBottom ();
185+ blur_uvs[3 ] = uvs->GetRightBottom ();
186+ }
187+ }
188+ return blur_uvs;
189+ }
190+
191+ Scalar CeilToDivisible (Scalar val, Scalar divisor) {
192+ if (divisor == 0 .0f ) {
193+ return val;
194+ }
195+
196+ Scalar remainder = fmod (val, divisor);
197+ if (remainder != 0 .0f ) {
198+ return val + (divisor - remainder);
199+ } else {
200+ return val;
201+ }
202+ }
203+
204+ Scalar FloorToDivisible (Scalar val, Scalar divisor) {
205+ if (divisor == 0 .0f ) {
206+ return val;
207+ }
208+
209+ Scalar remainder = fmod (val, divisor);
210+ if (remainder != 0 .0f ) {
211+ return val - remainder;
212+ } else {
213+ return val;
214+ }
215+ }
216+
159217struct DownsamplePassArgs {
160218 // / The output size of the down-sampling pass.
161219 ISize subpass_size;
@@ -166,13 +224,19 @@ struct DownsamplePassArgs {
166224 // / This isn't usually exactly as we'd calculate because it has to be rounded
167225 // / to integer boundaries for generating the texture for the output.
168226 Vector2 effective_scalar;
227+ // / Transforms from unrotated local space to position the output from the
228+ // / down-sample pass.
229+ // / This can differ if we request a coverage hint but it is rejected, as is
230+ // / the case with backdrop filters.
231+ Matrix transform;
169232};
170233
171234// / Calculates info required for the down-sampling pass.
172235DownsamplePassArgs CalculateDownsamplePassArgs (
173236 Vector2 scaled_sigma,
174237 Vector2 padding,
175- ISize input_snapshot_size,
238+ const Snapshot& input_snapshot,
239+ const std::optional<Rect>& source_expanded_coverage_hint,
176240 const std::shared_ptr<FilterInput>& input,
177241 const Entity& snapshot_entity) {
178242 Scalar desired_scalar =
@@ -182,8 +246,6 @@ DownsamplePassArgs CalculateDownsamplePassArgs(
182246 // gutter from the expanded_coverage_hint, we can skip the downsample pass.
183247 // pass.
184248 Vector2 downsample_scalar (desired_scalar, desired_scalar);
185- Rect source_rect = Rect::MakeSize (input_snapshot_size);
186- Rect source_rect_padded = source_rect.Expand (padding);
187249 // TODO(gaaclarke): The padding could be removed if we know it's not needed or
188250 // resized to account for the expanded_clip_coverage. There doesn't appear
189251 // to be the math to make those calculations though. The following
@@ -192,19 +254,68 @@ DownsamplePassArgs CalculateDownsamplePassArgs(
192254 //
193255 // !input_snapshot->GetCoverage()->Expand(-local_padding)
194256 // .Contains(coverage_hint.value()))
195- Vector2 downsampled_size = source_rect_padded.GetSize () * downsample_scalar;
196- ISize subpass_size =
197- ISize (round (downsampled_size.x ), round (downsampled_size.y ));
198- Vector2 effective_scalar =
199- Vector2 (subpass_size) / source_rect_padded.GetSize ();
200-
201- Quad uvs = GaussianBlurFilterContents::CalculateUVs (
202- input, snapshot_entity, source_rect_padded, input_snapshot_size);
203- return {
204- .subpass_size = subpass_size,
205- .uvs = uvs,
206- .effective_scalar = effective_scalar,
207- };
257+
258+ std::optional<Rect> snapshot_coverage = input_snapshot.GetCoverage ();
259+ if (input_snapshot.transform .IsIdentity () &&
260+ source_expanded_coverage_hint.has_value () &&
261+ snapshot_coverage.has_value () &&
262+ snapshot_coverage->Contains (source_expanded_coverage_hint.value ())) {
263+ // If the snapshot's transform is the identity transform and we have
264+ // coverage hint that fits inside of the snapshots coverage that means the
265+ // coverage hint was ignored so we will trim out the area we are interested
266+ // in the down-sample pass. This usually means we have a backdrop image
267+ // filter.
268+ //
269+ // The region we cut out will be aligned with the down-sample divisor to
270+ // avoid pixel alignment problems that create shimmering.
271+ int32_t divisor = std::round (1 .0f / desired_scalar);
272+ Rect aligned_coverage_hint = Rect::MakeLTRB (
273+ FloorToDivisible (source_expanded_coverage_hint->GetLeft (), divisor),
274+ FloorToDivisible (source_expanded_coverage_hint->GetTop (), divisor),
275+ source_expanded_coverage_hint->GetRight (),
276+ source_expanded_coverage_hint->GetBottom ());
277+ aligned_coverage_hint = Rect::MakeXYWH (
278+ aligned_coverage_hint.GetX (), aligned_coverage_hint.GetY (),
279+ CeilToDivisible (aligned_coverage_hint.GetWidth (), divisor),
280+ CeilToDivisible (aligned_coverage_hint.GetHeight (), divisor));
281+ ISize source_size = ISize (aligned_coverage_hint.GetSize ().width ,
282+ aligned_coverage_hint.GetSize ().height );
283+ Vector2 downsampled_size = source_size * downsample_scalar;
284+ Scalar int_part;
285+ FML_DCHECK (std::modf (downsampled_size.x , &int_part) == 0 .0f );
286+ FML_DCHECK (std::modf (downsampled_size.y , &int_part) == 0 .0f );
287+ (void )int_part;
288+ ISize subpass_size = ISize (downsampled_size.x , downsampled_size.y );
289+ Vector2 effective_scalar = Vector2 (subpass_size) / source_size;
290+ FML_DCHECK (effective_scalar == downsample_scalar);
291+
292+ Quad uvs = CalculateSnapshotUVs (input_snapshot, aligned_coverage_hint);
293+ return {
294+ .subpass_size = subpass_size,
295+ .uvs = uvs,
296+ .effective_scalar = effective_scalar,
297+ .transform = Matrix::MakeTranslation (
298+ {aligned_coverage_hint.GetX (), aligned_coverage_hint.GetY (), 0 })};
299+ } else {
300+ // ////////////////////////////////////////////////////////////////////////////
301+ auto input_snapshot_size = input_snapshot.texture ->GetSize ();
302+ Rect source_rect = Rect::MakeSize (input_snapshot_size);
303+ Rect source_rect_padded = source_rect.Expand (padding);
304+ Vector2 downsampled_size = source_rect_padded.GetSize () * downsample_scalar;
305+ ISize subpass_size =
306+ ISize (round (downsampled_size.x ), round (downsampled_size.y ));
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 =
316+ input_snapshot.transform * Matrix::MakeTranslation (-padding),
317+ };
318+ }
208319}
209320
210321// / Makes a subpass that will render the scaled down input and add the
@@ -326,38 +437,6 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
326437 }
327438}
328439
329- // / Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
330- // / be returned when `rect` == `reference`.
331- Rect MakeReferenceUVs (const Rect& reference, const Rect& rect) {
332- Rect result = Rect::MakeOriginSize (rect.GetOrigin () - reference.GetOrigin (),
333- rect.GetSize ());
334- return result.Scale (1 .0f / Vector2 (reference.GetSize ()));
335- }
336-
337- Quad CalculateBlurUVs (
338- const Snapshot& input_snapshot,
339- const std::optional<Rect>& source_expanded_coverage_hint) {
340- std::optional<Rect> input_snapshot_coverage = input_snapshot.GetCoverage ();
341- Quad blur_uvs = {Point (0 , 0 ), Point (1 , 0 ), Point (0 , 1 ), Point (1 , 1 )};
342- FML_DCHECK (input_snapshot.transform .IsTranslationScaleOnly ());
343- if (source_expanded_coverage_hint.has_value () &&
344- input_snapshot_coverage.has_value ()) {
345- // Only process the uvs where the blur is happening, not the whole texture.
346- std::optional<Rect> uvs =
347- MakeReferenceUVs (input_snapshot_coverage.value (),
348- source_expanded_coverage_hint.value ())
349- .Intersection (Rect::MakeSize (Size (1 , 1 )));
350- FML_DCHECK (uvs.has_value ());
351- if (uvs.has_value ()) {
352- blur_uvs[0 ] = uvs->GetLeftTop ();
353- blur_uvs[1 ] = uvs->GetRightTop ();
354- blur_uvs[2 ] = uvs->GetLeftBottom ();
355- blur_uvs[3 ] = uvs->GetRightBottom ();
356- }
357- }
358- return blur_uvs;
359- }
360-
361440int ScaleBlurRadius (Scalar radius, Scalar scalar) {
362441 return static_cast <int >(std::round (radius * scalar));
363442}
@@ -597,8 +676,8 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
597676 }
598677
599678 DownsamplePassArgs downsample_pass_args = CalculateDownsamplePassArgs (
600- blur_info.scaled_sigma , blur_info.padding ,
601- input_snapshot-> texture -> GetSize () , inputs[0 ], snapshot_entity);
679+ blur_info.scaled_sigma , blur_info.padding , input_snapshot. value (),
680+ source_expanded_coverage_hint , inputs[0 ], snapshot_entity);
602681
603682 fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass (
604683 renderer, command_buffer, input_snapshot->texture ,
@@ -611,8 +690,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
611690 Vector2 pass1_pixel_size =
612691 1.0 / Vector2 (pass1_out.value ().GetRenderTargetTexture ()->GetSize ());
613692
614- Quad blur_uvs =
615- CalculateBlurUVs (input_snapshot.value (), source_expanded_coverage_hint);
693+ Quad blur_uvs = {Point (0 , 0 ), Point (1 , 0 ), Point (0 , 1 ), Point (1 , 1 )};
616694
617695 fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass (
618696 renderer, command_buffer, /* input_pass=*/ pass1_out.value (),
@@ -676,8 +754,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
676754 .transform =
677755 entity.GetTransform () * //
678756 Matrix::MakeScale (1 .f / blur_info.source_space_scalar ) * //
679- input_snapshot->transform * //
680- Matrix::MakeTranslation (-blur_info.padding ) * //
757+ downsample_pass_args.transform * //
681758 Matrix::MakeScale (1 / downsample_pass_args.effective_scalar ),
682759 .sampler_descriptor = sampler_desc,
683760 .opacity = input_snapshot->opacity },
0 commit comments