feat: stamp 300dpi metadata on camera-captured exports
Skia JPEG re-encode drops original header (72dpi camera default). New src/utils/jpegDpi.ts rewrites JFIF APP0 density + EXIF X/YResolution (endian-aware) or inserts minimal JFIF when neither exists; applied via processAndExportPhoto options.dpi in the camera-capture branch only. Library-mode exports untouched. Pixels never change - metadata only.
This commit is contained in:
@@ -6,12 +6,18 @@ import { Recipe, GPSInfo, FrameId } from '../types';
|
||||
import { getSkiaColorMatrix } from './colorUtils';
|
||||
import { drawFrameOnCanvas } from './frameUtils';
|
||||
import { formatCoordinate } from './locationUtils';
|
||||
import { base64ToBytes, bytesToBase64, patchJpegDpi } from './jpegDpi';
|
||||
|
||||
export interface ExportResult {
|
||||
uri: string;
|
||||
savedToLibrary: boolean;
|
||||
}
|
||||
|
||||
export interface ExportOptions {
|
||||
// DPI to stamp into the JPEG header (metadata only — pixel size never changes).
|
||||
dpi?: number;
|
||||
}
|
||||
|
||||
// Fixed filename: every export overwrites the previous one, so temp files
|
||||
// never accumulate in the cache directory.
|
||||
const EXPORT_FILE = `${FileSystem.cacheDirectory}camrecipe_pro_last_export.jpg`;
|
||||
@@ -21,7 +27,8 @@ export async function processAndExportPhoto(
|
||||
recipe: Recipe,
|
||||
frameId: FrameId,
|
||||
useGeotag: boolean,
|
||||
gpsInfo: GPSInfo | null
|
||||
gpsInfo: GPSInfo | null,
|
||||
options?: ExportOptions
|
||||
): Promise<ExportResult | null> {
|
||||
try {
|
||||
// 1. Read source image file into Skia
|
||||
@@ -181,13 +188,19 @@ export async function processAndExportPhoto(
|
||||
|
||||
// 9. Snapshot & Encode to JPEG (base64 string, no SkData round-trip)
|
||||
const resultImage = surface.makeImageSnapshot();
|
||||
const base64 = resultImage.encodeToBase64(ImageFormat.JPEG, 95);
|
||||
let base64 = resultImage.encodeToBase64(ImageFormat.JPEG, 95);
|
||||
|
||||
if (!base64) {
|
||||
console.error('Failed to encode image to JPEG');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 9b. Patch DPI metadata (JFIF density / EXIF resolution) when requested
|
||||
if (options?.dpi && options.dpi > 0) {
|
||||
const patched = patchJpegDpi(base64ToBytes(base64), options.dpi);
|
||||
base64 = bytesToBase64(patched);
|
||||
}
|
||||
|
||||
// 10. Write binary bytes to temporary local file (overwrites previous export)
|
||||
await FileSystem.writeAsStringAsync(EXPORT_FILE, base64, {
|
||||
encoding: FileSystem.EncodingType.Base64,
|
||||
|
||||
Reference in New Issue
Block a user