fix(studio): the heal's fill is drawn at the surface's own corner, not at the frame's, so a repair no longer wears the colour of the frame's far side

This commit is contained in:
2026-10-12 20:23:32 +07:00
parent cf6b7f5b9f
commit 2d9d7ef644
2 changed files with 83 additions and 9 deletions
+14 -4
View File
@@ -1410,19 +1410,29 @@ export async function renderPhoto(input: RenderInput): Promise<RenderResult | nu
const boxBottom = Math.min(height, Math.ceil(Math.max(...ys) + widest + pad));
const fillSurface = own(createSurface(boxRight - boxLeft, boxBottom - boxTop));
const fillCanvas = fillSurface.getCanvas();
// The box's own corner is the surface's, and both rects are the same one:
// a 1:1 copy at an offset, which is the draw this Skia filters correctly.
// What the box names is drawn at the surface's own corner, 1:1 — the source
// rect is the frame's, the destination is the surface's, and the two are a
// whole number of pixels apart, which is the draw this Skia filters
// correctly. Drawn at the frame's own coordinates instead — both rects the
// box — the surface's left and top strips are left empty and its far side is
// clipped, and the composite that reads it back with
// `imageShaderChildAt(fillImage, boxLeft, boxTop)` then lands on the wrong
// pixels: the repaired patch takes the frame's colour from a boxLeft,boxTop
// up and to the left of where it is painted — a spot healed in the middle of
// the frame came out with the tone of the frame's top-left corner, a shade
// off on a smooth sky and the wrong colour outright on anything with shape.
const box = Skia.XYWHRect(boxLeft, boxTop, boxRight - boxLeft, boxBottom - boxTop);
const onto = Skia.XYWHRect(0, 0, boxRight - boxLeft, boxBottom - boxTop);
if (cutImage) {
for (let k = levelCount; k >= 1; k--) {
const sigma = HEAL_FILL_BLUR * widest * 2 ** (k - 1);
const blurPaint = own(Skia.Paint());
blurPaint.setImageFilter(own(Skia.ImageFilter.MakeBlur(sigma, sigma, Skia.TileMode.Clamp, null)));
fillCanvas.drawImageRect(cutImage, box, box, blurPaint);
fillCanvas.drawImageRect(cutImage, box, onto, blurPaint);
}
// The cut itself, unblurred, on top: the pixels the brush did not touch
// are the photo exactly, not a blur of it.
fillCanvas.drawImageRect(cutImage, box, box, own(Skia.Paint()));
fillCanvas.drawImageRect(cutImage, box, onto, own(Skia.Paint()));
}
flush(fillSurface);
const fillImage = own(fillSurface.makeImageSnapshot());