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 { 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); } }