Fix radial, angular, and diamond gradient rendering

Use the full gradientTransform affine matrix via CanvasKit's localMatrix
parameter instead of manually extracting center and radius. The previous
approach ignored rotation and aspect ratio, producing circular gradients
where Figma specifies rotated ellipses.

Define gradients in Figma's unit gradient space (center 0.5,0.5, radius
0.5) and let the localMatrix (scale(w,h) * gradientTransform) handle the
transform to pixel coordinates. Also merge the GRADIENT_DIAMOND branch
with GRADIENT_RADIAL since they use the same shader.
This commit is contained in:
Danila Poyarkov 2026-03-13 19:05:48 +03:00
parent ef0b45f0c1
commit 4dbb583218

View file

@ -101,40 +101,35 @@ export function applyGradientFill(r: SkiaRenderer, fill: Fill, node: SceneNode):
r.ck.TileMode.Clamp
)
r.fillPaint.setShader(shader)
} else if (fill.type === 'GRADIENT_RADIAL') {
const cx = t.m02 * w
const cy = t.m12 * h
const radius = Math.sqrt(t.m00 * t.m00 + t.m10 * t.m10) * Math.max(w, h)
const shader = r.ck.Shader.MakeRadialGradient(
[cx, cy],
radius,
colors,
positions,
r.ck.TileMode.Clamp
} else if (fill.type === 'GRADIENT_RADIAL' || fill.type === 'GRADIENT_DIAMOND') {
// Figma's gradientTransform maps gradient space (center 0.5,0.5, radius 0.5)
// to the node's normalized [0,1] coordinate space. The full local matrix
// converts to pixel coordinates: scale(w, h) * gradientTransform.
const localMatrix = r.ck.Matrix.multiply(
r.ck.Matrix.scaled(w, h),
[t.m00, t.m01, t.m02, t.m10, t.m11, t.m12, 0, 0, 1]
)
r.fillPaint.setShader(shader)
} else if (fill.type === 'GRADIENT_ANGULAR') {
const cx = t.m02 * w
const cy = t.m12 * h
const shader = r.ck.Shader.MakeSweepGradient(
cx,
cy,
const shader = r.ck.Shader.MakeRadialGradient(
[0.5, 0.5],
0.5,
colors,
positions,
r.ck.TileMode.Clamp,
undefined
localMatrix
)
r.fillPaint.setShader(shader)
} else if (fill.type === 'GRADIENT_DIAMOND') {
const cx = t.m02 * w
const cy = t.m12 * h
const radius = Math.sqrt(t.m00 * t.m00 + t.m10 * t.m10) * Math.max(w, h)
const shader = r.ck.Shader.MakeRadialGradient(
[cx, cy],
radius,
} else if (fill.type === 'GRADIENT_ANGULAR') {
const localMatrix = r.ck.Matrix.multiply(
r.ck.Matrix.scaled(w, h),
[t.m00, t.m01, t.m02, t.m10, t.m11, t.m12, 0, 0, 1]
)
const shader = r.ck.Shader.MakeSweepGradient(
0.5,
0.5,
colors,
positions,
r.ck.TileMode.Clamp
r.ck.TileMode.Clamp,
localMatrix
)
r.fillPaint.setShader(shader)
}