Pinch/drag library photo inside polaroid & wall frame windows with export parity
The framed windows (polaroid card / wall frame opening) now take the same pinch + double-tap gestures as plain library, clamped so the photo keeps covering the mat opening, and the visible sub-rectangle is handed to the export engine so the file matches the preview.
Wall frame export also stopped crashing: on Android release an image asset resolves to a drawable *name* ('wallframe') and is flagged downloaded, so Asset.downloadAsync short-circuited and readAsStringAsync threw 'Unsupported scheme for location wallframe'. Clearing that state lets the native module copy the drawable into the cache.
This commit is contained in:
@@ -38,6 +38,13 @@ export interface ExportOptions {
|
||||
// (BASELINE anchor + 0.35em drop — same formula as the viewfinder overlay,
|
||||
// so the export lands where the preview showed it). Amber mono, no icons.
|
||||
watermark?: { text: string; x: number; y: number } | null;
|
||||
// Library framed-window photo reposition (Viewfinder.getFrameWindowZoom):
|
||||
// after pinch/drag inside a polaroid card window or a wall-frame opening,
|
||||
// s = scale and u/v = the fraction of the photo's cover plane sitting at the
|
||||
// WINDOW CENTER. The export draws the source sub-rectangle that stays
|
||||
// visible through the opening, so the file matches the preview. Null /
|
||||
// undefined = centered 1x (the historical full-image draw).
|
||||
frameWindowZoom?: { s: number; u: number; v: number } | null;
|
||||
}
|
||||
|
||||
// Lightroom-style "Screen" output sharpen: subtle unsharp mask. A 3x3
|
||||
@@ -332,10 +339,22 @@ export async function processAndExportPhoto(
|
||||
const pl = polaroidLayout(outW, outH, width / height, 'contain');
|
||||
const win = pl.window;
|
||||
// Window aspect == photo aspect → fills exactly, no letterbox inside.
|
||||
// With a framed-window zoom the source rect shrinks to the visible part
|
||||
// (window keeps showing the same geometry as the viewfinder).
|
||||
const plainPaint = Skia.Paint();
|
||||
const pz = options?.frameWindowZoom ?? null;
|
||||
const pwz = pz && pz.s > 1.001 ? pz : null;
|
||||
const pVisW = pwz ? width / pwz.s : width;
|
||||
const pVisH = pwz ? height / pwz.s : height;
|
||||
const pSx = pwz
|
||||
? Math.min(Math.max(width * pwz.u - pVisW / 2, 0), width - pVisW)
|
||||
: 0;
|
||||
const pSy = pwz
|
||||
? Math.min(Math.max(height * pwz.v - pVisH / 2, 0), height - pVisH)
|
||||
: 0;
|
||||
cardCanvas.drawImageRect(
|
||||
photoImg,
|
||||
Skia.XYWHRect(0, 0, width, height),
|
||||
Skia.XYWHRect(pSx, pSy, pVisW, pVisH),
|
||||
Skia.XYWHRect(win.x, win.y, win.w, win.h),
|
||||
plainPaint
|
||||
);
|
||||
@@ -367,6 +386,13 @@ export async function processAndExportPhoto(
|
||||
// Decode the frame PNG artwork (bundled asset → base64, same path as the
|
||||
// GPS fonts below — embedded assets may not expose a readable file URI).
|
||||
const wfAsset = Asset.fromModule(require('../../wallframe.png'));
|
||||
// Android release builds resolve an image asset to a drawable *name*
|
||||
// ('wallframe', no scheme) and mark it downloaded, so downloadAsync()
|
||||
// short-circuits and readAsStringAsync throws 'Unsupported scheme for
|
||||
// location wallframe'. Clear that state so the native module copies the
|
||||
// drawable out of the APK into the cache as a real file path.
|
||||
wfAsset.localUri = null;
|
||||
wfAsset.downloaded = false;
|
||||
await wfAsset.downloadAsync();
|
||||
const wfDataStr = wfAsset.localUri
|
||||
? await FileSystem.readAsStringAsync(wfAsset.localUri, { encoding: FileSystem.EncodingType.Base64 })
|
||||
@@ -388,9 +414,23 @@ export async function processAndExportPhoto(
|
||||
const scale = Math.max(win.w / pw, win.h / ph);
|
||||
const srcW = win.w / scale;
|
||||
const srcH = win.h / scale;
|
||||
// Cover plane (center crop) that fills the window; a framed-window zoom
|
||||
// shrinks the visible source sub-rect inside that plane.
|
||||
const planeX = (pw - srcW) / 2;
|
||||
const planeY = (ph - srcH) / 2;
|
||||
const wz = options?.frameWindowZoom ?? null;
|
||||
const wZoom = wz && wz.s > 1.001 ? wz : null;
|
||||
const wVisW = wZoom ? srcW / wZoom.s : srcW;
|
||||
const wVisH = wZoom ? srcH / wZoom.s : srcH;
|
||||
const wSx = wZoom
|
||||
? Math.min(Math.max(planeX + srcW * wZoom.u - wVisW / 2, planeX), planeX + srcW - wVisW)
|
||||
: planeX;
|
||||
const wSy = wZoom
|
||||
? Math.min(Math.max(planeY + srcH * wZoom.v - wVisH / 2, planeY), planeY + srcH - wVisH)
|
||||
: planeY;
|
||||
cardCanvas.drawImageRect(
|
||||
photoImg,
|
||||
Skia.XYWHRect((pw - srcW) / 2, (ph - srcH) / 2, srcW, srcH),
|
||||
Skia.XYWHRect(wSx, wSy, wVisW, wVisH),
|
||||
Skia.XYWHRect(win.x, win.y, win.w, win.h),
|
||||
plainPaint
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user