76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
import RecipescamExport from '../../modules/recipescam-export';
|
|
import { applyExposureGain, getSkiaColorMatrix } from '../utils/colorUtils';
|
|
import { BaseFilter, ColorAdjustments } from '../types';
|
|
|
|
const RUNS = 2;
|
|
const IDENTITY = (() => {
|
|
const m = new Array(20).fill(0);
|
|
m[0] = m[6] = m[12] = m[18] = 1;
|
|
return m;
|
|
})();
|
|
|
|
const NEUTRAL_ADJ: ColorAdjustments = {
|
|
exposure: 0,
|
|
contrast: 0,
|
|
saturation: 0,
|
|
temperature: 5500,
|
|
tint: 0,
|
|
highlight: 0,
|
|
shadow: 0,
|
|
denoise: 0,
|
|
clarity: 0,
|
|
grain: 0,
|
|
colorChrome: 'none',
|
|
colorChromeBlue: 'none',
|
|
dynamicRange: 100,
|
|
sharpening: 0,
|
|
exposureCompensation: 0,
|
|
};
|
|
|
|
/**
|
|
* P0/P1/P2 probe harness (temporary — removed at P7).
|
|
* Native logs (RecipesCamExport tag) are the only visible channel: release
|
|
* Hermes strips console.*. Each gate prints its own centerPixel / timing lines.
|
|
*/
|
|
export async function nativeBenchProbe(): Promise<void> {
|
|
const m = RecipescamExport as any;
|
|
try {
|
|
const pong = await m.ping();
|
|
const src: string = await m.materializeBenchAsset('wallframe');
|
|
|
|
// P1 parity: classic-neg matrix on black -> expect center ~(13,5,0).
|
|
const matrix = applyExposureGain(getSkiaColorMatrix('classic-neg' as BaseFilter, NEUTRAL_ADJ), 0);
|
|
const black = await m.materializeSolidAsset(0, 0, 0);
|
|
const blackOut = black.replace(/\.png$/, `-neg-${Date.now()}.jpg`);
|
|
await m.processColorAsync(black, blackOut, matrix, null, 95);
|
|
console.error('[BENCH] classic-neg-on-black matrix[0..4]=' + matrix.slice(0, 5).join(','));
|
|
|
|
// P2 tone parity on solid ramps (expected values computed from TONE_SKSL):
|
|
// white(255) hl=-1 -> 140; gray64 sh=+1 -> 75; white dr=1 -> 209;
|
|
// gray26 sh=+1 -> 104; gray26 hl=+1 sh=-1 -> 11.
|
|
const toneGates: Array<{ rgb: [number, number, number]; tone: { dr: number; hl: number; sh: number }; tag: string }> = [
|
|
{ rgb: [255, 255, 255], tone: { dr: 0, hl: -1, sh: 0 }, tag: 'white-hl-1' },
|
|
{ rgb: [64, 64, 64], tone: { dr: 0, hl: 0, sh: 1 }, tag: 'gray64-sh+1' },
|
|
{ rgb: [255, 255, 255], tone: { dr: 1, hl: 0, sh: 0 }, tag: 'white-dr1' },
|
|
{ rgb: [26, 26, 26], tone: { dr: 0, hl: 0, sh: 1 }, tag: 'gray26-sh+1' },
|
|
{ rgb: [26, 26, 26], tone: { dr: 0, hl: 1, sh: -1 }, tag: 'gray26-hl+1-sh-1' },
|
|
];
|
|
for (const g of toneGates) {
|
|
const solid = await m.materializeSolidAsset(g.rgb[0], g.rgb[1], g.rgb[2]);
|
|
const out = solid.replace(/\.png$/, `-tone-${Date.now()}.jpg`);
|
|
const t = await m.processPhotoAsync(solid, out, IDENTITY, null, g.tone.dr, g.tone.hl, g.tone.sh, 95);
|
|
console.error('[BENCH] toneGate ' + g.tag + ' totalMs=' + t.totalMs + ' toneMs=' + (t.toneMs ?? 0));
|
|
}
|
|
|
|
// Full-artwork run: provia + mild tone to sanity-check timing on 12MP.
|
|
const provia = applyExposureGain(getSkiaColorMatrix('provia' as BaseFilter, NEUTRAL_ADJ), 0);
|
|
for (let i = 0; i < RUNS; i++) {
|
|
const dst = src.replace(/\.png$/, `-full-${Date.now()}-${i}.jpg`);
|
|
const t = await m.processPhotoAsync(src, dst, provia, null, 0.33, 0.3, 0.3, 95);
|
|
console.error('[BENCH] fullRun=' + i + ' decodeMs=' + t.decodeMs + ' toneMs=' + (t.toneMs ?? 0) + ' colorMs=' + t.colorMs + ' encodeWriteMs=' + t.encodeWriteMs + ' totalMs=' + t.totalMs + ' pong=' + pong);
|
|
}
|
|
} catch (e) {
|
|
console.error('[BENCH] failed', e);
|
|
}
|
|
}
|