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:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user