fix(frame): give OLD FILM PORTRAIT a real portrait output
The sheet was stretched over the visitor's frame, so the portrait variant only turned the paper inside whatever shape the photo had: a landscape photo stayed landscape. The film now has its own opening like the walls do — the frame is scaled up to the photo, the photo is cover-cropped into the whole sheet, and `old-film-portrait` is the PNG turned 90° CW, so the pair is one frame standing and one lying. The torn edge has no straight sides, so the photo runs under all of it instead of being cut against a measured window.
This commit is contained in:
@@ -765,11 +765,13 @@ function loadWallframe(assets: RenderAssets) {
|
||||
}
|
||||
|
||||
// Both films are the same sheet of distressed paper: `old-film` lays it down as
|
||||
// it is, `old-film-portrait` turns it 90° CW so the torn edge and the dust lie
|
||||
// the other way round. Unlike the walls the sheet has no mat of its own — its
|
||||
// middle is a transparent window and its opaque part is a border a couple of
|
||||
// percent wide — so it is drawn OVER the photo and never crops it.
|
||||
// it is, `old-film-portrait` turns it 90° CW, so the pair is one frame standing
|
||||
// and one lying — the same two shapes the walls offer, in a sheet whose middle
|
||||
// is a transparent window a couple of percent of paper wide around it.
|
||||
const isOldFilm = (id: FrameId) => id === 'old-film' || id === 'old-film-portrait';
|
||||
// A frame that carries its own opening: the aspect chip has nothing to say to
|
||||
// it (both walls, both films).
|
||||
const hasOwnAspect = (id: FrameId) => isWallframe(id) || isOldFilm(id);
|
||||
|
||||
function loadOldFilm(assets: RenderAssets) {
|
||||
if (oldFilmImage) return oldFilmImage;
|
||||
@@ -812,10 +814,10 @@ export async function renderPhoto(input: RenderInput): Promise<RenderResult | nu
|
||||
const aspectWH = options?.aspect && options.aspect !== 'full' ? ASPECT_RATIO_W_H[options.aspect] : null;
|
||||
const cropRect = options?.cropRect ?? null;
|
||||
const ratioWH = cropRect ? null : options?.cropWH ?? aspectWH;
|
||||
// A wall is an artwork with its own opening, so the aspect chip (ratioWH)
|
||||
// has nothing to say there — but the visitor's own crop does, or a photo
|
||||
// that was cropped and applied comes back whole behind the frame.
|
||||
if (cropRect || (ratioWH && !isWallframe(frameId))) {
|
||||
// A wall or a film is an artwork with its own opening, so the aspect chip
|
||||
// (ratioWH) has nothing to say there — but the visitor's own crop does, or a
|
||||
// photo that was cropped and applied comes back whole behind the frame.
|
||||
if (cropRect || (ratioWH && !hasOwnAspect(frameId))) {
|
||||
const curAspect = width / height;
|
||||
let cropW = width;
|
||||
let cropH = height;
|
||||
@@ -1460,36 +1462,65 @@ export async function renderPhoto(input: RenderInput): Promise<RenderResult | nu
|
||||
stampW = outW;
|
||||
stampH = outH;
|
||||
} else if (isOldFilm(frameId)) {
|
||||
// The sheet is stretched over the whole frame and drawn on top of the
|
||||
// photo, so the paper eats the outer percent of the picture instead of
|
||||
// cropping it — the inverse of the walls above, whose mat window crops.
|
||||
// The stretch is deliberately non-uniform: a cover-scale of a 3:2 sheet
|
||||
// onto a 4:3 frame would cut the torn edge off two sides, and the deckle
|
||||
// is the whole point of the frame.
|
||||
// The sheet has the frame's SHAPE, like the walls: `old-film-portrait` is
|
||||
// the PNG turned 90° CW, so the pair is one standing frame and one lying.
|
||||
// The sheet's own 914x609 is far too small to hold a photo, so the frame
|
||||
// is scaled UP to the photo instead of the photo down to the frame: the
|
||||
// deckle stays a couple of percent of the print at any export size, which
|
||||
// is also why the photo is cover-cropped into the whole sheet and not
|
||||
// into a measured window — the sheet's edge is torn, so it has no straight
|
||||
// sides to cut against; the photo simply runs under all of it.
|
||||
flush(surface);
|
||||
const photoImg = own(surface.makeImageSnapshot());
|
||||
cardSource = photoImg;
|
||||
const film = loadOldFilm(input.assets);
|
||||
if (!film) {
|
||||
console.error('Failed to decode old_film.png');
|
||||
return null;
|
||||
}
|
||||
const portrait = frameId === 'old-film-portrait';
|
||||
const filmPaint = own(Skia.Paint());
|
||||
canvas.save();
|
||||
const sheetW = portrait ? film.height() : film.width();
|
||||
const sheetH = portrait ? film.width() : film.height();
|
||||
const scale = Math.max(width / sheetW, height / sheetH);
|
||||
const outW = Math.round(sheetW * scale);
|
||||
const outH = Math.round(sheetH * scale);
|
||||
const cardSurface = createSurface(outW, outH);
|
||||
if (!cardSurface) return null;
|
||||
own(cardSurface);
|
||||
const cardCanvas = cardSurface.getCanvas();
|
||||
const plainPaint = own(Skia.Paint());
|
||||
const cover = Math.max(outW / width, outH / height);
|
||||
const coverW = outW / cover;
|
||||
const coverH = outH / cover;
|
||||
cardCanvas.drawImageRectOptions(
|
||||
photoImg,
|
||||
Skia.XYWHRect((width - coverW) / 2, (height - coverH) / 2, coverW, coverH),
|
||||
Skia.XYWHRect(0, 0, outW, outH),
|
||||
Skia.FilterMode.Linear,
|
||||
Skia.MipmapMode.None,
|
||||
plainPaint
|
||||
);
|
||||
cardCanvas.save();
|
||||
if (portrait) {
|
||||
// The wall-frame portrait's own turn: origin to the top-right corner,
|
||||
// then 90° CW, so the sheet's width runs down the frame and the drawn
|
||||
// rect is the frame's height by its width.
|
||||
canvas.translate(width, 0);
|
||||
canvas.rotate(90, 0, 0);
|
||||
cardCanvas.translate(outW, 0);
|
||||
cardCanvas.rotate(90, 0, 0);
|
||||
}
|
||||
canvas.drawImageRectOptions(
|
||||
cardCanvas.drawImageRectOptions(
|
||||
film,
|
||||
Skia.XYWHRect(0, 0, film.width(), film.height()),
|
||||
portrait ? Skia.XYWHRect(0, 0, height, width) : Skia.XYWHRect(0, 0, width, height),
|
||||
portrait ? Skia.XYWHRect(0, 0, outH, outW) : Skia.XYWHRect(0, 0, outW, outH),
|
||||
Skia.FilterMode.Linear,
|
||||
Skia.MipmapMode.None,
|
||||
filmPaint
|
||||
plainPaint
|
||||
);
|
||||
canvas.restore();
|
||||
cardCanvas.restore();
|
||||
outputSurface = cardSurface;
|
||||
stampCanvas = cardCanvas;
|
||||
stampW = outW;
|
||||
stampH = outH;
|
||||
} else {
|
||||
drawFrameOnCanvas(canvas, width, height, frameId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user