@flighthq/adjustments 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brightnessContrastAdjustment.d.ts +3 -0
- package/dist/brightnessContrastAdjustment.d.ts.map +1 -0
- package/dist/brightnessContrastAdjustment.js +22 -0
- package/dist/brightnessContrastAdjustment.js.map +1 -0
- package/dist/channelMixerAdjustment.d.ts +3 -0
- package/dist/channelMixerAdjustment.d.ts.map +1 -0
- package/dist/channelMixerAdjustment.js +16 -0
- package/dist/channelMixerAdjustment.js.map +1 -0
- package/dist/colorAdjustmentResolution.d.ts +7 -0
- package/dist/colorAdjustmentResolution.d.ts.map +1 -0
- package/dist/colorAdjustmentResolution.js +59 -0
- package/dist/colorAdjustmentResolution.js.map +1 -0
- package/dist/colorBlindSimulationAdjustment.d.ts +3 -0
- package/dist/colorBlindSimulationAdjustment.d.ts.map +1 -0
- package/dist/colorBlindSimulationAdjustment.js +33 -0
- package/dist/colorBlindSimulationAdjustment.js.map +1 -0
- package/dist/colorGradeAdjustment.d.ts +3 -0
- package/dist/colorGradeAdjustment.d.ts.map +1 -0
- package/dist/colorGradeAdjustment.js +49 -0
- package/dist/colorGradeAdjustment.js.map +1 -0
- package/dist/colorLut.d.ts +29 -0
- package/dist/colorLut.d.ts.map +1 -0
- package/dist/colorLut.js +87 -0
- package/dist/colorLut.js.map +1 -0
- package/dist/colorLutAdjustment.d.ts +8 -0
- package/dist/colorLutAdjustment.d.ts.map +1 -0
- package/dist/colorLutAdjustment.js +30 -0
- package/dist/colorLutAdjustment.js.map +1 -0
- package/dist/colorLutCache.d.ts +6 -0
- package/dist/colorLutCache.d.ts.map +1 -0
- package/dist/colorLutCache.js +43 -0
- package/dist/colorLutCache.js.map +1 -0
- package/dist/colorMatrixAdjustment.d.ts +8 -0
- package/dist/colorMatrixAdjustment.d.ts.map +1 -0
- package/dist/colorMatrixAdjustment.js +14 -0
- package/dist/colorMatrixAdjustment.js.map +1 -0
- package/dist/colorMatrixMath.d.ts +160 -0
- package/dist/colorMatrixMath.d.ts.map +1 -0
- package/dist/colorMatrixMath.js +398 -0
- package/dist/colorMatrixMath.js.map +1 -0
- package/dist/colorTransformAdjustment.d.ts +3 -0
- package/dist/colorTransformAdjustment.d.ts.map +1 -0
- package/dist/colorTransformAdjustment.js +16 -0
- package/dist/colorTransformAdjustment.js.map +1 -0
- package/dist/exposureAdjustment.d.ts +3 -0
- package/dist/exposureAdjustment.d.ts.map +1 -0
- package/dist/exposureAdjustment.js +18 -0
- package/dist/exposureAdjustment.js.map +1 -0
- package/dist/grayscaleAdjustment.d.ts +3 -0
- package/dist/grayscaleAdjustment.d.ts.map +1 -0
- package/dist/grayscaleAdjustment.js +20 -0
- package/dist/grayscaleAdjustment.js.map +1 -0
- package/dist/hueSaturationAdjustment.d.ts +3 -0
- package/dist/hueSaturationAdjustment.d.ts.map +1 -0
- package/dist/hueSaturationAdjustment.js +62 -0
- package/dist/hueSaturationAdjustment.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/invertAdjustment.d.ts +3 -0
- package/dist/invertAdjustment.d.ts.map +1 -0
- package/dist/invertAdjustment.js +17 -0
- package/dist/invertAdjustment.js.map +1 -0
- package/dist/liftGammaGainAdjustment.d.ts +3 -0
- package/dist/liftGammaGainAdjustment.d.ts.map +1 -0
- package/dist/liftGammaGainAdjustment.js +29 -0
- package/dist/liftGammaGainAdjustment.js.map +1 -0
- package/dist/lookupTableGradeAdjustment.d.ts +3 -0
- package/dist/lookupTableGradeAdjustment.d.ts.map +1 -0
- package/dist/lookupTableGradeAdjustment.js +24 -0
- package/dist/lookupTableGradeAdjustment.js.map +1 -0
- package/dist/sepiaAdjustment.d.ts +3 -0
- package/dist/sepiaAdjustment.d.ts.map +1 -0
- package/dist/sepiaAdjustment.js +15 -0
- package/dist/sepiaAdjustment.js.map +1 -0
- package/package.json +37 -0
- package/src/brightnessContrastAdjustment.test.ts +23 -0
- package/src/channelMixerAdjustment.test.ts +24 -0
- package/src/colorAdjustmentResolution.test.ts +77 -0
- package/src/colorBlindSimulationAdjustment.test.ts +27 -0
- package/src/colorGradeAdjustment.test.ts +36 -0
- package/src/colorLut.test.ts +62 -0
- package/src/colorLutAdjustment.test.ts +37 -0
- package/src/colorLutCache.test.ts +61 -0
- package/src/colorMatrixAdjustment.test.ts +24 -0
- package/src/colorMatrixMath.test.ts +431 -0
- package/src/colorTransformAdjustment.test.ts +40 -0
- package/src/exposureAdjustment.test.ts +22 -0
- package/src/grayscaleAdjustment.test.ts +23 -0
- package/src/hueSaturationAdjustment.test.ts +33 -0
- package/src/invertAdjustment.test.ts +24 -0
- package/src/liftGammaGainAdjustment.test.ts +31 -0
- package/src/lookupTableGradeAdjustment.test.ts +49 -0
- package/src/sepiaAdjustment.test.ts +18 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grayscaleAdjustment.d.ts","sourceRoot":"","sources":["../src/grayscaleAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAK3D,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,GAAG,aAAa,CAAC,CAAM,GACxE,mBAAmB,CAerB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Luma desaturation as a matrix-tier adjustment. `mix(rgb, vec3(luma), intensity)` with ITU-R BT.709
|
|
2
|
+
// weights is a full affine 3×3 (no offset); alpha is unchanged. At intensity 1 every channel becomes
|
|
3
|
+
// the luma. BT.709 (0.2126/0.7152/0.0722) matches the prior full-frame grayscale pass.
|
|
4
|
+
export function createGrayscaleAdjustment(options = {}) {
|
|
5
|
+
const intensity = options.intensity ?? 1;
|
|
6
|
+
const k = intensity;
|
|
7
|
+
const j = 1 - intensity;
|
|
8
|
+
const lr = 0.2126 * k;
|
|
9
|
+
const lg = 0.7152 * k;
|
|
10
|
+
const lb = 0.0722 * k;
|
|
11
|
+
// prettier-ignore
|
|
12
|
+
const colorMatrix = [
|
|
13
|
+
j + lr, lg, lb, 0, 0,
|
|
14
|
+
lr, j + lg, lb, 0, 0,
|
|
15
|
+
lr, lg, j + lb, 0, 0,
|
|
16
|
+
0, 0, 0, 1, 0,
|
|
17
|
+
];
|
|
18
|
+
return { kind: 'GrayscaleAdjustment', ...options, colorMatrix };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=grayscaleAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grayscaleAdjustment.js","sourceRoot":"","sources":["../src/grayscaleAdjustment.ts"],"names":[],"mappings":"AAEA,qGAAqG;AACrG,qGAAqG;AACrG,uFAAuF;AACvF,MAAM,UAAU,yBAAyB,CACvC,UAAuE,EAAE;IAEzE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,SAAS,CAAC;IACpB,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACxB,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC;IACtB,kBAAkB;IAClB,MAAM,WAAW,GAAG;QAClB,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACpB,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACpB,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;QACpB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;KACd,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hueSaturationAdjustment.d.ts","sourceRoot":"","sources":["../src/hueSaturationAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAKvF,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,GAAG,WAAW,CAAC,CAAM,GAC1E,uBAAuB,CAkCzB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Hue/saturation/lightness as a LUT-tier adjustment. The transform is the exact HSL round-trip the old
|
|
2
|
+
// hueSaturationEffect shader did — convert to HSL, rotate hue, scale saturation, offset lightness,
|
|
3
|
+
// convert back — ported faithfully so the look is unchanged while the op now fuses into a baked LUT.
|
|
4
|
+
export function createHueSaturationAdjustment(options = {}) {
|
|
5
|
+
const hue = (options.hue ?? 0) / 360;
|
|
6
|
+
const saturation = options.saturation ?? 1;
|
|
7
|
+
const lightness = options.lightness ?? 0;
|
|
8
|
+
const transform = (out, r, g, b) => {
|
|
9
|
+
const mx = Math.max(r, g, b);
|
|
10
|
+
const mn = Math.min(r, g, b);
|
|
11
|
+
let h = 0;
|
|
12
|
+
let s = 0;
|
|
13
|
+
const l = (mx + mn) * 0.5;
|
|
14
|
+
const d = mx - mn;
|
|
15
|
+
if (d > 0.0001) {
|
|
16
|
+
s = l < 0.5 ? d / (mx + mn) : d / (2 - mx - mn);
|
|
17
|
+
if (mx === r)
|
|
18
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
19
|
+
else if (mx === g)
|
|
20
|
+
h = (b - r) / d + 2;
|
|
21
|
+
else
|
|
22
|
+
h = (r - g) / d + 4;
|
|
23
|
+
h /= 6;
|
|
24
|
+
}
|
|
25
|
+
h = fract(h + hue);
|
|
26
|
+
s = clamp01(s * saturation);
|
|
27
|
+
const ln = clamp01(l + lightness);
|
|
28
|
+
if (s <= 0) {
|
|
29
|
+
out[0] = ln;
|
|
30
|
+
out[1] = ln;
|
|
31
|
+
out[2] = ln;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const q = ln < 0.5 ? ln * (1 + s) : ln + s - ln * s;
|
|
35
|
+
const p = 2 * ln - q;
|
|
36
|
+
out[0] = hue2rgb(p, q, h + 1 / 3);
|
|
37
|
+
out[1] = hue2rgb(p, q, h);
|
|
38
|
+
out[2] = hue2rgb(p, q, h - 1 / 3);
|
|
39
|
+
};
|
|
40
|
+
return { kind: 'HueSaturationAdjustment', ...options, transform };
|
|
41
|
+
}
|
|
42
|
+
function clamp01(v) {
|
|
43
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
44
|
+
}
|
|
45
|
+
function fract(v) {
|
|
46
|
+
return v - Math.floor(v);
|
|
47
|
+
}
|
|
48
|
+
function hue2rgb(p, q, tRaw) {
|
|
49
|
+
let t = tRaw;
|
|
50
|
+
if (t < 0)
|
|
51
|
+
t += 1;
|
|
52
|
+
if (t > 1)
|
|
53
|
+
t -= 1;
|
|
54
|
+
if (t < 1 / 6)
|
|
55
|
+
return p + (q - p) * 6 * t;
|
|
56
|
+
if (t < 1 / 2)
|
|
57
|
+
return q;
|
|
58
|
+
if (t < 2 / 3)
|
|
59
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
60
|
+
return p;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=hueSaturationAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hueSaturationAdjustment.js","sourceRoot":"","sources":["../src/hueSaturationAdjustment.ts"],"names":[],"mappings":"AAEA,uGAAuG;AACvG,mGAAmG;AACnG,qGAAqG;AACrG,MAAM,UAAU,6BAA6B,CAC3C,UAAyE,EAAE;IAE3E,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;IACrC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;IACzC,MAAM,SAAS,GAA2B,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QACzD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC;QAC1B,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC;YACf,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAChD,IAAI,EAAE,KAAK,CAAC;gBAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC3C,IAAI,EAAE,KAAK,CAAC;gBAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;gBAClC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;QACD,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACnB,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;QAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACX,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACZ,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACZ,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QACD,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrB,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAClC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,CAAC;AACpE,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,OAAO,CAAC,CAAS,EAAE,CAAS,EAAE,IAAY;IACjD,IAAI,CAAC,GAAG,IAAI,CAAC;IACb,IAAI,CAAC,GAAG,CAAC;QAAE,CAAC,IAAI,CAAC,CAAC;IAClB,IAAI,CAAC,GAAG,CAAC;QAAE,CAAC,IAAI,CAAC,CAAC;IAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACpD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from './brightnessContrastAdjustment';
|
|
2
|
+
export * from './channelMixerAdjustment';
|
|
3
|
+
export * from './colorAdjustmentResolution';
|
|
4
|
+
export * from './colorBlindSimulationAdjustment';
|
|
5
|
+
export * from './colorGradeAdjustment';
|
|
6
|
+
export * from './colorLut';
|
|
7
|
+
export * from './colorLutAdjustment';
|
|
8
|
+
export * from './colorLutCache';
|
|
9
|
+
export * from './colorMatrixAdjustment';
|
|
10
|
+
export * from './colorMatrixMath';
|
|
11
|
+
export * from './colorTransformAdjustment';
|
|
12
|
+
export * from './exposureAdjustment';
|
|
13
|
+
export * from './grayscaleAdjustment';
|
|
14
|
+
export * from './hueSaturationAdjustment';
|
|
15
|
+
export * from './invertAdjustment';
|
|
16
|
+
export * from './liftGammaGainAdjustment';
|
|
17
|
+
export * from './lookupTableGradeAdjustment';
|
|
18
|
+
export * from './sepiaAdjustment';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from './brightnessContrastAdjustment';
|
|
2
|
+
export * from './channelMixerAdjustment';
|
|
3
|
+
export * from './colorAdjustmentResolution';
|
|
4
|
+
export * from './colorBlindSimulationAdjustment';
|
|
5
|
+
export * from './colorGradeAdjustment';
|
|
6
|
+
export * from './colorLut';
|
|
7
|
+
export * from './colorLutAdjustment';
|
|
8
|
+
export * from './colorLutCache';
|
|
9
|
+
export * from './colorMatrixAdjustment';
|
|
10
|
+
export * from './colorMatrixMath';
|
|
11
|
+
export * from './colorTransformAdjustment';
|
|
12
|
+
export * from './exposureAdjustment';
|
|
13
|
+
export * from './grayscaleAdjustment';
|
|
14
|
+
export * from './hueSaturationAdjustment';
|
|
15
|
+
export * from './invertAdjustment';
|
|
16
|
+
export * from './liftGammaGainAdjustment';
|
|
17
|
+
export * from './lookupTableGradeAdjustment';
|
|
18
|
+
export * from './sepiaAdjustment';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invertAdjustment.d.ts","sourceRoot":"","sources":["../src/invertAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKxD,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,aAAa,CAAC,CAAM,GACrE,gBAAgB,CAYlB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Channel invert as a matrix-tier adjustment. `mix(rgb, 1 - rgb, intensity)` is affine per channel —
|
|
2
|
+
// scale `1 - 2·intensity`, offset `intensity` (255·intensity in the 0–255 offset convention). Alpha is
|
|
3
|
+
// unchanged. At intensity 1 this is the classic invert (scale −1, offset 255).
|
|
4
|
+
export function createInvertAdjustment(options = {}) {
|
|
5
|
+
const intensity = options.intensity ?? 1;
|
|
6
|
+
const s = 1 - 2 * intensity;
|
|
7
|
+
const o = 255 * intensity;
|
|
8
|
+
// prettier-ignore
|
|
9
|
+
const colorMatrix = [
|
|
10
|
+
s, 0, 0, 0, o,
|
|
11
|
+
0, s, 0, 0, o,
|
|
12
|
+
0, 0, s, 0, o,
|
|
13
|
+
0, 0, 0, 1, 0,
|
|
14
|
+
];
|
|
15
|
+
return { kind: 'InvertAdjustment', ...options, colorMatrix };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=invertAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invertAdjustment.js","sourceRoot":"","sources":["../src/invertAdjustment.ts"],"names":[],"mappings":"AAEA,qGAAqG;AACrG,uGAAuG;AACvG,+EAA+E;AAC/E,MAAM,UAAU,sBAAsB,CACpC,UAAoE,EAAE;IAEtE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC;IAC1B,kBAAkB;IAClB,MAAM,WAAW,GAAG;QAClB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACb,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;KACd,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liftGammaGainAdjustment.d.ts","sourceRoot":"","sources":["../src/liftGammaGainAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAKvF,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,GAAG,WAAW,CAAC,CAAM,GAC1E,uBAAuB,CAgBzB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Lift/gamma/gain as a LUT-tier adjustment (gamma is the nonlinear part). Ported faithfully from the old
|
|
2
|
+
// liftGammaGainEffect shader: gain multiplies, lift offsets toward the packed neutral, gamma applies a
|
|
3
|
+
// per-channel power curve. Packed-RGBA neutrals: lift 0x000000ff, gamma 0x808080ff, gain 0xffffffff.
|
|
4
|
+
export function createLiftGammaGainAdjustment(options = {}) {
|
|
5
|
+
const lift = unpackRgb(options.lift ?? 0x000000ff);
|
|
6
|
+
const gammaRaw = unpackRgb(options.gamma ?? 0x808080ff);
|
|
7
|
+
const gain = unpackRgb(options.gain ?? 0xffffffff);
|
|
8
|
+
// Map gamma's 0.5-neutral to a 1.0-neutral exponent so 0x808080 leaves the image unchanged.
|
|
9
|
+
const gammaExp = [
|
|
10
|
+
1 / Math.max(gammaRaw[0] * 2, 1e-3),
|
|
11
|
+
1 / Math.max(gammaRaw[1] * 2, 1e-3),
|
|
12
|
+
1 / Math.max(gammaRaw[2] * 2, 1e-3),
|
|
13
|
+
];
|
|
14
|
+
const transform = (out, r, g, b) => {
|
|
15
|
+
out[0] = clamp01(Math.pow(Math.max(r * gain[0] + lift[0] * (1 - r), 0), gammaExp[0]));
|
|
16
|
+
out[1] = clamp01(Math.pow(Math.max(g * gain[1] + lift[1] * (1 - g), 0), gammaExp[1]));
|
|
17
|
+
out[2] = clamp01(Math.pow(Math.max(b * gain[2] + lift[2] * (1 - b), 0), gammaExp[2]));
|
|
18
|
+
};
|
|
19
|
+
return { kind: 'LiftGammaGainAdjustment', ...options, transform };
|
|
20
|
+
}
|
|
21
|
+
function clamp01(v) {
|
|
22
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
23
|
+
}
|
|
24
|
+
// Unpacks a packed RGBA integer (0xRRGGBBAA) into normalized [r, g, b]. Alpha is dropped — grade values
|
|
25
|
+
// describe RGB channels only.
|
|
26
|
+
function unpackRgb(c) {
|
|
27
|
+
return [((c >>> 24) & 255) / 255, ((c >>> 16) & 255) / 255, ((c >>> 8) & 255) / 255];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=liftGammaGainAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liftGammaGainAdjustment.js","sourceRoot":"","sources":["../src/liftGammaGainAdjustment.ts"],"names":[],"mappings":"AAEA,yGAAyG;AACzG,uGAAuG;AACvG,qGAAqG;AACrG,MAAM,UAAU,6BAA6B,CAC3C,UAAyE,EAAE;IAE3E,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,CAAC;IACnD,4FAA4F;IAC5F,MAAM,QAAQ,GAA6B;QACzC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;QACnC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;QACnC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;KACpC,CAAC;IACF,MAAM,SAAS,GAA2B,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QACzD,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtF,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtF,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,CAAC;AACpE,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,wGAAwG;AACxG,8BAA8B;AAC9B,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AACvF,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LookupTableGradeAdjustment } from '@flighthq/types';
|
|
2
|
+
export declare function createLookupTableGradeAdjustment(options?: Readonly<Omit<LookupTableGradeAdjustment, 'kind' | 'transform'>>): LookupTableGradeAdjustment;
|
|
3
|
+
//# sourceMappingURL=lookupTableGradeAdjustment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookupTableGradeAdjustment.d.ts","sourceRoot":"","sources":["../src/lookupTableGradeAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAS1F,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,0BAA0B,EAAE,MAAM,GAAG,WAAW,CAAC,CAAM,GAC7E,0BAA0B,CAgB5B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sampleColorLut } from './colorLut';
|
|
2
|
+
// A supplied 3D color grade LUT applied at `strength`. Unlike the other LUT-tier ops it is ALREADY a LUT,
|
|
3
|
+
// so it carries one directly; its transform trilinearly samples the carried `lut` and mixes toward it by
|
|
4
|
+
// `strength` (0 = original, 1 = full grade). With no `lut` supplied it is identity (a neutral
|
|
5
|
+
// passthrough), matching the old lookupTableGradeEffect. Because it exposes a `transform`, it fuses with
|
|
6
|
+
// neighbouring adjustments — a run containing it (matrices included) bakes into one LUT.
|
|
7
|
+
export function createLookupTableGradeAdjustment(options = {}) {
|
|
8
|
+
const lut = options.lut;
|
|
9
|
+
const strength = options.strength ?? 1;
|
|
10
|
+
const transform = (out, r, g, b) => {
|
|
11
|
+
if (lut === undefined || strength <= 0) {
|
|
12
|
+
out[0] = r;
|
|
13
|
+
out[1] = g;
|
|
14
|
+
out[2] = b;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
sampleColorLut(lut, out, r, g, b);
|
|
18
|
+
out[0] = r + (out[0] - r) * strength;
|
|
19
|
+
out[1] = g + (out[1] - g) * strength;
|
|
20
|
+
out[2] = b + (out[2] - b) * strength;
|
|
21
|
+
};
|
|
22
|
+
return { kind: 'LookupTableGradeAdjustment', ...options, transform };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=lookupTableGradeAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookupTableGradeAdjustment.js","sourceRoot":"","sources":["../src/lookupTableGradeAdjustment.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,0GAA0G;AAC1G,yGAAyG;AACzG,8FAA8F;AAC9F,yGAAyG;AACzG,yFAAyF;AACzF,MAAM,UAAU,gCAAgC,CAC9C,UAA4E,EAAE;IAE9E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IACvC,MAAM,SAAS,GAA2B,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QACzD,IAAI,GAAG,KAAK,SAAS,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACX,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACX,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACX,OAAO;QACT,CAAC;QACD,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAClC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;QACrC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;QACrC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACvC,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,4BAA4B,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sepiaAdjustment.d.ts","sourceRoot":"","sources":["../src/sepiaAdjustment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,CAAC,CAAM,GACpE,eAAe,CAWjB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Sepia tone as a matrix-tier adjustment. `mix(rgb, sepia·rgb, intensity)` is affine 3×3 (no offset);
|
|
2
|
+
// alpha is unchanged. At intensity 1 this is the standard sepia matrix used across CSS/Flash.
|
|
3
|
+
export function createSepiaAdjustment(options = {}) {
|
|
4
|
+
const k = options.intensity ?? 1;
|
|
5
|
+
const j = 1 - k;
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
const colorMatrix = [
|
|
8
|
+
j + 0.393 * k, 0.769 * k, 0.189 * k, 0, 0,
|
|
9
|
+
0.349 * k, j + 0.686 * k, 0.168 * k, 0, 0,
|
|
10
|
+
0.272 * k, 0.534 * k, j + 0.131 * k, 0, 0,
|
|
11
|
+
0, 0, 0, 1, 0,
|
|
12
|
+
];
|
|
13
|
+
return { kind: 'SepiaAdjustment', ...options, colorMatrix };
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=sepiaAdjustment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sepiaAdjustment.js","sourceRoot":"","sources":["../src/sepiaAdjustment.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,8FAA8F;AAC9F,MAAM,UAAU,qBAAqB,CACnC,UAAmE,EAAE;IAErE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,kBAAkB;IAClB,MAAM,WAAW,GAAG;QAClB,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;KACd,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,CAAC;AAC9D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/adjustments",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/types": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
},
|
|
35
|
+
"description": "Pointwise value-remap adjustment descriptors and color-matrix fuse math",
|
|
36
|
+
"sideEffects": false
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createBrightnessContrastAdjustment } from './brightnessContrastAdjustment';
|
|
2
|
+
import { applyColorMatrixToColor } from './colorMatrixMath';
|
|
3
|
+
|
|
4
|
+
describe('createBrightnessContrastAdjustment', () => {
|
|
5
|
+
it('defaults to the identity (brightness 0, contrast 1)', () => {
|
|
6
|
+
const adjustment = createBrightnessContrastAdjustment();
|
|
7
|
+
expect(adjustment.kind).toBe('BrightnessContrastAdjustment');
|
|
8
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0x3366aaff)).toBe(0x3366aaff);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('scales contrast about mid-grey 0.5, leaving mid-grey fixed', () => {
|
|
12
|
+
const adjustment = createBrightnessContrastAdjustment({ contrast: 0.5 });
|
|
13
|
+
// 128 is the pivot → unchanged; black lifts toward mid-grey (255·0.5·0.5 = 63.75 → 64).
|
|
14
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0x808080ff)).toBe(0x808080ff);
|
|
15
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0x000000ff)).toBe(0x404040ff);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('reproduces the prior shader (rgb + brightness − 0.5)·contrast + 0.5', () => {
|
|
19
|
+
// Scene values: brightness 0.15, contrast 0.35. White → (1 + 0.15 − 0.5)·0.35 + 0.5 = 0.7275 → 186.
|
|
20
|
+
const adjustment = createBrightnessContrastAdjustment({ brightness: 0.15, contrast: 0.35 });
|
|
21
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0xffffffff)).toBe(0xbababaff);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createChannelMixerAdjustment } from './channelMixerAdjustment';
|
|
2
|
+
import { applyColorMatrixToColor } from './colorMatrixMath';
|
|
3
|
+
|
|
4
|
+
describe('createChannelMixerAdjustment', () => {
|
|
5
|
+
it('rotates channels via a 3×4 row-major mix and carries the fusable kind', () => {
|
|
6
|
+
// R<-B, G<-R, B<-G — the same mix the functional scene uses.
|
|
7
|
+
const adjustment = createChannelMixerAdjustment({ matrix: [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0] });
|
|
8
|
+
expect(adjustment.kind).toBe('ChannelMixerAdjustment');
|
|
9
|
+
expect(adjustment.colorMatrix).toHaveLength(20);
|
|
10
|
+
// 0xAABBCC → R'=CC (from blue), G'=AA (from red), B'=BB (from green); alpha preserved.
|
|
11
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0xaabbccff)).toBe(0xccaabbff);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('defaults to the identity mix', () => {
|
|
15
|
+
const adjustment = createChannelMixerAdjustment();
|
|
16
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0x123456ff)).toBe(0x123456ff);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('applies the per-row offset in normalized 0–1, scaled to the 0–255 column', () => {
|
|
20
|
+
// Identity mix plus a full-white offset on every row → saturates to white.
|
|
21
|
+
const adjustment = createChannelMixerAdjustment({ matrix: [1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1] });
|
|
22
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0x102030ff)).toBe(0xffffffff);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Adjustment, ColorTransform } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
COLOR_ADJUSTMENT_AFFINE,
|
|
5
|
+
COLOR_ADJUSTMENT_CHANNEL_MIXING,
|
|
6
|
+
COLOR_ADJUSTMENT_NONE,
|
|
7
|
+
isAffineColorMatrix,
|
|
8
|
+
resolveColorAdjustmentsColorTransform,
|
|
9
|
+
} from './colorAdjustmentResolution';
|
|
10
|
+
import { createIdentityColorMatrix, createSaturationColorMatrix } from './colorMatrixMath';
|
|
11
|
+
import { createColorTransformAdjustment } from './colorTransformAdjustment';
|
|
12
|
+
|
|
13
|
+
function makeColorTransform(fields: Partial<ColorTransform> = {}): ColorTransform {
|
|
14
|
+
return {
|
|
15
|
+
redMultiplier: 1,
|
|
16
|
+
greenMultiplier: 1,
|
|
17
|
+
blueMultiplier: 1,
|
|
18
|
+
alphaMultiplier: 1,
|
|
19
|
+
redOffset: 0,
|
|
20
|
+
greenOffset: 0,
|
|
21
|
+
blueOffset: 0,
|
|
22
|
+
alphaOffset: 0,
|
|
23
|
+
...fields,
|
|
24
|
+
} as ColorTransform;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('isAffineColorMatrix', () => {
|
|
28
|
+
it('is true for a diagonal identity matrix', () => {
|
|
29
|
+
expect(isAffineColorMatrix(createIdentityColorMatrix())).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('is false when off-diagonal channel-mixing terms are present', () => {
|
|
33
|
+
expect(isAffineColorMatrix(createSaturationColorMatrix(0))).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('resolveColorAdjustmentsColorTransform', () => {
|
|
38
|
+
it('returns NONE for a null or empty stack', () => {
|
|
39
|
+
const out = makeColorTransform();
|
|
40
|
+
expect(resolveColorAdjustmentsColorTransform(null, out)).toBe(COLOR_ADJUSTMENT_NONE);
|
|
41
|
+
expect(resolveColorAdjustmentsColorTransform([], out)).toBe(COLOR_ADJUSTMENT_NONE);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('resolves a single ColorTransformAdjustment exactly (affine)', () => {
|
|
45
|
+
const adjustment = createColorTransformAdjustment(
|
|
46
|
+
makeColorTransform({ redMultiplier: 0.5, greenMultiplier: 0, redOffset: 40 }),
|
|
47
|
+
);
|
|
48
|
+
const out = makeColorTransform();
|
|
49
|
+
expect(resolveColorAdjustmentsColorTransform([adjustment], out)).toBe(COLOR_ADJUSTMENT_AFFINE);
|
|
50
|
+
expect(out.redMultiplier).toBe(0.5);
|
|
51
|
+
expect(out.greenMultiplier).toBe(0);
|
|
52
|
+
expect(out.redOffset).toBe(40);
|
|
53
|
+
expect(out.alphaMultiplier).toBe(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('fuses two affine adjustments (multipliers compose)', () => {
|
|
57
|
+
const a = createColorTransformAdjustment(makeColorTransform({ redMultiplier: 0.5 }));
|
|
58
|
+
const b = createColorTransformAdjustment(makeColorTransform({ redMultiplier: 0.5 }));
|
|
59
|
+
const out = makeColorTransform();
|
|
60
|
+
expect(resolveColorAdjustmentsColorTransform([a, b], out)).toBe(COLOR_ADJUSTMENT_AFFINE);
|
|
61
|
+
expect(out.redMultiplier).toBe(0.25);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('reports channel-mixing and writes only the affine part for an off-diagonal stack', () => {
|
|
65
|
+
const saturation: Adjustment = { kind: 'Saturation', colorMatrix: createSaturationColorMatrix(0) } as Adjustment;
|
|
66
|
+
const out = makeColorTransform();
|
|
67
|
+
expect(resolveColorAdjustmentsColorTransform([saturation], out)).toBe(COLOR_ADJUSTMENT_CHANNEL_MIXING);
|
|
68
|
+
// Only the diagonal (grayscale luma weight for red) is written; off-diagonal mix is dropped.
|
|
69
|
+
expect(out.redMultiplier).toBeCloseTo(0.299);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('reports channel-mixing when a non-matrix (LUT) op is present', () => {
|
|
73
|
+
const lut: Adjustment = { kind: 'acme.Lut' };
|
|
74
|
+
const out = makeColorTransform();
|
|
75
|
+
expect(resolveColorAdjustmentsColorTransform([lut], out)).toBe(COLOR_ADJUSTMENT_CHANNEL_MIXING);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createColorBlindSimulationAdjustment } from './colorBlindSimulationAdjustment';
|
|
2
|
+
import { applyColorMatrixToColor } from './colorMatrixMath';
|
|
3
|
+
|
|
4
|
+
describe('createColorBlindSimulationAdjustment', () => {
|
|
5
|
+
it('defaults to deuteranopia and bakes its linear matrix, preserving alpha', () => {
|
|
6
|
+
const adjustment = createColorBlindSimulationAdjustment();
|
|
7
|
+
expect(adjustment.kind).toBe('ColorBlindSimulationAdjustment');
|
|
8
|
+
expect(adjustment.colorMatrix).toHaveLength(20);
|
|
9
|
+
// Pure red under deuteranopia: R'=0.625·255≈159, G'=0.7·255≈179, B'=0; alpha preserved.
|
|
10
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0xff0000ff)).toBe(0x9fb300ff);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('achromatopsia collapses to equal-channel luma (monochrome)', () => {
|
|
14
|
+
const adjustment = createColorBlindSimulationAdjustment({ type: 'achromatopsia' });
|
|
15
|
+
// Pure red → 0.299·255≈76 across all channels.
|
|
16
|
+
expect(applyColorMatrixToColor(adjustment.colorMatrix as number[], 0xff0000ff)).toBe(0x4c4c4cff);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('selects a distinct matrix per type', () => {
|
|
20
|
+
const protan = createColorBlindSimulationAdjustment({ type: 'protanopia' });
|
|
21
|
+
const tritan = createColorBlindSimulationAdjustment({ type: 'tritanopia' });
|
|
22
|
+
const green = 0x00ff00ff;
|
|
23
|
+
expect(applyColorMatrixToColor(protan.colorMatrix as number[], green)).not.toBe(
|
|
24
|
+
applyColorMatrixToColor(tritan.colorMatrix as number[], green),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createColorGradeAdjustment } from './colorGradeAdjustment';
|
|
2
|
+
|
|
3
|
+
describe('createColorGradeAdjustment', () => {
|
|
4
|
+
it('defaults to approximately neutral (contrast 1, all stages near-neutral)', () => {
|
|
5
|
+
// The lift/gamma/gain stage's packed neutral 0x808080 gamma is 128/255 ≈ 0.502, so the default is
|
|
6
|
+
// near- (not exactly) neutral — faithful to the ported shader.
|
|
7
|
+
const adjustment = createColorGradeAdjustment();
|
|
8
|
+
expect(adjustment.kind).toBe('ColorGradeAdjustment');
|
|
9
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
10
|
+
adjustment.transform(out, 0.3, 0.5, 0.7);
|
|
11
|
+
expect(out[0]).toBeCloseTo(0.3, 2);
|
|
12
|
+
expect(out[1]).toBeCloseTo(0.5, 2);
|
|
13
|
+
expect(out[2]).toBeCloseTo(0.7, 2);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('scales contrast about mid-grey', () => {
|
|
17
|
+
const adjustment = createColorGradeAdjustment({ contrast: 2 });
|
|
18
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
19
|
+
adjustment.transform(out, 0.5, 0.5, 0.5);
|
|
20
|
+
// Mid-grey is the pivot → unchanged (within the near-neutral default gamma).
|
|
21
|
+
expect(out[0]).toBeCloseTo(0.5, 2);
|
|
22
|
+
adjustment.transform(out, 0.75, 0.75, 0.75);
|
|
23
|
+
// (0.75 - 0.5)·2 + 0.5 = 1, and pow(1, gamma) = 1 exactly.
|
|
24
|
+
expect(out[0]).toBeCloseTo(1, 4);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('desaturates toward luma at saturation 0', () => {
|
|
28
|
+
const adjustment = createColorGradeAdjustment({ saturation: 0 });
|
|
29
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
30
|
+
adjustment.transform(out, 1, 0, 0);
|
|
31
|
+
// Rec.709 luma of pure red is 0.2126 on every channel (within the near-neutral default gamma).
|
|
32
|
+
expect(out[0]).toBeCloseTo(0.2126, 2);
|
|
33
|
+
expect(out[1]).toBeCloseTo(0.2126, 2);
|
|
34
|
+
expect(out[2]).toBeCloseTo(0.2126, 2);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ColorTransformFunction } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { bakeColorLut, COLOR_LUT_DEFAULT_SIZE, sampleColorLut } from './colorLut';
|
|
4
|
+
|
|
5
|
+
describe('bakeColorLut', () => {
|
|
6
|
+
it('bakes an empty stack into an identity LUT', () => {
|
|
7
|
+
const lut = bakeColorLut([], 8);
|
|
8
|
+
expect(lut.size).toBe(8);
|
|
9
|
+
expect(lut.samples).toHaveLength(8 * 8 * 8 * 3);
|
|
10
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
11
|
+
sampleColorLut(lut, out, 0.25, 0.5, 0.75);
|
|
12
|
+
expect(out[0]).toBeCloseTo(0.25, 5);
|
|
13
|
+
expect(out[1]).toBeCloseTo(0.5, 5);
|
|
14
|
+
expect(out[2]).toBeCloseTo(0.75, 5);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('composes transforms left-to-right and clamps to [0, 1]', () => {
|
|
18
|
+
const invert: ColorTransformFunction = (out, r, g, b) => {
|
|
19
|
+
out[0] = 1 - r;
|
|
20
|
+
out[1] = 1 - g;
|
|
21
|
+
out[2] = 1 - b;
|
|
22
|
+
};
|
|
23
|
+
const brighten: ColorTransformFunction = (out, r, g, b) => {
|
|
24
|
+
out[0] = r + 0.5;
|
|
25
|
+
out[1] = g + 0.5;
|
|
26
|
+
out[2] = b + 0.5;
|
|
27
|
+
};
|
|
28
|
+
const lut = bakeColorLut([invert, brighten], 16);
|
|
29
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
30
|
+
// invert(0.2)=0.8, +0.5 = 1.3 → clamped to 1.
|
|
31
|
+
sampleColorLut(lut, out, 0.2, 0.2, 0.2);
|
|
32
|
+
expect(out[0]).toBeCloseTo(1, 5);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('defaults to the standard resolution', () => {
|
|
36
|
+
const lut = bakeColorLut([]);
|
|
37
|
+
expect(lut.size).toBe(COLOR_LUT_DEFAULT_SIZE);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('sampleColorLut', () => {
|
|
42
|
+
it('interpolates between cells trilinearly', () => {
|
|
43
|
+
const ramp: ColorTransformFunction = (out, r) => {
|
|
44
|
+
out[0] = r;
|
|
45
|
+
out[1] = r;
|
|
46
|
+
out[2] = r;
|
|
47
|
+
};
|
|
48
|
+
const lut = bakeColorLut([ramp], 2);
|
|
49
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
50
|
+
sampleColorLut(lut, out, 0.5, 0, 0);
|
|
51
|
+
expect(out[0]).toBeCloseTo(0.5, 5);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('clamps out-of-range inputs to the cube', () => {
|
|
55
|
+
const lut = bakeColorLut([], 4);
|
|
56
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
57
|
+
sampleColorLut(lut, out, 2, -1, 0.5);
|
|
58
|
+
expect(out[0]).toBeCloseTo(1, 5);
|
|
59
|
+
expect(out[1]).toBeCloseTo(0, 5);
|
|
60
|
+
expect(out[2]).toBeCloseTo(0.5, 5);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getAdjustmentColorTransform, isColorLutAdjustment } from './colorLutAdjustment';
|
|
2
|
+
import { createHueSaturationAdjustment } from './hueSaturationAdjustment';
|
|
3
|
+
import { createInvertAdjustment } from './invertAdjustment';
|
|
4
|
+
|
|
5
|
+
describe('getAdjustmentColorTransform', () => {
|
|
6
|
+
it('returns a LUT-tier adjustment its own transform', () => {
|
|
7
|
+
const op = createHueSaturationAdjustment({ hue: 45 });
|
|
8
|
+
expect(getAdjustmentColorTransform(op)).toBe(op.transform);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('wraps a matrix-tier adjustment as an rgb→rgb transform at opaque alpha', () => {
|
|
12
|
+
const op = createInvertAdjustment();
|
|
13
|
+
const fn = getAdjustmentColorTransform(op);
|
|
14
|
+
expect(fn).not.toBeNull();
|
|
15
|
+
const out: [number, number, number] = [0, 0, 0];
|
|
16
|
+
fn!(out, 1, 0, 0);
|
|
17
|
+
// Full invert: red → cyan.
|
|
18
|
+
expect(out[0]).toBeCloseTo(0, 5);
|
|
19
|
+
expect(out[1]).toBeCloseTo(1, 5);
|
|
20
|
+
expect(out[2]).toBeCloseTo(1, 5);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns null for a non-pointwise operation', () => {
|
|
24
|
+
expect(getAdjustmentColorTransform({ kind: 'BlurEffect' })).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('isColorLutAdjustment', () => {
|
|
29
|
+
it('is true for a LUT-tier adjustment', () => {
|
|
30
|
+
expect(isColorLutAdjustment(createHueSaturationAdjustment())).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('is false for a matrix-tier adjustment and for a plain effect', () => {
|
|
34
|
+
expect(isColorLutAdjustment(createInvertAdjustment())).toBe(false);
|
|
35
|
+
expect(isColorLutAdjustment({ kind: 'BlurEffect' })).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|