fix(gate-b): drawMaskOverlay — cutout on checkerboard, no green overlay (#55)

Replace green overlay rendering with cutout-on-checkerboard preview:
- Slab pixels show actual photo RGB with mask-based alpha
- Removed background area shows 10px checkerboard (224/192 gray)
- Knob debounce from a1d4a96 preserved unchanged

Update Playwright test to assert checkerboard + slab pixels
visible, not green overlay avgAlpha.
This commit is contained in:
grokbot_ty
2026-09-08 00:30:24 +00:00
parent 3496670045
commit 83171fab6e
2 changed files with 92 additions and 55 deletions
+32 -13
View File
@@ -337,21 +337,40 @@
img.onload = () => {
canvas.width = photo.rgba!.width;
canvas.height = photo.rgba!.height;
ctx.drawImage(img, 0, 0);
const imgData = ctx.createImageData(canvas.width, canvas.height);
const w = canvas.width;
const h = canvas.height;
const srcData = photo.rgba!.data;
const mask = photo.maskResult!.mask;
// Translucent light green overlay — photo remains clearly visible.
// Max alpha 80 (~31% opacity) on full-slab pixels; feathered edge blends smoothly.
for (let i = 0; i < mask.length; i++) {
const alpha = Math.round((mask[i]! / 255) * 80);
const pixelIdx = i * 4;
imgData.data[pixelIdx] = 60;
imgData.data[pixelIdx + 1] = 220;
imgData.data[pixelIdx + 2] = 60;
imgData.data[pixelIdx + 3] = alpha;
const CHECKER_SIZE = 10;
// Build ImageData: slab pixels pass through; background = checkerboard.
const outData = ctx.createImageData(w, h);
const out = outData.data;
for (let i = 0; i < w * h; i++) {
const srcIdx = i * 4;
const outIdx = i * 4;
const maskVal = mask[i]!;
if (maskVal > 0) {
// Slab pixel — show actual photo RGB, alpha from mask.
out[outIdx] = srcData[srcIdx]!;
out[outIdx + 1] = srcData[srcIdx + 1]!;
out[outIdx + 2] = srcData[srcIdx + 2]!;
out[outIdx + 3] = maskVal;
} else {
// Background removed — checkerboard.
const px = i % w;
const py = Math.floor(i / w);
const isLight = (Math.floor(px / CHECKER_SIZE) + Math.floor(py / CHECKER_SIZE)) % 2 === 0;
const v = isLight ? 224 : 192;
out[outIdx] = v;
out[outIdx + 1] = v;
out[outIdx + 2] = v;
out[outIdx + 3] = 255;
}
}
ctx.putImageData(imgData, 0, 0);
ctx.putImageData(outData, 0, 0);
URL.revokeObjectURL(img.src);
};
img.src = URL.createObjectURL(photo.blob);
@@ -520,7 +539,7 @@
{#if state.step === 'mask'}
<div class="step-content mask-step">
<h2>Mask & Knobs</h2>
<p class="instruction">Adjust knobs to refine the mask. Green overlay shows the detected slab.</p>
<p class="instruction">Adjust knobs to refine the mask. Detected slab shows in foreground; checkerboard = removed background.</p>
<div class="canvas-row">
<canvas class="mask-canvas" bind:this={maskCanvas}></canvas>
<div class="knobs-panel">