fix(sim): rebuild the six film-sim color matrices to match the stock looks

Provia/Velvia/Astia/Classic Chrome/Classic Neg/Acros each get their own
primaries+cross-talk matrix measured against the pipeline, plus a split-tone
stage (shadow/highlight tint uniforms) for Classic Chrome's teal and Classic
Neg's green-cyan shadows vs warm highlights.
This commit is contained in:
2026-09-10 20:57:19 +07:00
parent 93f48537f8
commit 4bb2bda5c8
5 changed files with 139 additions and 53 deletions
+31 -16
View File
@@ -29,7 +29,7 @@ import {
import { Lock } from 'lucide-react-native';
import { Recipe, GPSInfo, FrameId, AspectRatio, ColorAdjustments, ASPECT_RATIO_W_H, CropRatio, CropRect, CROP_W_H, DEFAULT_CROP_RECT, MIN_CROP_FRAC } from '../types';
import { getSkiaColorMatrix, applyExposureGain } from '../utils/colorUtils';
import { TONE_SKSL, getToneUniforms } from '../utils/toneShader';
import { TONE_SKSL, getToneUniforms, toneUniformArray } from '../utils/toneShader';
import { CINEMA_SKSL, getCinemaUniforms, cinemaOffFlat } from '../utils/cinemaShader';
import { polaroidLayout, wallframeLayout, WALLFRAME_PNG_W, WALLFRAME_PNG_H } from '../utils/frameUtils';
import { rotateSkImage90 } from '../utils/skiaImage';
@@ -392,13 +392,12 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
// Tone uniforms [dr, hl, sh] for the preview worklet; all-zero = no tone pass.
const toneParams = (() => {
const u = getToneUniforms(adjustments);
return [u.dr, u.hl, u.sh, u.vib];
})();
// Tone uniforms [dr, hl, sh, vib, shadow split RGB, highlight split RGB] for
// the preview worklet; all-zero = no tone pass. A stock may keep the pass on
// with every user knob at 0 (Classic Neg split tone, Classic Chrome crush).
const toneParams = toneUniformArray(getToneUniforms(adjustments, recipe.baseFilter));
const toneSync = useMemo(
() => createSynchronizable<number[]>([0, 0, 0]),
() => createSynchronizable<number[]>(new Array(10).fill(0)),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
@@ -820,7 +819,11 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
}
const tone = toneSync.getDirty();
const cinema = cinemaSync.getDirty();
const hasTone = toneEffect != null && (tone[0] !== 0 || tone[1] !== 0 || tone[2] !== 0 || tone[3] !== 0);
const hasTone =
toneEffect != null &&
(tone[0] !== 0 || tone[1] !== 0 || tone[2] !== 0 || tone[3] !== 0 ||
tone[4] !== 0 || tone[5] !== 0 || tone[6] !== 0 ||
tone[7] !== 0 || tone[8] !== 0 || tone[9] !== 0);
const hasCinema = cinemaEffect != null && cinema[0] > 0;
// Draws the full frame with the tone/cinema shader when active (drawRect,
// not drawImage: paint shaders only apply to geometry fills — drawImage
@@ -2135,8 +2138,20 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
}
// Library mode — fullscreen, full-photo (contain), pinch/double-tap zoom.
const toneOn =
toneParams[0] !== 0 || toneParams[1] !== 0 || toneParams[2] !== 0 || toneParams[3] !== 0;
const toneOn = toneParams.some((v) => v !== 0);
// One object for every <Shader> below; keys must match TONE_SKSL's uniforms.
const toneUniforms = {
dr: toneParams[0],
hl: toneParams[1],
sh: toneParams[2],
vib: toneParams[3],
shTr: toneParams[4],
shTg: toneParams[5],
shTb: toneParams[6],
hlTr: toneParams[7],
hlTg: toneParams[8],
hlTb: toneParams[9],
};
const cinemaOn = cinemaEffect != null && !!cinemaParams;
const useShaderPass = !!imageFitRect && ((toneEffect != null && toneOn) || cinemaOn);
@@ -2164,7 +2179,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
{toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader image={skiaImage} fit={polaroidFit} tx="clamp" ty="clamp" rect={winRect} />
</Shader>
@@ -2175,7 +2190,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
) : toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader image={skiaImage} fit={polaroidFit} tx="clamp" ty="clamp" rect={winRect} />
</Shader>
@@ -2244,7 +2259,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
{toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader image={skiaImage} fit="cover" tx="clamp" ty="clamp" rect={winRect} />
</Shader>
@@ -2255,7 +2270,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
) : toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader image={skiaImage} fit="cover" tx="clamp" ty="clamp" rect={winRect} />
</Shader>
@@ -2321,7 +2336,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
{toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader
image={skiaImage}
@@ -2344,7 +2359,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
) : toneEffect && toneOn ? (
<Shader
source={toneEffect}
uniforms={{ dr: toneParams[0], hl: toneParams[1], sh: toneParams[2], vib: toneParams[3] }}
uniforms={toneUniforms}
>
<ImageShader
image={skiaImage}