Repository: flutter
Issue: Evaluate ImageFilter.blur tileMode behaviors
Labels: engine,P3,team-engine,triaged-engine

The engine PR https://github.com/flutter/engine/pull/55552 changed the default behavior of the ImageFilter.blur as will be documented in the next release notes via https://github.com/flutter/website/pull/11338

While implementing the new defaults, we observed some behavioral issues that suggest that we might want to change the handling of the tileMode even further before the next release.

### Strange kaleidoscope

The main issue is that for rendered shapes, the tile modes other than `decal` don't seem to exhibit desirable behavior. The most surprising behavior happens with the old default tile mode of `clamp` as can be seen in the following images and video. Note that Impeller currently produces output for `clamp` that is identical to what it produces for `decal` and that behavior is consistent with the description of the `decal` mode. Skia attempts to produce output that resembles the description for `clamp` but has a number of inconsistent anomalies. The following images were generated by a Flutter app that draws a round rect with an ImageFilter.blur in the Paint object and then various properties of that rendering can be modified with a slider.

<details>
<summary>Skia static sample</summary>
<img width="777" alt="skia blurred rendering gallery" src="https://github.com/user-attachments/assets/bf8754a5-0dd9-4847-aabf-cd3d1aeab263">
</details>

<details>
<summary>Impeller static sample</summary>
<img width="777" alt="impeller blurred rendering gallery" src="https://github.com/user-attachments/assets/1555bc9a-6b67-4f06-a1e3-1f0f43abf631">
</details>

The primary differences between these 2 outputs are that Impeller treats `clamp` mode as if it were `decal` and the reflections and repeats at the edges for `mirror` and `repeated` mode are more crowded for Skia than for Impeller. This is likely due to Impeller rounding out the temporary surface used to render the shape before the ImageFilter is applied causing it to contain a border of transparent pixels. The `clamp` mode would then clamp to these transparent edge pixels and look like `decal` mode while the `mirror` and `repeated` modes would have the multiple copies of the source image spread out more than Skia.

Another issue with `clamp` mode is that it is hard to get right when the shape being filtered has some unpredictable edge registrations (how the pixels at the edges of the shape line up with actual pixels in the output surface). This can make the edge pixels sampled for the `clamp` mode contain pixels of varying opacity depending on whether the shape barely filled them or filled them nearly completely. This would be somewhat surprising as can be seen in the static samples above if the shapes occupied a static space on the screen, but if the shapes are moved, scaled, or resized, their edge registrations could vary widely as they are repositioned as can be seen in the following video. This video is showing the Skia output as the Impeller implementation does not attempt to preserve the edges of the shape within the temporary surface.

<details>
<summary>Skia animated sample</summary>

[Skia rendering gallery animated (trimmed).mov](https://github.com/user-attachments/assets/ee18604a-1c9d-4734-9aa3-6e6867225d30)
</details>

These behaviors suggest the following potential changes in behavior for the new defaults.
- Change the shape rendering methods (and the application of a blur filter to saveLayer content as well) to always use a `decal` mode so that the strange and inconsistent spikes for the `clamp` mode are avoided and the partial, difficult to control kaleidoscope effect for `mirror` and `repeated` modes are also avoided
  - The concept/justification for this behavior could simply be that we don't implement the other modes for this operation, or
  - that the operation itself represents drawing the shape on an infinitely large blank surface and then filtering the surface - which essentially makes all modes appear the same as `decal` mode.
- Other uses deal with source information that is complete and comes with naturally defined "edge" pixels that are not inferred from the pixel registration of a transformed potentially irregular shape, to wit:
  - The drawImage variants (including drawAtlas), which take their source pixels from a well defined image, would still provide support for all 4 modes and a default of `clamp` as already implemented in https://github.com/flutter/engine/pull/55552
  - The backdrop handling for BackdropFilter with a blur ImageFilter, which takes its source from a rendered framebuffer with well defined edges, will still provide support for all 4 modes and a default of `mirror` as we determined prior to making the change.