fix(camera): matrix-based zoom for pinch/double-tap, tap-to-focus AE/AF lock, library letterbox smear fix, export raw JPEG bytes q95, resume isActive fixes, dark theme polish
This commit is contained in:
+47
-11
@@ -1,3 +1,7 @@
|
||||
import { ColorAdjustments } from '../types';
|
||||
|
||||
export type BaseFilter = 'classic-neg' | 'provia' | 'velvia' | 'monochrome' | 'none';
|
||||
|
||||
// Kelvin to RGB compensation for White Balance.
|
||||
// In photography, when WB is set HIGH (e.g. 10000K), the camera understands
|
||||
// the scene light is COLD and compensates with WARM tones (red/orange).
|
||||
@@ -23,16 +27,21 @@ export function kelvinToRGB(kelvin: number): { r: number; g: number; b: number }
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
// Generate a 4x5 ColorMatrix (array of 20 floats) based on base style and adjustments
|
||||
export function getSkiaColorMatrix(
|
||||
baseFilter: 'classic-neg' | 'provia' | 'velvia' | 'monochrome' | 'none',
|
||||
exposure: number, // -10 to +10
|
||||
contrast: number, // -10 to +10
|
||||
saturation: number, // -10 to +10
|
||||
temperature: number, // 2500 to 10000
|
||||
tint: number, // -10 to +10
|
||||
colorChrome: 'none' | 'weak' | 'strong'
|
||||
): number[] {
|
||||
// Generate a 4x5 ColorMatrix (array of 20 floats) based on base style and
|
||||
// adjustments. Tone-domain knobs (dynamicRange/highlight/shadow) and the
|
||||
// exposure-compensation EV gain are intentionally NOT part of the matrix —
|
||||
// dynamicRange/highlight/shadow run as a tone shader (see toneShader.ts),
|
||||
// and EV is applied either as hardware AE bias (camera) or by the caller via
|
||||
// applyExposureGain (library), never doubled.
|
||||
export function getSkiaColorMatrix(baseFilter: BaseFilter, adj: ColorAdjustments): number[] {
|
||||
const exposure = adj.exposure;
|
||||
const contrast = adj.contrast;
|
||||
const saturation = adj.saturation;
|
||||
const temperature = adj.temperature;
|
||||
const tint = adj.tint;
|
||||
const colorChrome = adj.colorChrome || 'none';
|
||||
const colorChromeBlue = adj.colorChromeBlue || 'none';
|
||||
|
||||
// 1. Start with Identity Matrix
|
||||
let matrix = [
|
||||
1, 0, 0, 0, 0,
|
||||
@@ -110,6 +119,18 @@ export function getSkiaColorMatrix(
|
||||
matrix = multiplyMatrices(chromeMat, matrix);
|
||||
}
|
||||
|
||||
// 4b. Color Chrome Effect Blue — deep-blue richness boost (blue channel only)
|
||||
if (baseFilter !== 'monochrome' && colorChromeBlue !== 'none') {
|
||||
const factor = colorChromeBlue === 'strong' ? 0.35 : 0.15;
|
||||
const blueMat = [
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1 + factor, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
];
|
||||
matrix = multiplyMatrices(blueMat, matrix);
|
||||
}
|
||||
|
||||
// 5. White Balance (Kelvin Temperature and Tint) — skip for monochrome to prevent color leakage
|
||||
if (baseFilter !== 'monochrome') {
|
||||
const rgbTemp = kelvinToRGB(temperature);
|
||||
@@ -146,6 +167,21 @@ export function getSkiaColorMatrix(
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Apply photographic EV compensation (stops) as a linear gain: 2^EV on every
|
||||
// RGB channel (offsets scale too, matching true exposure). Only used for
|
||||
// stills that were NOT exposed with a hardware AE bias (library images).
|
||||
export function applyExposureGain(matrix: number[], evStops: number): number[] {
|
||||
if (!evStops) return matrix;
|
||||
const s = Math.pow(2, evStops);
|
||||
const gainMat = [
|
||||
s, 0, 0, 0, 0,
|
||||
0, s, 0, 0, 0,
|
||||
0, 0, s, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
];
|
||||
return multiplyMatrices(gainMat, matrix);
|
||||
}
|
||||
|
||||
// Compute a tint color + opacity from the matrix for camera overlay preview
|
||||
// Applies the matrix to white (1,1,1,1) to see what color cast the filter creates
|
||||
export function computeMatrixTint(matrix: number[]): { r: number; g: number; b: number; a: number } {
|
||||
@@ -177,7 +213,7 @@ function multiplyMatrices(a: number[], b: number[]): number[] {
|
||||
a[r * 5 + 2] * b[2 * 5 + c] +
|
||||
a[r * 5 + 3] * b[3 * 5 + c];
|
||||
}
|
||||
// Handle the 5th column translation translation
|
||||
// Handle the 5th column translation
|
||||
result[r * 5 + 4] =
|
||||
a[r * 5 + 0] * b[0 * 5 + 4] +
|
||||
a[r * 5 + 1] * b[1 * 5 + 4] +
|
||||
|
||||
Reference in New Issue
Block a user