Code Through Coffee

Fixing Energy Loss in IIR-Blurred UI Shadows: Why Renormalisation Matters

IIR blurs make a pretty significant difference in shadow quality compared to integral blurs or even StackBlur approaches.

Here's a comparison of the same shadow parameters (drop + inset) for the same mask. The top example is using the fast path (simple integral blur), the bottom one is using IIR (Deriche / Young‑van Vliet). Ignore the aliasing, the source mask (blue) is rendered just for debugging purposes from a binary bitmap:

iir compare

But there's a problem with using IIR for UI shadows, where elements are often just rectangles: energy loss.

Let's remove energy loss compensation (renormalisation) from our shadow engine and render an inset on a simple rect mask. Here it is compared against the other versions:

renorm2

The recursive (Deriche / Young–van Vliet) Gaussian blur we use is not energy‑preserving near hard edges: the blur kernel samples a patch that extends beyond the polygon boundary, where the source signal is 0.

The further a pixel is from the interior, the more “energy” leaks out of the kernel footprint. For an inset shadow, this shows up as the dark‑corner / light‑corner artefact: pixels near the top‑left edge are blended with more zeros than those processed later, so they end up darker once converted to colour (because α is lower).

To correct this, we need to:

In the limit where a pixel’s kernel is entirely outside the polygon, the factor is zero; we clamp it to a small ε to avoid divide‑by‑zero and keep those pixels at 0 α, which is correct for an inset shadow.

Drop shadows do not need this renormalisation because their kernel is supposed to fade to transparent as it moves away from the shape.

Cheers ☕

#graphics #til