feat(frame): add OLD FILM LANDSCAPE and OLD FILM PORTRAIT from old_film.png
This commit is contained in:
@@ -34,13 +34,20 @@ export function probeGpu(): boolean {
|
||||
export async function loadEngine(): Promise<{ assets: RenderAssets; gpu: boolean }> {
|
||||
await initSkia({ locateFile: (file) => `/wasm/${file}` });
|
||||
if (!assets) {
|
||||
const [textFont, emojiFont, wallframePng, markFonts] = await Promise.all([
|
||||
const [textFont, emojiFont, wallframePng, oldFilmPng, markFonts] = await Promise.all([
|
||||
buf('/assets/Cousine-Regular.ttf'),
|
||||
buf('/assets/NotoEmoji-GPS.ttf'),
|
||||
buf('/assets/wallframe.png'),
|
||||
buf('/assets/old_film.png'),
|
||||
Promise.all(MARK_FACES.map(async (face) => ({ family: face.family, bytes: await buf(face.file) }))),
|
||||
]);
|
||||
assets = { textFont, emojiFont, wallframe: new Uint8Array(wallframePng), markFonts };
|
||||
assets = {
|
||||
textFont,
|
||||
emojiFont,
|
||||
wallframe: new Uint8Array(wallframePng),
|
||||
oldFilm: new Uint8Array(oldFilmPng),
|
||||
markFonts,
|
||||
};
|
||||
}
|
||||
return { assets, gpu: probeGpu() };
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export interface RenderAssets {
|
||||
textFont: ArrayBuffer; // assets/Cousine-Regular.ttf
|
||||
emojiFont: ArrayBuffer; // assets/NotoEmoji-GPS.ttf
|
||||
wallframe: Uint8Array; // wallframe.png
|
||||
oldFilm: Uint8Array; // old_film.png
|
||||
// The FONT option's faces beside DEFAULT (see assets.ts MARK_FACES).
|
||||
markFonts: { family: string; bytes: ArrayBuffer }[];
|
||||
}
|
||||
@@ -690,6 +691,7 @@ function replaceThrough(
|
||||
|
||||
let faces: { typeface: any; iconTypeface: any } | null = null;
|
||||
let wallframeImage: any = null;
|
||||
let oldFilmImage: any = null;
|
||||
|
||||
function loadFaces(assets: RenderAssets) {
|
||||
if (faces) return faces;
|
||||
@@ -762,6 +764,19 @@ function loadWallframe(assets: RenderAssets) {
|
||||
return wallframeImage;
|
||||
}
|
||||
|
||||
// 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.
|
||||
const isOldFilm = (id: FrameId) => id === 'old-film' || id === 'old-film-portrait';
|
||||
|
||||
function loadOldFilm(assets: RenderAssets) {
|
||||
if (oldFilmImage) return oldFilmImage;
|
||||
oldFilmImage = Skia.Image.MakeImageFromEncoded(assets.oldFilm);
|
||||
return oldFilmImage;
|
||||
}
|
||||
|
||||
export async function renderPhoto(input: RenderInput): Promise<RenderResult | null> {
|
||||
const { recipe, frameId, options } = input;
|
||||
const owned: SkDisposable[] = [];
|
||||
@@ -1444,6 +1459,37 @@ export async function renderPhoto(input: RenderInput): Promise<RenderResult | nu
|
||||
stampCanvas = cardCanvas;
|
||||
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.
|
||||
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();
|
||||
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);
|
||||
}
|
||||
canvas.drawImageRectOptions(
|
||||
film,
|
||||
Skia.XYWHRect(0, 0, film.width(), film.height()),
|
||||
portrait ? Skia.XYWHRect(0, 0, height, width) : Skia.XYWHRect(0, 0, width, height),
|
||||
Skia.FilterMode.Linear,
|
||||
Skia.MipmapMode.None,
|
||||
filmPaint
|
||||
);
|
||||
canvas.restore();
|
||||
} else {
|
||||
drawFrameOnCanvas(canvas, width, height, frameId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user