Add aspect-ratio composition band and RAW DNG sidecar capture
This commit is contained in:
@@ -3,7 +3,7 @@ import { File } from 'expo-file-system';
|
||||
import * as FileSystem from 'expo-file-system/legacy';
|
||||
import * as MediaLibrary from 'expo-media-library/legacy';
|
||||
import { Asset } from 'expo-asset';
|
||||
import { Recipe, GPSInfo, FrameId } from '../types';
|
||||
import { Recipe, GPSInfo, FrameId, AspectRatio, ASPECT_RATIO_W_H } from '../types';
|
||||
import { getSkiaColorMatrix, applyExposureGain } from './colorUtils';
|
||||
import { TONE_SKSL, getToneUniforms, toneIsActive } from './toneShader';
|
||||
import { CINEMA_SKSL, getCinemaUniforms, cinemaIsActive } from './cinemaShader';
|
||||
@@ -25,6 +25,12 @@ export interface ExportOptions {
|
||||
// already applied as a hardware AE bias at capture, so no 2^EV matrix gain
|
||||
// should be added on top. Library stills omit this and get the matrix gain.
|
||||
evFromCamera?: boolean;
|
||||
// Composition aspect-ratio crop (upright 4:3 or 3:2). Center-largest crop
|
||||
// applied right after decode — every later step (frame/GPS/sharpen/size)
|
||||
// then runs on the cropped pixels, so the export matches the ratio the
|
||||
// viewfinder displayed. WALL FRAME ignores this (frame keeps its fixed
|
||||
// opening and export size). 'full'/undefined = no crop.
|
||||
aspect?: AspectRatio;
|
||||
}
|
||||
|
||||
// Lightroom-style "Screen" output sharpen: subtle unsharp mask. A 3x3
|
||||
@@ -87,18 +93,60 @@ export async function processAndExportPhoto(
|
||||
console.error('Failed to load image data from URI');
|
||||
return null;
|
||||
}
|
||||
const skImage = Skia.Image.MakeImageFromEncoded(skiaData);
|
||||
const skImage0 = Skia.Image.MakeImageFromEncoded(skiaData);
|
||||
|
||||
if (!skImage) {
|
||||
if (!skImage0) {
|
||||
console.error('Failed to parse image from URI');
|
||||
return null;
|
||||
}
|
||||
console.error(`[export] decode ${Date.now() - t0}ms`);
|
||||
|
||||
const width = skImage.width();
|
||||
const height = skImage.height();
|
||||
let skImage = skImage0;
|
||||
let width = skImage.width();
|
||||
let height = skImage.height();
|
||||
console.error(`[export] BEGIN ${width}x${height} ev=${recipe.adjustments.exposureCompensation ?? 0} evFromCamera=${options?.evFromCamera ?? false}`);
|
||||
|
||||
// 1b. Aspect-ratio crop (composition): center-largest upright crop of the
|
||||
// requested ratio. Runs BEFORE any processing so frame/GPS/grain/sharpen
|
||||
// all apply to exactly the pixels the user framed. Wall Frame is exempt —
|
||||
// its export is the fixed frame artwork whose opening cover-crops anyway.
|
||||
const ratioWH = options?.aspect && options.aspect !== 'full' ? ASPECT_RATIO_W_H[options.aspect] : null;
|
||||
if (ratioWH && frameId !== 'wallframe') {
|
||||
const curAspect = width / height;
|
||||
let cropW = width;
|
||||
let cropH = height;
|
||||
let srcX = 0;
|
||||
let srcY = 0;
|
||||
if (curAspect > ratioWH) {
|
||||
cropW = Math.round(height * ratioWH);
|
||||
srcX = Math.round((width - cropW) / 2);
|
||||
} else {
|
||||
cropH = Math.round(width / ratioWH);
|
||||
srcY = Math.round((height - cropH) / 2);
|
||||
}
|
||||
const cropSurface = createSurface(cropW, cropH);
|
||||
if (!cropSurface) {
|
||||
console.error('Failed to create aspect-crop surface');
|
||||
return null;
|
||||
}
|
||||
const cropCanvas = cropSurface.getCanvas();
|
||||
cropCanvas.drawImageRect(
|
||||
skImage,
|
||||
Skia.XYWHRect(srcX, srcY, cropW, cropH),
|
||||
Skia.XYWHRect(0, 0, cropW, cropH),
|
||||
Skia.Paint()
|
||||
);
|
||||
const cropped = cropSurface.makeImageSnapshot();
|
||||
if (!cropped) {
|
||||
console.error('Failed to snapshot aspect crop');
|
||||
return null;
|
||||
}
|
||||
skImage = cropped;
|
||||
width = cropW;
|
||||
height = cropH;
|
||||
console.error(`[export] aspect-crop ${width}x${height}`);
|
||||
}
|
||||
|
||||
// 2. Create offscreen canvas (GPU-backed when available — see createSurface)
|
||||
const surface = createSurface(width, height);
|
||||
if (!surface) {
|
||||
|
||||
Reference in New Issue
Block a user