Let the GPS watermark be placed, sized and hung landscape

Three WATERMARK/FRAME behaviours that share the stamp geometry:

* GPS mark: drag it anywhere on the frame/photo area and pinch it bigger or
  smaller. The 0..1 anchor plus the size multiplier ride along in the export
  options, so a framed file (whose canvas IS the card/frame) burns the mark in
  at exactly the fraction the preview showed.
* The photo's own pinch zoom stays available while the WATERMARK editor is
  closed, so a two-finger zoom is never swallowed by the mark while its panel
  is open.
* WALL FRAME gains a WALL PORTRAIT / WALL LANDSCAPE chip: the artwork hangs
  in its own 4000x3117 orientation instead of the default 90-degree rotation.
This commit is contained in:
2026-09-10 16:35:35 +07:00
parent 47e424789d
commit 11ed786758
6 changed files with 313 additions and 116 deletions
+32 -12
View File
@@ -53,6 +53,17 @@ export const WALLFRAME_WIN = {
w: 1766 / 3117, // ≈ 0.56657
h: 2647 / 4000, // 0.66175
} as const;
// Landscape hang: the PNG's OWN orientation, so nothing is rotated — the
// canvas is the artwork (4000x3117) and the opening is the source window as
// fractions of it. Same 3:2 mat opening, hung the other way round.
export const WALLFRAME_LAND_W = WALLFRAME_PNG_W; // 4000
export const WALLFRAME_LAND_H = WALLFRAME_PNG_H; // 3117
export const WALLFRAME_LAND_WIN = {
left: 675 / WALLFRAME_PNG_W, // 0.16875
top: 675 / WALLFRAME_PNG_H, // ≈ 0.21688
w: 2647 / WALLFRAME_PNG_W, // 0.66175
h: 1766 / WALLFRAME_PNG_H, // ≈ 0.56657
} as const;
export interface WallframeLayout {
frame: { x: number; y: number; w: number; h: number };
@@ -63,36 +74,45 @@ export interface WallframeLayout {
drawMatrix: number[];
}
// Geometry of the ROTATED wall-frame inside an arbitrary container. The frame
// is contain-fitted ('contain', centered, letterboxed) for the live viewfinder,
// Geometry of the wall-frame inside an arbitrary container. The frame is
// contain-fitted ('contain', centered, letterboxed) for the live viewfinder,
// or fills the container exactly ('fill', export canvas already at frame size).
// `landscape` hangs the artwork the other way round: same PNG, no rotation.
export function wallframeLayout(
contW: number,
contH: number,
mode: 'contain' | 'fill' = 'contain'
mode: 'contain' | 'fill' = 'contain',
landscape = false
): WallframeLayout {
const FW = landscape ? WALLFRAME_LAND_W : WALLFRAME_W;
const FH = landscape ? WALLFRAME_LAND_H : WALLFRAME_H;
const WIN = landscape ? WALLFRAME_LAND_WIN : WALLFRAME_WIN;
let frameW: number;
let frameH: number;
if (mode === 'fill') {
frameW = contW;
frameH = contH;
} else {
const s = Math.min(contW / WALLFRAME_W, contH / WALLFRAME_H);
frameW = WALLFRAME_W * s;
frameH = WALLFRAME_H * s;
const s = Math.min(contW / FW, contH / FH);
frameW = FW * s;
frameH = FH * s;
}
const fx = (contW - frameW) / 2;
const fy = (contH - frameH) / 2;
const scale = frameW / WALLFRAME_W; // == frameH / WALLFRAME_H
const scale = frameW / FW; // == frameH / FH
return {
frame: { x: fx, y: fy, w: frameW, h: frameH },
window: {
x: fx + WALLFRAME_WIN.left * frameW,
y: fy + WALLFRAME_WIN.top * frameH,
w: WALLFRAME_WIN.w * frameW,
h: WALLFRAME_WIN.h * frameH,
x: fx + WIN.left * frameW,
y: fy + WIN.top * frameH,
w: WIN.w * frameW,
h: WIN.h * frameH,
},
drawMatrix: [0, -scale, fx + frameW, scale, 0, fy, 0, 0, 1],
// Portrait: the source PNG must be turned 90° CW onto the canvas.
// Landscape: it already sits the right way up — scale and centre it.
drawMatrix: landscape
? [scale, 0, fx, 0, scale, fy, 0, 0, 1]
: [0, -scale, fx + frameW, scale, 0, fy, 0, 0, 1],
};
}