Treat FOREGROUND_BLUR as layer blur wrapping children

Figma's kiwi format uses FOREGROUND_BLUR for what the Plugin API calls
LAYER_BLUR — a blur that applies to the entire layer including children.

Previously FOREGROUND_BLUR was handled in the per-effect 'front' pass via
applyClippedBlur, which only blurred the node's own fills (drawn before
children). For nodes with no fills but with children (like groups),
the blur had no visible effect.

Now FOREGROUND_BLUR is handled alongside LAYER_BLUR in renderNode,
using a saveLayer that wraps both the node content and its children.
This fixes gradient blobs that should appear soft/blurred (e.g. the
decorative gradients in gold-preview.fig's Preview Thumbnail).
This commit is contained in:
Danila Poyarkov 2026-03-13 21:09:43 +03:00
parent 73668530b2
commit e954fdabff
2 changed files with 7 additions and 9 deletions

View file

@ -141,7 +141,9 @@ export function renderNode(
canvas.saveLayer(r.opacityPaint)
}
const layerBlur = node.effects.find((e) => e.visible && e.type === 'LAYER_BLUR')
const layerBlur = node.effects.find(
(e) => e.visible && (e.type === 'LAYER_BLUR' || e.type === 'FOREGROUND_BLUR')
)
if (layerBlur) {
r.effectLayerPaint.setImageFilter(r.getCachedBlur(layerBlur.radius / 2))
canvas.saveLayer(r.effectLayerPaint)
@ -443,10 +445,7 @@ export function renderEffects(
}
}
if (
(pass === 'behind' && effect.type === 'BACKGROUND_BLUR') ||
(pass === 'front' && effect.type === 'FOREGROUND_BLUR')
) {
if (pass === 'behind' && effect.type === 'BACKGROUND_BLUR') {
r.applyClippedBlur(canvas, node, rect, hasRadius, effect.radius / 2)
}

View file

@ -44,8 +44,8 @@ describe('Renderer handles all effect types', () => {
expect(rendererSource).toContain("effect.type === 'BACKGROUND_BLUR'")
})
test('handles FOREGROUND_BLUR', () => {
expect(rendererSource).toContain("effect.type === 'FOREGROUND_BLUR'")
test('handles FOREGROUND_BLUR as layer blur', () => {
expect(rendererSource).toContain("e.type === 'FOREGROUND_BLUR'")
})
})
@ -107,9 +107,8 @@ describe('Blur effects use saveLayer pattern', () => {
expect(layerBlurSection).toContain('saveLayer')
})
test('background and foreground blur use applyClippedBlur', () => {
test('background blur uses applyClippedBlur', () => {
expect(rendererSource).toContain("effect.type === 'BACKGROUND_BLUR'")
expect(rendererSource).toContain("effect.type === 'FOREGROUND_BLUR'")
expect(rendererSource).toContain('applyClippedBlur')
})