Add wall frame overlay with transparent PNG window

This commit is contained in:
2026-09-08 17:56:50 +07:00
parent 669224fa39
commit a1769e8c84
5 changed files with 218 additions and 22 deletions
+52
View File
@@ -10,6 +10,7 @@ export const FRAMES: FrameDefinition[] = [
{ id: 'none', name: 'NO FRAME' },
{ id: 'classic-white', name: 'CLASSIC BORDER' },
{ id: 'polaroid', name: 'RETRO POLAROID' },
{ id: 'wallframe', name: 'WALL FRAME' },
];
// Real Polaroid print proportions (88 x 107 mm card, 79 x 79 mm image print).
@@ -30,6 +31,57 @@ export interface PolaroidLayout {
window: { x: number; y: number; w: number; h: number };
}
// Wall frame: the PNG artwork lives at the project root (wallframe.png,
// 4000x3117 RGBA, transparent window 675..3322 x 675..2441). Only the geometry
// lives here; the bitmap itself is drawn by the caller (Viewfinder overlay /
// export engine). The window is FIXED 3:2 landscape, so the photo is
// cover-cropped (center) into it — the mat of a real frame hides the print
// edges. Fractions are relative to the full PNG.
export const WALLFRAME_IMG_W = 4000;
export const WALLFRAME_IMG_H = 3117;
export const WALLFRAME = {
winLeft: 675 / 4000, // 0.16875
winTop: 675 / 3117, // ≈ 0.21655
winW: 2648 / 4000, // 0.662
winH: 1767 / 3117, // ≈ 0.56689
} as const;
export interface WallframeLayout {
frame: { x: number; y: number; w: number; h: number };
window: { x: number; y: number; w: number; h: number };
}
// Geometry of the wall-frame PNG inside an arbitrary container. The PNG is
// contain-fitted ('contain', centered, letterboxed) for the live viewfinder, or
// stretched to fill exactly ('fill', export canvas already has PNG aspect).
export function wallframeLayout(
contW: number,
contH: number,
mode: 'contain' | 'fill' = 'contain'
): WallframeLayout {
let frameW: number;
let frameH: number;
if (mode === 'fill') {
frameW = contW;
frameH = contH;
} else {
const s = Math.min(contW / WALLFRAME_IMG_W, contH / WALLFRAME_IMG_H);
frameW = WALLFRAME_IMG_W * s;
frameH = WALLFRAME_IMG_H * s;
}
const fx = (contW - frameW) / 2;
const fy = (contH - frameH) / 2;
return {
frame: { x: fx, y: fy, w: frameW, h: frameH },
window: {
x: fx + WALLFRAME.winLeft * frameW,
y: fy + WALLFRAME.winTop * frameH,
w: WALLFRAME.winW * frameW,
h: WALLFRAME.winH * frameH,
},
};
}
// Geometry of a retro-polaroid card inside an arbitrary container. The photo is
// NEVER cropped: the image window keeps the photo aspect ratio (photoW/photoH)
// and the card grows/shrinks around it, so the whole picture always fits the