import { Recipe, ColorAdjustments } from '../types'; // Fallback values for every adjustable knob. Old recipes/sessions persisted in // AsyncStorage lack the newer fields — merge this under stored JSON to keep // every adjustment well-defined (see storageUtils.getAllRecipes / App session). export const DEFAULT_ADJUSTMENTS: ColorAdjustments = { exposure: 0, contrast: 0, saturation: 0, temperature: 5500, tint: 0, wbRed: 0, wbBlue: 0, highlight: 0, shadow: 0, denoise: 0, clarity: 0, grain: 0, colorChrome: 'none', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, hslHue: 0, hslSat: 0, hslLum: 0, exposureCompensation: 0, }; // Film simulations. These are the fixed "look" presets at the front of the // PRESETS chip row, and each one is COLOUR AND TONE ONLY. // // A sim carries exactly two things: its `baseFilter` colour matrix // (colorUtils.ts) and, where the stock needs to shape one end of the curve, its // FILM_TONE entry (toneShader.ts). Everything the sim does to the picture is in // those two places — including the brightness a stock owns: that is folded into // the matrix through SIM_EXPOSURE_BIAS (colorUtils.ts), never into // `adjustments`. `adjustments` is therefore the neutral default for every sim — // no grain, no clarity, no saturation push, no exposure/highlight/shadow move — // because those are the user's knobs, not the stock's. A sim is a clean starting // point, never a baked recipe; any further look is added afterwards. // // Name → the stock's colour and tone it must stand for. This table is the // REFERENCE ONLY: the string at the left is just the app's label for the chip // (the legacy 'PES' spelling), and what matters is that the sim behind it grades // the picture the way the stock at the right does. So a change to a label must // never touch the grading, and a change to a grading never renames the chip: // PROVIPES Provia / Standard // VELVIPES Velvia / Vivid // CLASSIC CHRIPES Classic Chrome // CLASSIC VIVIDIPES Velvia spliced with Classic Chrome at the blue row // CLASSIC NEGIPES Classic Negative (Superia-like) // ASTIPES Astia / Soft // ETERNIPES Eterna (cinema) // ACRIPES Acros (monochrome) // LC STREETLIFE CLASSIC Leica Authentic Classic // LC STREETLIFE VIVID Leica Vivid export interface FilmSim { id: string; // Recipe-like id used only while the sim is the current selection name: string; baseFilter: Recipe['baseFilter']; adjustments: ColorAdjustments; } export const FILM_SIMS: FilmSim[] = [ { id: 'sim-provia', name: 'PROVIPES', baseFilter: 'provia', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-velvia', name: 'VELVIPES', baseFilter: 'velvia', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-classic-chrome', name: 'CLASSIC CHRIPES', baseFilter: 'classic-chrome', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-classic-vivid', name: 'CLASSIC VIVIDIPES', baseFilter: 'classic-vivid', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-classic-neg', name: 'CLASSIC NEGIPES', baseFilter: 'classic-neg', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-astia', name: 'ASTIPES', baseFilter: 'astia', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-eterna', name: 'ETERNIPES', baseFilter: 'eterna', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-acros', name: 'ACRIPES', baseFilter: 'monochrome', adjustments: { ...DEFAULT_ADJUSTMENTS } }, // The rangefinder digital look (M/Q/SL) is not a film stock, so it ships as its own // pair instead of one look that has to compromise. Both are still colour and // tone only: CLASSIC is Leica Authentic (the matrix carries the channel // separation and the warm European cast, nothing else), VIVID is Leica Vivid // (the same separation opened up — again in the matrix, with the one // brightness step the stock owns folded in through SIM_EXPOSURE_BIAS in // colorUtils, never as an `adjustments` value). { id: 'sim-leica', name: 'LC STREETLIFE CLASSIC', baseFilter: 'leica', adjustments: { ...DEFAULT_ADJUSTMENTS } }, { id: 'sim-leica-vivid', name: 'LC STREETLIFE VIVID', baseFilter: 'leica-vivid', adjustments: { ...DEFAULT_ADJUSTMENTS } }, ]; // Film sim → full Recipe (never persisted; sims are applied on the fly). export function filmSimToRecipe(sim: FilmSim): Recipe { return { id: sim.id, name: sim.name, baseFilter: sim.baseFilter, adjustments: { ...sim.adjustments }, frameId: 'none', useGeotag: true, }; } export const DEFAULT_RECIPES: Recipe[] = [ { id: 'classic-neg-default', name: 'CLASSIC NEGIPES.', baseFilter: 'classic-neg', adjustments: { exposure: 2, contrast: 1, saturation: -1, temperature: 6300, tint: 2, highlight: -2, shadow: 1, denoise: 1, clarity: 2, grain: 4, colorChrome: 'weak', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', useGeotag: true, }, { id: 'velvia-default', name: 'VELVIPES VIVID', baseFilter: 'velvia', adjustments: { exposure: 0, contrast: 2, saturation: 3, temperature: 5500, tint: 0, highlight: 1, shadow: -1, denoise: 2, clarity: 4, grain: 1, colorChrome: 'strong', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', useGeotag: true, }, { id: 'provia-default', name: 'PROVIPES STD', baseFilter: 'provia', adjustments: { exposure: 1, contrast: 0, saturation: 0, temperature: 5000, tint: 0, highlight: 0, shadow: 0, denoise: 2, clarity: 1, grain: 2, colorChrome: 'none', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', useGeotag: true, }, { id: 'acros-default', name: 'ACRIPES MONO', baseFilter: 'monochrome', adjustments: { exposure: 0, // Low: the deep black comes from FILM_TONE's toe and the micro-contrast // from CLARITY, both of which add separation WITHOUT flattening the grey // ramp. The old 4 / highlight 3 / shadow -2 stack was three global // contrast moves on top of a matrix that was already clipping — it // bought its deep black by throwing away every step below 9% and every // one above 91%. contrast: 2, saturation: 0, temperature: 5000, tint: 0, highlight: 0, shadow: 0, denoise: 0, clarity: 3, grain: 5, colorChrome: 'none', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', useGeotag: true, }, { id: 'retro-amber-default', name: 'RETRO AMBER 🏮', baseFilter: 'classic-neg', adjustments: { exposure: 3, contrast: 2, saturation: 1, temperature: 7500, tint: -2, highlight: -3, shadow: 2, denoise: 3, clarity: -2, grain: 5, colorChrome: 'strong', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'classic-white', useGeotag: true, }, { id: 'cinema-spring-default', name: 'CINEMA SPRING', baseFilter: 'none', adjustments: { exposure: 0, contrast: 1, saturation: 1, temperature: 5800, tint: 0, highlight: -1, shadow: 1, denoise: 2, clarity: 1, grain: 3, colorChrome: 'none', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', cinema: 'spring', useGeotag: true, }, { id: 'cinema-summer-default', name: 'CINEMA SUMMER', baseFilter: 'none', adjustments: { exposure: 0, contrast: 2, saturation: 2, temperature: 6000, tint: -1, highlight: 1, shadow: -1, denoise: 1, clarity: 2, grain: 2, colorChrome: 'weak', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', cinema: 'summer', useGeotag: true, }, { id: 'cinema-autumn-default', name: 'CINEMA AUTUMN', baseFilter: 'none', adjustments: { exposure: 0, contrast: 1, saturation: 2, temperature: 7200, tint: -2, highlight: -2, shadow: 1, denoise: 2, clarity: 0, grain: 4, colorChrome: 'weak', colorChromeBlue: 'none', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', cinema: 'autumn', useGeotag: true, }, { id: 'cinema-winter-default', name: 'CINEMA WINTER', baseFilter: 'none', adjustments: { exposure: 1, contrast: -1, saturation: -2, temperature: 4500, tint: 2, highlight: -2, shadow: 2, denoise: 3, clarity: -2, grain: 3, colorChrome: 'none', colorChromeBlue: 'weak', dynamicRange: 'auto', sharpening: 0, exposureCompensation: 0, }, frameId: 'none', cinema: 'winter', useGeotag: true, }, ];