@pixerate/editor 0.22.0 → 0.24.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.
Files changed (188) hide show
  1. package/README.md +175 -2
  2. package/THIRD_PARTY_NOTICES.md +139 -0
  3. package/dist/agent/index.cjs +87 -111
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +70 -211
  6. package/dist/agent/index.d.ts +70 -211
  7. package/dist/agent/index.js +78 -102
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/arrangeShortcuts-B8O3f8-P.d.cts +184 -0
  10. package/dist/arrangeShortcuts-sLlD2rSX.d.ts +184 -0
  11. package/dist/canvas/index.cjs +2075 -86
  12. package/dist/canvas/index.cjs.map +1 -1
  13. package/dist/canvas/index.d.cts +608 -186
  14. package/dist/canvas/index.d.ts +608 -186
  15. package/dist/canvas/index.js +2060 -71
  16. package/dist/canvas/index.js.map +1 -1
  17. package/dist/chunk-26BVFKHU.cjs +453 -0
  18. package/dist/chunk-26BVFKHU.cjs.map +1 -0
  19. package/dist/chunk-2H7ZHPEJ.js +762 -0
  20. package/dist/chunk-2H7ZHPEJ.js.map +1 -0
  21. package/dist/{chunk-DENSCKFC.js → chunk-3CN2WD3X.js} +1730 -189
  22. package/dist/chunk-3CN2WD3X.js.map +1 -0
  23. package/dist/{chunk-JJDLPEWL.js → chunk-3LV6RRPH.js} +2 -2
  24. package/dist/{chunk-MJZBNWG7.js → chunk-5S3Z4PPF.js} +78 -557
  25. package/dist/chunk-5S3Z4PPF.js.map +1 -0
  26. package/dist/chunk-664LQYFJ.js +115 -0
  27. package/dist/chunk-664LQYFJ.js.map +1 -0
  28. package/dist/chunk-7DWMJCAT.js +453 -0
  29. package/dist/chunk-7DWMJCAT.js.map +1 -0
  30. package/dist/chunk-ARRTP33I.js +101 -0
  31. package/dist/chunk-ARRTP33I.js.map +1 -0
  32. package/dist/chunk-CPOKFNBG.cjs +101 -0
  33. package/dist/chunk-CPOKFNBG.cjs.map +1 -0
  34. package/dist/{chunk-AA4ZEVCC.cjs → chunk-D27YFC46.cjs} +101 -580
  35. package/dist/chunk-D27YFC46.cjs.map +1 -0
  36. package/dist/chunk-D6FH2YAX.cjs +131 -0
  37. package/dist/chunk-D6FH2YAX.cjs.map +1 -0
  38. package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
  39. package/dist/chunk-FD4LPFAE.cjs.map +1 -0
  40. package/dist/chunk-FODNLX5N.js +749 -0
  41. package/dist/chunk-FODNLX5N.js.map +1 -0
  42. package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
  43. package/dist/chunk-FPENXAEJ.js.map +1 -0
  44. package/dist/chunk-G2I4QJOT.cjs +762 -0
  45. package/dist/chunk-G2I4QJOT.cjs.map +1 -0
  46. package/dist/chunk-HLIMTC6N.js +1 -0
  47. package/dist/chunk-HLIMTC6N.js.map +1 -0
  48. package/dist/{chunk-QGHGJ56O.js → chunk-ITBSKI26.js} +1483 -19
  49. package/dist/chunk-ITBSKI26.js.map +1 -0
  50. package/dist/{chunk-6M3BCTBD.cjs → chunk-MA2F5KHF.cjs} +470 -242
  51. package/dist/chunk-MA2F5KHF.cjs.map +1 -0
  52. package/dist/{chunk-SAZRENEZ.cjs → chunk-MCSJFWAO.cjs} +1733 -192
  53. package/dist/chunk-MCSJFWAO.cjs.map +1 -0
  54. package/dist/chunk-MKA3AHLV.cjs +256 -0
  55. package/dist/chunk-MKA3AHLV.cjs.map +1 -0
  56. package/dist/chunk-OLZJXL4R.cjs +749 -0
  57. package/dist/chunk-OLZJXL4R.cjs.map +1 -0
  58. package/dist/chunk-OXSV64JX.js +689 -0
  59. package/dist/chunk-OXSV64JX.js.map +1 -0
  60. package/dist/chunk-P3RQ5UJE.cjs +689 -0
  61. package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
  62. package/dist/chunk-P6ODPZ4W.js +256 -0
  63. package/dist/chunk-P6ODPZ4W.js.map +1 -0
  64. package/dist/chunk-PGNZKFQS.js +685 -0
  65. package/dist/chunk-PGNZKFQS.js.map +1 -0
  66. package/dist/chunk-PL4JB3BF.cjs +1 -0
  67. package/dist/chunk-PL4JB3BF.cjs.map +1 -0
  68. package/dist/{chunk-GVSMRPHB.cjs → chunk-SBCZ6RN5.cjs} +1568 -104
  69. package/dist/chunk-SBCZ6RN5.cjs.map +1 -0
  70. package/dist/{chunk-CL37JGFW.cjs → chunk-SML64VJG.cjs} +4 -4
  71. package/dist/{chunk-CL37JGFW.cjs.map → chunk-SML64VJG.cjs.map} +1 -1
  72. package/dist/chunk-TNNJXF46.cjs +115 -0
  73. package/dist/chunk-TNNJXF46.cjs.map +1 -0
  74. package/dist/chunk-U5HZSFCS.js +131 -0
  75. package/dist/chunk-U5HZSFCS.js.map +1 -0
  76. package/dist/{chunk-RU52I7QO.js → chunk-U5KWZU4X.js} +388 -160
  77. package/dist/chunk-U5KWZU4X.js.map +1 -0
  78. package/dist/chunk-XNORY7M6.cjs +685 -0
  79. package/dist/chunk-XNORY7M6.cjs.map +1 -0
  80. package/dist/commands/index.cjs +860 -0
  81. package/dist/commands/index.cjs.map +1 -0
  82. package/dist/commands/index.d.cts +244 -0
  83. package/dist/commands/index.d.ts +244 -0
  84. package/dist/commands/index.js +860 -0
  85. package/dist/commands/index.js.map +1 -0
  86. package/dist/comment-store-BEMgVlAA.d.cts +135 -0
  87. package/dist/comment-store-BEMgVlAA.d.ts +135 -0
  88. package/dist/compare/index.cjs +15 -0
  89. package/dist/compare/index.cjs.map +1 -0
  90. package/dist/compare/index.d.cts +58 -0
  91. package/dist/compare/index.d.ts +58 -0
  92. package/dist/compare/index.js +15 -0
  93. package/dist/compare/index.js.map +1 -0
  94. package/dist/{controller-B7GIybc4.d.cts → controller-BhLLAoi8.d.cts} +119 -5
  95. package/dist/{controller-B7GIybc4.d.ts → controller-BhLLAoi8.d.ts} +119 -5
  96. package/dist/documents-BQjzJNe2.d.ts +76 -0
  97. package/dist/documents-KRTPhvst.d.cts +76 -0
  98. package/dist/extensions/index.cjs +53 -5
  99. package/dist/extensions/index.cjs.map +1 -1
  100. package/dist/extensions/index.d.cts +7 -3
  101. package/dist/extensions/index.d.ts +7 -3
  102. package/dist/extensions/index.js +52 -4
  103. package/dist/generation/index.cjs +1373 -0
  104. package/dist/generation/index.cjs.map +1 -0
  105. package/dist/generation/index.d.cts +554 -0
  106. package/dist/generation/index.d.ts +554 -0
  107. package/dist/generation/index.js +1373 -0
  108. package/dist/generation/index.js.map +1 -0
  109. package/dist/grammar/index.cjs +48 -3
  110. package/dist/grammar/index.cjs.map +1 -1
  111. package/dist/grammar/index.d.cts +322 -100
  112. package/dist/grammar/index.d.ts +322 -100
  113. package/dist/grammar/index.js +54 -9
  114. package/dist/history/index.cjs +12 -2
  115. package/dist/history/index.cjs.map +1 -1
  116. package/dist/history/index.d.cts +211 -7
  117. package/dist/history/index.d.ts +211 -7
  118. package/dist/history/index.js +13 -3
  119. package/dist/image-editor/index.cjs +2244 -38
  120. package/dist/image-editor/index.cjs.map +1 -1
  121. package/dist/image-editor/index.d.cts +742 -4
  122. package/dist/image-editor/index.d.ts +742 -4
  123. package/dist/image-editor/index.js +2222 -16
  124. package/dist/image-editor/index.js.map +1 -1
  125. package/dist/{index-CCr_z9kF.d.cts → index-BhJBSm2d.d.cts} +370 -4
  126. package/dist/{index-Ck6ajwXh.d.ts → index-fJrlkJCd.d.ts} +370 -4
  127. package/dist/index.cjs +193 -98
  128. package/dist/index.cjs.map +1 -1
  129. package/dist/index.d.cts +17 -48
  130. package/dist/index.d.ts +17 -48
  131. package/dist/index.js +198 -103
  132. package/dist/index.js.map +1 -1
  133. package/dist/migrate-iTlm3a8h.d.cts +112 -0
  134. package/dist/migrate-iTlm3a8h.d.ts +112 -0
  135. package/dist/persistence/index.cjs +676 -0
  136. package/dist/persistence/index.cjs.map +1 -0
  137. package/dist/persistence/index.d.cts +347 -0
  138. package/dist/persistence/index.d.ts +347 -0
  139. package/dist/persistence/index.js +676 -0
  140. package/dist/persistence/index.js.map +1 -0
  141. package/dist/registry-BDe-vlv3.d.ts +171 -0
  142. package/dist/registry-D32LpIc4.d.cts +354 -0
  143. package/dist/registry-DnzpgY27.d.cts +171 -0
  144. package/dist/registry-evKTP4ls.d.ts +354 -0
  145. package/dist/schema-DYDGdYL9.d.cts +41 -0
  146. package/dist/schema-DYDGdYL9.d.ts +41 -0
  147. package/dist/serializers/index.cjs +4 -3
  148. package/dist/serializers/index.cjs.map +1 -1
  149. package/dist/serializers/index.d.cts +2 -1
  150. package/dist/serializers/index.d.ts +2 -1
  151. package/dist/serializers/index.js +3 -2
  152. package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
  153. package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
  154. package/dist/spreadsheet/index.cjs +53 -3
  155. package/dist/spreadsheet/index.cjs.map +1 -1
  156. package/dist/spreadsheet/index.d.cts +247 -18
  157. package/dist/spreadsheet/index.d.ts +247 -18
  158. package/dist/spreadsheet/index.js +58 -8
  159. package/dist/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
  160. package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
  161. package/dist/{track-changes-BSF58RK0.d.cts → track-changes-BK5sVf4_.d.cts} +18 -136
  162. package/dist/{track-changes-BSF58RK0.d.ts → track-changes-CDjCrwKe.d.ts} +18 -136
  163. package/dist/{types-CJ4U-HHI.d.ts → types-DX4Mh4SL.d.cts} +1 -1
  164. package/dist/{types-CJ4U-HHI.d.cts → types-DX4Mh4SL.d.ts} +1 -1
  165. package/dist/types-GT5a5YAX.d.cts +86 -0
  166. package/dist/types-GT5a5YAX.d.ts +86 -0
  167. package/package.json +23 -2
  168. package/dist/chunk-2TWGLC5K.cjs +0 -262
  169. package/dist/chunk-2TWGLC5K.cjs.map +0 -1
  170. package/dist/chunk-4SUNJZWB.cjs.map +0 -1
  171. package/dist/chunk-6M3BCTBD.cjs.map +0 -1
  172. package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
  173. package/dist/chunk-AXV2ZW34.cjs +0 -118
  174. package/dist/chunk-AXV2ZW34.cjs.map +0 -1
  175. package/dist/chunk-B2ZRLN4S.js +0 -118
  176. package/dist/chunk-B2ZRLN4S.js.map +0 -1
  177. package/dist/chunk-DENSCKFC.js.map +0 -1
  178. package/dist/chunk-GVSMRPHB.cjs.map +0 -1
  179. package/dist/chunk-KAADNAPF.js +0 -262
  180. package/dist/chunk-KAADNAPF.js.map +0 -1
  181. package/dist/chunk-MJZBNWG7.js.map +0 -1
  182. package/dist/chunk-OYXSKQFR.js.map +0 -1
  183. package/dist/chunk-QGHGJ56O.js.map +0 -1
  184. package/dist/chunk-RU52I7QO.js.map +0 -1
  185. package/dist/chunk-SAZRENEZ.cjs.map +0 -1
  186. package/dist/types-BZgcDsC5.d.cts +0 -42
  187. package/dist/types-BZgcDsC5.d.ts +0 -42
  188. /package/dist/{chunk-JJDLPEWL.js.map → chunk-3LV6RRPH.js.map} +0 -0
@@ -1,9 +1,1140 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/image-editor/filters.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;
2
+
3
+ var _chunkD6FH2YAXcjs = require('../chunk-D6FH2YAX.cjs');
4
+
5
+ // src/image-editor/color.ts
6
+ var LEGACY_PROCESS_VERSION = 1;
7
+ var LATEST_PROCESS_VERSION = 2;
8
+ function normalizeProcessVersion(value) {
9
+ if (typeof value !== "number" || !Number.isInteger(value)) return LEGACY_PROCESS_VERSION;
10
+ return value >= LATEST_PROCESS_VERSION ? LATEST_PROCESS_VERSION : LEGACY_PROCESS_VERSION;
11
+ }
12
+ function srgbToLinear(v) {
13
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
14
+ }
15
+ function linearToSrgb(v) {
16
+ return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
17
+ }
18
+ var LUMA_709 = [0.2126, 0.7152, 0.0722];
19
+ var IDENTITY_MATRIX3 = [1, 0, 0, 0, 1, 0, 0, 0, 1];
20
+ function mulMatrix3(a, b) {
21
+ const out = new Array(9).fill(0);
22
+ for (let r = 0; r < 3; r++) {
23
+ for (let c = 0; c < 3; c++) {
24
+ out[r * 3 + c] = a[r * 3] * b[c] + a[r * 3 + 1] * b[3 + c] + a[r * 3 + 2] * b[6 + c];
25
+ }
26
+ }
27
+ return out;
28
+ }
29
+ function applyMatrix3(m, v) {
30
+ return [
31
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
32
+ m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
33
+ m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
34
+ ];
35
+ }
36
+ function invertMatrix3(m) {
37
+ const [a, b, c, d, e, f, g, h, i] = m;
38
+ const A = e * i - f * h;
39
+ const B = -(d * i - f * g);
40
+ const C = d * h - e * g;
41
+ const det = a * A + b * B + c * C;
42
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
43
+ const k = 1 / det;
44
+ return [
45
+ A * k,
46
+ -(b * i - c * h) * k,
47
+ (b * f - c * e) * k,
48
+ B * k,
49
+ (a * i - c * g) * k,
50
+ -(a * f - c * d) * k,
51
+ C * k,
52
+ -(a * h - b * g) * k,
53
+ (a * e - b * d) * k
54
+ ];
55
+ }
56
+ function diag3(x, y, z) {
57
+ return [x, 0, 0, 0, y, 0, 0, 0, z];
58
+ }
59
+ var D65 = { x: 0.3127, y: 0.329 };
60
+ function xyToXyz(p) {
61
+ return [p.x / p.y, 1, (1 - p.x - p.y) / p.y];
62
+ }
63
+ function xyzToXy(v) {
64
+ const s = v[0] + v[1] + v[2];
65
+ return s <= 0 ? { ...D65 } : { x: v[0] / s, y: v[1] / s };
66
+ }
67
+ var SRGB_TO_XYZ = (() => {
68
+ const p = [{ x: 0.64, y: 0.33 }, { x: 0.3, y: 0.6 }, { x: 0.15, y: 0.06 }].map(xyToXyz);
69
+ const m = [p[0][0], p[1][0], p[2][0], p[0][1], p[1][1], p[2][1], p[0][2], p[1][2], p[2][2]];
70
+ const inv = invertMatrix3(m);
71
+ const s = inv ? applyMatrix3(inv, xyToXyz(D65)) : [1, 1, 1];
72
+ return mulMatrix3(m, diag3(s[0], s[1], s[2]));
73
+ })();
74
+ var XYZ_TO_SRGB = _nullishCoalesce(invertMatrix3(SRGB_TO_XYZ), () => ( IDENTITY_MATRIX3));
75
+ var BRADFORD = [0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296];
76
+ var BRADFORD_INV = _nullishCoalesce(invertMatrix3(BRADFORD), () => ( IDENTITY_MATRIX3));
77
+ function bradfordAdaptation(src, dst) {
78
+ if (src.x === dst.x && src.y === dst.y) return [...IDENTITY_MATRIX3];
79
+ const s = applyMatrix3(BRADFORD, xyToXyz(src));
80
+ const d = applyMatrix3(BRADFORD, xyToXyz(dst));
81
+ return mulMatrix3(BRADFORD_INV, mulMatrix3(diag3(d[0] / s[0], d[1] / s[1], d[2] / s[2]), BRADFORD));
82
+ }
83
+ function wbMatrix(src) {
84
+ return mulMatrix3(XYZ_TO_SRGB, mulMatrix3(bradfordAdaptation(src, D65), SRGB_TO_XYZ));
85
+ }
86
+ function linearSrgbToXy(rgb) {
87
+ return xyzToXy(applyMatrix3(SRGB_TO_XYZ, rgb));
88
+ }
89
+ var TINT_SCALE = 3e3;
90
+ var MIN_KELVIN = 1667;
91
+ var MAX_KELVIN = 25e3;
92
+ function kimXy(tIn) {
93
+ const t = Math.min(MAX_KELVIN, Math.max(MIN_KELVIN, tIn));
94
+ const t2 = t * t;
95
+ const t3 = t2 * t;
96
+ const x = t <= 4e3 ? -266123900 / t3 - 234358.9 / t2 + 877.6956 / t + 0.17991 : -3025846900 / t3 + 21070379e-1 / t2 + 222.6347 / t + 0.24039;
97
+ const x2 = x * x;
98
+ const x3 = x2 * x;
99
+ const y = t <= 2222 ? -1.1063814 * x3 - 1.3481102 * x2 + 2.18555832 * x - 0.20219683 : t <= 4e3 ? -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867 : 3.081758 * x3 - 5.8733867 * x2 + 3.75112997 * x - 0.37001483;
100
+ return { x, y };
101
+ }
102
+ function xyToUv(p) {
103
+ const d = -2 * p.x + 12 * p.y + 3;
104
+ return [4 * p.x / d, 6 * p.y / d];
105
+ }
106
+ function uvToXy(u, v) {
107
+ const d = 2 * u - 8 * v + 4;
108
+ return { x: 3 * u / d, y: 2 * v / d };
109
+ }
110
+ function locusUv(tIn) {
111
+ const t = Math.max(1e3, tIn);
112
+ if (t >= MIN_KELVIN && t <= MAX_KELVIN) return xyToUv(kimXy(t));
113
+ const [a, b] = t < MIN_KELVIN ? [MIN_KELVIN, MIN_KELVIN + 50] : [MAX_KELVIN - 500, MAX_KELVIN];
114
+ const [ua, va] = xyToUv(kimXy(a));
115
+ const [ub, vb] = xyToUv(kimXy(b));
116
+ const ma = 1e6 / a;
117
+ const mb = 1e6 / b;
118
+ const s = (1e6 / t - ma) / (mb - ma);
119
+ return [ua + (ub - ua) * s, va + (vb - va) * s];
120
+ }
121
+ function locusNormal(t) {
122
+ const m = 1e6 / t;
123
+ const [u0, v0] = locusUv(1e6 / (m + 1));
124
+ const [u1, v1] = locusUv(1e6 / Math.max(m - 1, 1));
125
+ const du = u1 - u0;
126
+ const dv = v1 - v0;
127
+ const l = Math.max(Math.hypot(du, dv), 1e-12);
128
+ const nx = -dv / l;
129
+ const ny = du / l;
130
+ return ny < 0 ? [-nx, -ny] : [nx, ny];
131
+ }
132
+ function tempTintToXy(kelvin, tint) {
133
+ const [u, v] = locusUv(kelvin);
134
+ const [nu, nv] = locusNormal(kelvin);
135
+ const duv = tint / TINT_SCALE;
136
+ return uvToXy(u + nu * duv, v + nv * duv);
137
+ }
138
+ function xyToTempTint(p) {
139
+ const [u, v] = xyToUv(p);
140
+ const dist = (m) => {
141
+ const [lu2, lv2] = locusUv(1e6 / m);
142
+ return (u - lu2) ** 2 + (v - lv2) ** 2;
143
+ };
144
+ let best = 20;
145
+ let bd = Infinity;
146
+ for (let m = 20; m <= 1e3; m += 5) {
147
+ const d = dist(m);
148
+ if (d < bd) {
149
+ bd = d;
150
+ best = m;
151
+ }
152
+ }
153
+ let a = Math.max(best - 5, 20);
154
+ let b = Math.min(best + 5, 1e3);
155
+ const g = 0.61803398875;
156
+ for (let i = 0; i < 60; i++) {
157
+ const c = b - g * (b - a);
158
+ const d = a + g * (b - a);
159
+ if (dist(c) < dist(d)) b = d;
160
+ else a = c;
161
+ }
162
+ const kelvin = 1e6 / ((a + b) / 2);
163
+ const [lu, lv] = locusUv(kelvin);
164
+ const [nu, nv] = locusNormal(kelvin);
165
+ const duv = (u - lu) * nu + (v - lv) * nv;
166
+ return { kelvin, tint: duv * TINT_SCALE };
167
+ }
168
+ var AS_SHOT_KELVIN = 6500;
169
+ var MIRED_PER_TEMPERATURE_UNIT = 1;
170
+ var AS_SHOT_MIRED = 1e6 / AS_SHOT_KELVIN;
171
+ function temperatureToKelvin(temperature) {
172
+ return 1e6 / (AS_SHOT_MIRED - temperature * MIRED_PER_TEMPERATURE_UNIT);
173
+ }
174
+ function kelvinToTemperature(kelvin) {
175
+ return (AS_SHOT_MIRED - 1e6 / kelvin) / MIRED_PER_TEMPERATURE_UNIT;
176
+ }
177
+ var AS_SHOT_WB_INV = _nullishCoalesce(invertMatrix3(wbMatrix(tempTintToXy(AS_SHOT_KELVIN, 0))), () => ( IDENTITY_MATRIX3));
178
+ function whiteBalanceMatrix(temperature, tint = 0) {
179
+ if (!temperature && !tint) return [...IDENTITY_MATRIX3];
180
+ const set = wbMatrix(tempTintToXy(temperatureToKelvin(temperature), tint));
181
+ const m = mulMatrix3(set, AS_SHOT_WB_INV);
182
+ const g = applyMatrix3(m, [1, 1, 1]);
183
+ const y = g[0] * LUMA_709[0] + g[1] * LUMA_709[1] + g[2] * LUMA_709[2];
184
+ return mulMatrix3(m, diag3(1 / y, 1 / y, 1 / y));
185
+ }
186
+ function whiteBalanceFromSample(rgb) {
187
+ const [r, g, b] = Array.isArray(rgb) ? rgb : [rgb.r, rgb.g, rgb.b];
188
+ const lin = [r, g, b].map((c) => srgbToLinear(Math.min(255, Math.max(0, c)) / 255));
189
+ if (lin[0] + lin[1] + lin[2] <= 1e-6) return { temperature: 0, tint: 0 };
190
+ const src = linearSrgbToXy(applyMatrix3(AS_SHOT_WB_INV, lin));
191
+ const { kelvin, tint } = xyToTempTint(src);
192
+ const clamp2 = (v) => Math.max(-100, Math.min(100, v));
193
+ return { temperature: clamp2(kelvinToTemperature(kelvin)), tint: clamp2(tint) };
194
+ }
195
+ var TONE_LUT_SIZE = 4096;
196
+ var GREY = 0.18;
197
+ var GREY_P = Math.pow(GREY, 1 / 2.2);
198
+ var SHOULDER_KNEE = 0.6;
199
+ function toneCurve(y, contrast, brightness, headroom = 1) {
200
+ if (!(y > 0)) return 0;
201
+ const h = Math.max(1, headroom);
202
+ let o = Math.min(y, h);
203
+ if (h > 1 && o > SHOULDER_KNEE) {
204
+ const span = 1 - SHOULDER_KNEE;
205
+ const x = (o - SHOULDER_KNEE) / span;
206
+ const X = (h - SHOULDER_KNEE) / span;
207
+ o = SHOULDER_KNEE + span * (x * (1 + x / (X * X)) / (1 + x));
208
+ }
209
+ if (contrast === 0 && brightness === 0) return Math.min(1, o);
210
+ let p = Math.pow(Math.min(1, o), 1 / 2.2);
211
+ const c = contrast / 100;
212
+ if (c !== 0) {
213
+ p += c * 0.4 * (p - GREY_P) * (1 - (2 * p - 1) ** 2);
214
+ }
215
+ const b = brightness / 100;
216
+ if (b !== 0) {
217
+ p += b * 0.8 * p * (1 - p);
218
+ }
219
+ return Math.pow(Math.min(1, Math.max(0, p)), 2.2);
220
+ }
221
+ function buildToneLut(contrast, brightness, headroom = 1) {
222
+ const h = Math.max(1, headroom);
223
+ const lut = new Float32Array(TONE_LUT_SIZE + 1);
224
+ for (let i = 0; i <= TONE_LUT_SIZE; i++) {
225
+ lut[i] = toneCurve(h * i / TONE_LUT_SIZE, contrast, brightness, h);
226
+ }
227
+ return lut;
228
+ }
229
+ function sampleToneLut(lut, headroom, y) {
230
+ if (!(y > 0)) return lut[0];
231
+ const n = lut.length - 1;
232
+ const f = y * n / Math.max(1, headroom);
233
+ if (f >= n) return lut[n];
234
+ const i = f | 0;
235
+ return lut[i] + (lut[i + 1] - lut[i]) * (f - i);
236
+ }
237
+
238
+ // src/image-editor/blend.ts
239
+ var BLEND_MODES = [
240
+ { id: "normal", label: "Normal", canvas: "source-over" },
241
+ { id: "darken", label: "Darken", canvas: "darken" },
242
+ { id: "multiply", label: "Multiply", canvas: "multiply" },
243
+ { id: "color-burn", label: "Color Burn", canvas: "color-burn" },
244
+ { id: "linear-burn", label: "Linear Burn", canvas: null },
245
+ { id: "darker-color", label: "Darker Color", canvas: null },
246
+ { id: "lighten", label: "Lighten", canvas: "lighten" },
247
+ { id: "screen", label: "Screen", canvas: "screen" },
248
+ { id: "color-dodge", label: "Color Dodge", canvas: "color-dodge" },
249
+ { id: "linear-dodge", label: "Linear Dodge (Add)", canvas: null },
250
+ { id: "lighter-color", label: "Lighter Color", canvas: null },
251
+ { id: "overlay", label: "Overlay", canvas: "overlay" },
252
+ // Canvas soft-light is the W3C formula, not Photoshop's: use the scalar path.
253
+ { id: "soft-light", label: "Soft Light", canvas: null },
254
+ { id: "hard-light", label: "Hard Light", canvas: "hard-light" },
255
+ { id: "vivid-light", label: "Vivid Light", canvas: null },
256
+ { id: "linear-light", label: "Linear Light", canvas: null },
257
+ { id: "pin-light", label: "Pin Light", canvas: null },
258
+ { id: "hard-mix", label: "Hard Mix", canvas: null },
259
+ { id: "difference", label: "Difference", canvas: "difference" },
260
+ { id: "exclusion", label: "Exclusion", canvas: "exclusion" },
261
+ { id: "subtract", label: "Subtract", canvas: null },
262
+ { id: "divide", label: "Divide", canvas: null },
263
+ { id: "hue", label: "Hue", canvas: "hue" },
264
+ { id: "saturation", label: "Saturation", canvas: "saturation" },
265
+ { id: "color", label: "Color", canvas: "color" },
266
+ { id: "luminosity", label: "Luminosity", canvas: "luminosity" }
267
+ ];
268
+ var BLEND_INFO = new Map(BLEND_MODES.map((m) => [m.id, m]));
269
+ function isBlendMode(value) {
270
+ return typeof value === "string" && BLEND_INFO.has(value);
271
+ }
272
+ function normalizeBlendMode(value) {
273
+ return isBlendMode(value) ? value : "normal";
274
+ }
275
+ function canvasCompositeOperation(mode) {
276
+ return _nullishCoalesce(_optionalChain([BLEND_INFO, 'access', _2 => _2.get, 'call', _3 => _3(_nullishCoalesce(mode, () => ( "normal"))), 'optionalAccess', _4 => _4.canvas]), () => ( null));
277
+ }
278
+ function isSeparableBlendMode(mode) {
279
+ return !(mode === "hue" || mode === "saturation" || mode === "color" || mode === "luminosity" || mode === "darker-color" || mode === "lighter-color");
280
+ }
281
+ function colorBurn(cb, cs) {
282
+ if (cb >= 1) return 1;
283
+ if (cs <= 0) return 0;
284
+ return 1 - Math.min(1, (1 - cb) / cs);
285
+ }
286
+ function colorDodge(cb, cs) {
287
+ if (cb <= 0) return 0;
288
+ if (cs >= 1) return 1;
289
+ return Math.min(1, cb / (1 - cs));
290
+ }
291
+ function hardLight(cb, cs) {
292
+ if (cs <= 0.5) return cb * 2 * cs;
293
+ const s = 2 * cs - 1;
294
+ return cb + s - cb * s;
295
+ }
296
+ function softLight(cb, cs) {
297
+ return cs <= 0.5 ? 2 * cb * cs + cb * cb * (1 - 2 * cs) : 2 * cb * (1 - cs) + Math.sqrt(Math.max(0, cb)) * (2 * cs - 1);
298
+ }
299
+ function vividLightHardMix(cb, cs) {
300
+ const EDGE = 1e-4;
301
+ if (cs <= 0.5) {
302
+ if (cb >= 1 - EDGE) return 1;
303
+ if (cs <= 0) return 0;
304
+ return 1 - Math.min(1, (1 - cb) / (2 * cs));
305
+ }
306
+ if (cb <= EDGE) return 0;
307
+ if (cs >= 1) return 1;
308
+ return Math.min(1, cb / (2 * (1 - cs)));
309
+ }
310
+ function blendChannel(mode, cb, cs) {
311
+ switch (mode) {
312
+ case "normal":
313
+ return cs;
314
+ case "darken":
315
+ return Math.min(cb, cs);
316
+ case "multiply":
317
+ return cb * cs;
318
+ case "color-burn":
319
+ return colorBurn(cb, cs);
320
+ case "linear-burn":
321
+ return Math.max(0, cb + cs - 1);
322
+ case "lighten":
323
+ return Math.max(cb, cs);
324
+ case "screen":
325
+ return cb + cs - cb * cs;
326
+ case "color-dodge":
327
+ return colorDodge(cb, cs);
328
+ case "linear-dodge":
329
+ return Math.min(1, cb + cs);
330
+ case "overlay":
331
+ return hardLight(cs, cb);
332
+ case "soft-light":
333
+ return softLight(cb, cs);
334
+ case "hard-light":
335
+ return hardLight(cb, cs);
336
+ case "vivid-light":
337
+ if (cs <= 0) return 0;
338
+ if (cs >= 1) return 1;
339
+ return cs <= 0.5 ? colorBurn(cb, 2 * cs) : colorDodge(cb, 2 * (cs - 0.5));
340
+ case "linear-light":
341
+ return Math.min(1, Math.max(0, cb + 2 * cs - 1));
342
+ case "pin-light":
343
+ return cs <= 0.5 ? Math.min(cb, 2 * cs) : Math.max(cb, 2 * cs - 1);
344
+ case "hard-mix":
345
+ return vividLightHardMix(cb, cs) >= 0.5 - 1e-6 ? 1 : 0;
346
+ case "difference":
347
+ return Math.abs(cb - cs);
348
+ case "exclusion":
349
+ return cb + cs - 2 * cb * cs;
350
+ case "subtract":
351
+ return Math.max(0, cb - cs);
352
+ case "divide":
353
+ if (cs <= 0) return cb <= 0 ? 0 : 1;
354
+ return Math.min(1, cb / cs);
355
+ default:
356
+ return cs;
357
+ }
358
+ }
359
+ function blendLum(c) {
360
+ return 0.3 * c[0] + 0.59 * c[1] + 0.11 * c[2];
361
+ }
362
+ function clipColor(c) {
363
+ const l = blendLum(c);
364
+ const n = Math.min(c[0], c[1], c[2]);
365
+ const x = Math.max(c[0], c[1], c[2]);
366
+ let out = [c[0], c[1], c[2]];
367
+ if (n < 0) {
368
+ const d = l - n;
369
+ out = out.map((v) => l > 0 && d > 0 ? l + (v - l) * l / d : l);
370
+ }
371
+ if (x > 1) {
372
+ const d = x - l;
373
+ out = out.map((v) => l < 1 && d > 0 ? l + (v - l) * (1 - l) / d : l);
374
+ }
375
+ return out;
376
+ }
377
+ function setLum(c, l) {
378
+ const d = l - blendLum(c);
379
+ return clipColor([c[0] + d, c[1] + d, c[2] + d]);
380
+ }
381
+ function sat(c) {
382
+ return Math.max(c[0], c[1], c[2]) - Math.min(c[0], c[1], c[2]);
383
+ }
384
+ function setSat(c, s) {
385
+ const idx = [0, 1, 2].sort((a, b) => c[a] - c[b]);
386
+ const [imin, imid, imax] = idx;
387
+ const out = [0, 0, 0];
388
+ if (c[imax] > c[imin]) {
389
+ out[imid] = (c[imid] - c[imin]) * s / (c[imax] - c[imin]);
390
+ out[imax] = s;
391
+ }
392
+ out[imin] = 0;
393
+ return out;
394
+ }
395
+ function blendRgb(mode, cb, cs) {
396
+ switch (mode) {
397
+ case "hue":
398
+ return setLum(setSat(cs, sat(cb)), blendLum(cb));
399
+ case "saturation":
400
+ return setLum(setSat(cb, sat(cs)), blendLum(cb));
401
+ case "color":
402
+ return setLum(cs, blendLum(cb));
403
+ case "luminosity":
404
+ return setLum(cb, blendLum(cs));
405
+ case "darker-color":
406
+ return blendLum(cs) < blendLum(cb) ? [cs[0], cs[1], cs[2]] : [cb[0], cb[1], cb[2]];
407
+ case "lighter-color":
408
+ return blendLum(cs) > blendLum(cb) ? [cs[0], cs[1], cs[2]] : [cb[0], cb[1], cb[2]];
409
+ default:
410
+ return [blendChannel(mode, cb[0], cs[0]), blendChannel(mode, cb[1], cs[1]), blendChannel(mode, cb[2], cs[2])];
411
+ }
412
+ }
413
+ function blendPixel(mode, backdrop, source, alpha = 1) {
414
+ const ab = _nullishCoalesce(backdrop[3], () => ( 1));
415
+ const as = (_nullishCoalesce(source[3], () => ( 1))) * alpha;
416
+ if (!(as > 0)) return [backdrop[0], backdrop[1], backdrop[2], ab];
417
+ const b = blendRgb(mode, backdrop, source);
418
+ const ao = as + ab * (1 - as);
419
+ if (ao <= 0) return [0, 0, 0, 0];
420
+ const out = [0, 0, 0, ao];
421
+ for (let i = 0; i < 3; i++) {
422
+ out[i] = ((1 - as) * ab * backdrop[i] + (1 - ab) * as * source[i] + as * ab * b[i]) / ao;
423
+ }
424
+ return out;
425
+ }
426
+ function blendImageData(backdrop, source, mode, alpha = 1) {
427
+ const d = backdrop.data;
428
+ const s = source.data;
429
+ const len = Math.min(d.length, s.length);
430
+ const cb = [0, 0, 0, 0];
431
+ const cs = [0, 0, 0, 0];
432
+ for (let i = 0; i < len; i += 4) {
433
+ if (s[i + 3] === 0) continue;
434
+ for (let k = 0; k < 4; k++) {
435
+ cb[k] = d[i + k] / 255;
436
+ cs[k] = s[i + k] / 255;
437
+ }
438
+ const o = blendPixel(mode, cb, cs, alpha);
439
+ d[i] = Math.round(o[0] * 255);
440
+ d[i + 1] = Math.round(o[1] * 255);
441
+ d[i + 2] = Math.round(o[2] * 255);
442
+ d[i + 3] = Math.round(o[3] * 255);
443
+ }
444
+ }
445
+
446
+ // src/image-editor/adjustment-layers.ts
447
+ var ADJUSTMENT_LUT_SIZE = 4096;
448
+ var clamp01 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
449
+ var finite = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : fallback;
450
+ var clampTo = (v, lo, hi, fallback) => Math.min(hi, Math.max(lo, finite(v, fallback)));
451
+ var remEuclid = (a, b) => (a % b + b) % b;
452
+ function sampleLut(table, v) {
453
+ const n = table.length - 1;
454
+ const x = clamp01(v) * n;
455
+ const i = Math.floor(x);
456
+ const j = Math.min(i + 1, n);
457
+ const f = x - i;
458
+ return table[i] * (1 - f) + table[j] * f;
459
+ }
460
+ var ADJUSTMENT_KINDS = [
461
+ { kind: "curves", label: "Curves" },
462
+ { kind: "levels", label: "Levels" },
463
+ { kind: "hueSaturation", label: "Hue/Saturation" },
464
+ { kind: "vibrance", label: "Vibrance" },
465
+ { kind: "colorBalance", label: "Color Balance" },
466
+ { kind: "blackWhite", label: "Black & White" },
467
+ { kind: "photoFilter", label: "Photo Filter" },
468
+ { kind: "invert", label: "Invert" },
469
+ { kind: "posterize", label: "Posterize" },
470
+ { kind: "threshold", label: "Threshold" }
471
+ ];
472
+ var KIND_LABELS = new Map(ADJUSTMENT_KINDS.map((k) => [k.kind, k.label]));
473
+ function isAdjustmentKind(value) {
474
+ return typeof value === "string" && KIND_LABELS.has(value);
475
+ }
476
+ function adjustmentKindLabel(kind) {
477
+ return _nullishCoalesce(KIND_LABELS.get(kind), () => ( kind));
478
+ }
479
+ var HUE_RANGE_LABELS = ["Reds", "Yellows", "Greens", "Cyans", "Blues", "Magentas"];
480
+ function neutralHueRange(i) {
481
+ const c = 60 * i;
482
+ return { hue: 0, saturation: 0, lightness: 0, bounds: [c - 45, c - 15, c + 15, c + 45] };
483
+ }
484
+ var identityCurve = () => [
485
+ { input: 0, output: 0 },
486
+ { input: 255, output: 255 }
487
+ ];
488
+ var identityLevels = () => ({ inBlack: 0, inWhite: 255, gamma: 1, outBlack: 0, outWhite: 255 });
489
+ function defaultAdjustmentParams(kind) {
490
+ const defaults = {
491
+ curves: () => ({ master: identityCurve(), red: identityCurve(), green: identityCurve(), blue: identityCurve() }),
492
+ levels: () => ({ master: identityLevels(), red: identityLevels(), green: identityLevels(), blue: identityLevels() }),
493
+ hueSaturation: () => ({
494
+ hue: 0,
495
+ saturation: 0,
496
+ lightness: 0,
497
+ colorize: false,
498
+ ranges: [0, 1, 2, 3, 4, 5].map(neutralHueRange)
499
+ }),
500
+ vibrance: () => ({ vibrance: 0, saturation: 0 }),
501
+ colorBalance: () => ({ shadows: [0, 0, 0], midtones: [0, 0, 0], highlights: [0, 0, 0], preserveLuminosity: true }),
502
+ blackWhite: () => ({ reds: 40, yellows: 60, greens: 40, cyans: 60, blues: 20, magentas: 80, tint: null }),
503
+ invert: () => ({}),
504
+ posterize: () => ({ levels: 4 }),
505
+ threshold: () => ({ level: 128 }),
506
+ photoFilter: () => ({ color: "#ec8a00", density: 25, preserveLuminosity: true })
507
+ };
508
+ return defaults[kind]();
509
+ }
510
+ var layerCounter = 0;
511
+ function createAdjustmentLayer(kind, options = {}) {
512
+ const raw = {
513
+ id: _nullishCoalesce(options.id, () => ( `adj_${Date.now().toString(36)}_${(++layerCounter).toString(36)}`)),
514
+ kind,
515
+ name: options.name,
516
+ params: { ...defaultAdjustmentParams(kind), ..._nullishCoalesce(options.params, () => ( {})) },
517
+ enabled: _nullishCoalesce(options.enabled, () => ( true)),
518
+ opacity: _nullishCoalesce(options.opacity, () => ( 1)),
519
+ blend: _nullishCoalesce(options.blend, () => ( "normal"))
520
+ };
521
+ return normalizeAdjustmentLayer(raw);
522
+ }
523
+ var HEX = /^#[0-9a-f]{6}$/i;
524
+ function normCurve(points) {
525
+ if (!Array.isArray(points)) return identityCurve();
526
+ const out = points.filter((p) => p && typeof p === "object").map((p) => ({ input: clampTo(p.input, 0, 255, 0), output: clampTo(p.output, 0, 255, 0) })).sort((a, b) => a.input - b.input);
527
+ return out.length >= 2 ? out : identityCurve();
528
+ }
529
+ function normLevels(ch) {
530
+ const c = ch && typeof ch === "object" ? ch : {};
531
+ return {
532
+ inBlack: clampTo(c.inBlack, 0, 254, 0),
533
+ inWhite: clampTo(c.inWhite, 1, 255, 255),
534
+ gamma: clampTo(c.gamma, 0.1, 9.99, 1),
535
+ outBlack: clampTo(c.outBlack, 0, 255, 0),
536
+ outWhite: clampTo(c.outWhite, 0, 255, 255)
537
+ };
538
+ }
539
+ function normTone(t) {
540
+ const a = Array.isArray(t) ? t : [];
541
+ return [clampTo(a[0], -100, 100, 0), clampTo(a[1], -100, 100, 0), clampTo(a[2], -100, 100, 0)];
542
+ }
543
+ function normParams(kind, raw) {
544
+ const p = raw && typeof raw === "object" ? raw : {};
545
+ const d = defaultAdjustmentParams(kind);
546
+ switch (kind) {
547
+ case "curves":
548
+ return { master: normCurve(p.master), red: normCurve(p.red), green: normCurve(p.green), blue: normCurve(p.blue) };
549
+ case "levels":
550
+ return { master: normLevels(p.master), red: normLevels(p.red), green: normLevels(p.green), blue: normLevels(p.blue) };
551
+ case "hueSaturation": {
552
+ const ranges = [0, 1, 2, 3, 4, 5].map((i) => {
553
+ const r = Array.isArray(p.ranges) && p.ranges[i] && typeof p.ranges[i] === "object" ? p.ranges[i] : {};
554
+ const n = neutralHueRange(i);
555
+ const b = Array.isArray(r.bounds) && r.bounds.length === 4 && r.bounds.every((v) => Number.isFinite(v)) ? r.bounds : n.bounds;
556
+ return {
557
+ hue: clampTo(r.hue, -180, 180, 0),
558
+ saturation: clampTo(r.saturation, -100, 100, 0),
559
+ lightness: clampTo(r.lightness, -100, 100, 0),
560
+ bounds: [b[0], b[1], b[2], b[3]]
561
+ };
562
+ });
563
+ const colorize = p.colorize === true;
564
+ return {
565
+ hue: colorize ? clampTo(p.hue, 0, 360, 0) : clampTo(p.hue, -180, 180, 0),
566
+ saturation: clampTo(p.saturation, -100, 100, 0),
567
+ lightness: clampTo(p.lightness, -100, 100, 0),
568
+ colorize,
569
+ ranges
570
+ };
571
+ }
572
+ case "vibrance":
573
+ return { vibrance: clampTo(p.vibrance, -100, 100, 0), saturation: clampTo(p.saturation, -100, 100, 0) };
574
+ case "colorBalance":
575
+ return {
576
+ shadows: normTone(p.shadows),
577
+ midtones: normTone(p.midtones),
578
+ highlights: normTone(p.highlights),
579
+ preserveLuminosity: p.preserveLuminosity !== false
580
+ };
581
+ case "blackWhite": {
582
+ const out = {};
583
+ for (const k of ["reds", "yellows", "greens", "cyans", "blues", "magentas"]) out[k] = clampTo(p[k], -200, 300, d[k]);
584
+ out.tint = typeof p.tint === "string" && HEX.test(p.tint) ? p.tint.toLowerCase() : null;
585
+ return out;
586
+ }
587
+ case "photoFilter":
588
+ return {
589
+ color: typeof p.color === "string" && HEX.test(p.color) ? p.color.toLowerCase() : d.color,
590
+ density: clampTo(p.density, 0, 100, d.density),
591
+ preserveLuminosity: p.preserveLuminosity !== false
592
+ };
593
+ case "posterize":
594
+ return { levels: Math.round(clampTo(p.levels, 2, 255, 4)) };
595
+ case "threshold":
596
+ return { level: Math.round(clampTo(p.level, 1, 255, 128)) };
597
+ case "invert":
598
+ return {};
599
+ }
600
+ }
601
+ function normalizeAdjustmentLayer(raw) {
602
+ if (!raw || typeof raw !== "object") return null;
603
+ const r = raw;
604
+ if (!isAdjustmentKind(r.kind)) return null;
605
+ const layer = {
606
+ id: typeof r.id === "string" && r.id ? r.id : `adj_${Date.now().toString(36)}_${(++layerCounter).toString(36)}`,
607
+ kind: r.kind,
608
+ enabled: r.enabled !== false,
609
+ opacity: clampTo(r.opacity, 0, 1, 1),
610
+ blend: normalizeBlendMode(r.blend),
611
+ params: normParams(r.kind, r.params)
612
+ };
613
+ if (typeof r.name === "string" && r.name) layer.name = r.name;
614
+ return layer;
615
+ }
616
+ function normalizeAdjustmentLayers(raw) {
617
+ if (!Array.isArray(raw)) return [];
618
+ const seen = /* @__PURE__ */ new Set();
619
+ const out = [];
620
+ for (const item of raw) {
621
+ const layer = normalizeAdjustmentLayer(item);
622
+ if (!layer) continue;
623
+ while (seen.has(layer.id)) layer.id = `${layer.id}_${(++layerCounter).toString(36)}`;
624
+ seen.add(layer.id);
625
+ out.push(layer);
626
+ }
627
+ return out;
628
+ }
629
+ function curveLut(points, size = ADJUSTMENT_LUT_SIZE) {
630
+ const pts = points.filter((p) => Number.isFinite(p.input) && Number.isFinite(p.output)).map((p) => [p.input, p.output]).sort((a, b) => a[0] - b[0]);
631
+ const dedup = [];
632
+ for (const p of pts) {
633
+ if (dedup.length && Math.abs(dedup[dedup.length - 1][0] - p[0]) < 1e-6) continue;
634
+ dedup.push(p);
635
+ }
636
+ const out = new Float32Array(size);
637
+ const n = dedup.length;
638
+ if (n < 2) {
639
+ for (let k = 0; k < size; k++) out[k] = k / (size - 1);
640
+ return out;
641
+ }
642
+ const x = dedup.map((p) => p[0]);
643
+ const y = dedup.map((p) => p[1]);
644
+ const m2 = new Float64Array(n);
645
+ if (n > 2) {
646
+ const c = new Float64Array(n);
647
+ const d = new Float64Array(n);
648
+ for (let i = 1; i < n - 1; i++) {
649
+ const h0 = x[i] - x[i - 1];
650
+ const h1 = x[i + 1] - x[i];
651
+ const a = h0 / 6;
652
+ const b = (h0 + h1) / 3;
653
+ const cc = h1 / 6;
654
+ const r = (y[i + 1] - y[i]) / h1 - (y[i] - y[i - 1]) / h0;
655
+ const denom = b - a * c[i - 1];
656
+ c[i] = cc / denom;
657
+ d[i] = (r - a * d[i - 1]) / denom;
658
+ }
659
+ for (let i = n - 2; i >= 1; i--) m2[i] = d[i] - c[i] * m2[i + 1];
660
+ }
661
+ let seg = 0;
662
+ for (let k = 0; k < size; k++) {
663
+ const xv = k / (size - 1);
664
+ if (xv <= x[0]) {
665
+ out[k] = clamp01(y[0]);
666
+ continue;
667
+ }
668
+ if (xv >= x[n - 1]) {
669
+ out[k] = clamp01(y[n - 1]);
670
+ continue;
671
+ }
672
+ while (seg < n - 2 && xv > x[seg + 1]) seg++;
673
+ const x0 = x[seg], x1 = x[seg + 1];
674
+ const h = x1 - x0;
675
+ const a = (x1 - xv) / h;
676
+ const b = (xv - x0) / h;
677
+ const v = a * y[seg] + b * y[seg + 1] + ((a * a * a - a) * m2[seg] + (b * b * b - b) * m2[seg + 1]) * (h * h) / 6;
678
+ out[k] = clamp01(v);
679
+ }
680
+ return out;
681
+ }
682
+ function curveLutFromLevels(points) {
683
+ return curveLut(points.map((p) => ({ input: p.input / 255, output: p.output / 255 })));
684
+ }
685
+ var toUnitLevels = (c) => ({
686
+ inBlack: c.inBlack / 255,
687
+ inWhite: c.inWhite / 255,
688
+ gamma: c.gamma,
689
+ outBlack: c.outBlack / 255,
690
+ outWhite: c.outWhite / 255
691
+ });
692
+ function levelsValue(ch, v, quantum) {
693
+ const range = Math.max(1e-6, ch.inWhite - ch.inBlack);
694
+ let u;
695
+ if (quantum !== void 0 && Number.isFinite(quantum) && quantum >= 1) {
696
+ const q = quantum;
697
+ const vq = Math.round(v * q) / q;
698
+ u = Math.floor(clamp01((vq - ch.inBlack) / range) * q + 0.5 + 1e-3) / q;
699
+ } else {
700
+ u = clamp01((v - ch.inBlack) / range);
701
+ }
702
+ const g = Math.max(0.01, ch.gamma);
703
+ let t;
704
+ if (g > 1) {
705
+ const power = Math.pow(u, 1 / g);
706
+ const line = 0.93 * Math.pow(2, g) * u;
707
+ if (power <= 1e-6 || line <= 1e-6) t = Math.min(power, line);
708
+ else {
709
+ const p = 10;
710
+ t = Math.pow(Math.pow(power, -p) + Math.pow(line, -p), -1 / p);
711
+ }
712
+ } else {
713
+ t = Math.pow(u, 1 / g);
714
+ }
715
+ return ch.outBlack + t * (ch.outWhite - ch.outBlack);
716
+ }
717
+ function rgbToHsl(c) {
718
+ const max = Math.max(c[0], c[1], c[2]);
719
+ const min = Math.min(c[0], c[1], c[2]);
720
+ const l = (max + min) / 2;
721
+ if (Math.abs(max - min) < 1e-7) return [0, 0, l];
722
+ const d = max - min;
723
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
724
+ let h;
725
+ if (max === c[0]) h = remEuclid((c[1] - c[2]) / d, 6);
726
+ else if (max === c[1]) h = (c[2] - c[0]) / d + 2;
727
+ else h = (c[0] - c[1]) / d + 4;
728
+ return [h / 6, s, l];
729
+ }
730
+ function hslToRgb(h, s, l) {
731
+ if (s <= 0) return [l, l, l];
732
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
733
+ const p = 2 * l - q;
734
+ const f = (t) => {
735
+ t = remEuclid(t, 1);
736
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
737
+ if (t < 0.5) return q;
738
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
739
+ return p;
740
+ };
741
+ return [f(h + 1 / 3), f(h), f(h - 1 / 3)];
742
+ }
743
+ function hueRangeWeight(range, deg) {
744
+ const [a, b0, c0, d0] = range.bounds;
745
+ const rel = (x) => Number.isFinite(x) && Number.isFinite(a) ? remEuclid(x - a, 360) : 0;
746
+ const b = rel(b0);
747
+ const c = Math.max(rel(c0), b);
748
+ const d = Math.max(rel(d0), c);
749
+ const h = rel(deg);
750
+ if (h <= b) return b <= 0 ? 1 : h / b;
751
+ if (h <= c) return 1;
752
+ if (h <= d) return d <= c ? 1 : (d - h) / (d - c);
753
+ return 0;
754
+ }
755
+ var isNeutralRange = (r) => r.hue === 0 && r.saturation === 0 && r.lightness === 0;
756
+ function hueRangeTables(ranges) {
757
+ const n = ADJUSTMENT_LUT_SIZE;
758
+ const t = [new Float32Array(n), new Float32Array(n), new Float32Array(n)];
759
+ const active = ranges.filter((r) => !isNeutralRange(r));
760
+ for (let k = 0; k < n; k++) {
761
+ const deg = k / (n - 1) * 360;
762
+ for (const r of active) {
763
+ const w = hueRangeWeight(r, deg);
764
+ t[0][k] += w * r.hue;
765
+ t[1][k] += w * r.saturation / 100;
766
+ t[2][k] += w * r.lightness / 100;
767
+ }
768
+ }
769
+ return t;
770
+ }
771
+ function hueSaturation(c, hue, s, l, colorize) {
772
+ const lighten = (v) => l > 0 ? v + (1 - v) * l : v * (1 + l);
773
+ if (colorize) {
774
+ const ll2 = rgbToHsl(c)[2];
775
+ return hslToRgb(remEuclid(hue, 360) / 360, Math.min(1, Math.abs(s)), lighten(ll2));
776
+ }
777
+ const [hh0, ss0, ll] = rgbToHsl([lighten(c[0]), lighten(c[1]), lighten(c[2])]);
778
+ const hh = remEuclid(hh0 + hue / 360, 1);
779
+ const ss = Math.min(1, Math.max(0, s > 0 ? ss0 / Math.max(1 / 512, 1 - s) : ss0 * (1 + s)));
780
+ return hslToRgb(hh, ss, ll);
781
+ }
782
+ function rangeLightness(c, l) {
783
+ const k = Math.min(1, Math.max(-1, l));
784
+ const target = k > 0 ? Math.max(c[0], c[1], c[2]) : Math.min(c[0], c[1], c[2]);
785
+ const a = Math.abs(k);
786
+ return [c[0] + (target - c[0]) * a, c[1] + (target - c[1]) * a, c[2] + (target - c[2]) * a];
787
+ }
788
+ var VIBRANCE_GREY = [0.288, 0.712];
789
+ var VIBRANCE_BOOST = [0.5001, 0.0887, 0.4374, 0.7573, -0.8113, 0.5154, 2.8026];
790
+ function vibranceSkin(h) {
791
+ if (h <= 30) return 1;
792
+ if (h < 45) return (45 - h) / 15;
793
+ if (h < 300) return 0;
794
+ return (h - 300) / 60;
795
+ }
796
+ function hsvHue(c, mx, mn) {
797
+ const d = mx - mn;
798
+ let h;
799
+ if (mx === c[0]) h = remEuclid((c[1] - c[2]) / d, 6);
800
+ else if (mx === c[1]) h = (c[2] - c[0]) / d + 2;
801
+ else h = (c[0] - c[1]) / d + 4;
802
+ return h * 60;
803
+ }
804
+ function vibranceLinear(c, v) {
805
+ const mx = Math.max(c[0], c[1], c[2]);
806
+ const mn = Math.min(c[0], c[1], c[2]);
807
+ if (mx <= 0 || mx - mn <= 1e-9) return c;
808
+ const t = 1 - mn / mx;
809
+ const shape = t * (1 - t) * (2 - t + t * t) * mx * Math.max(0, 1 - mx);
810
+ let t2, v2;
811
+ if (v < 0) {
812
+ const a = -v;
813
+ t2 = t * (1 - a / 4) * (1 - a + a * t * (1 + t) / 2);
814
+ v2 = mx - a * shape;
815
+ } else {
816
+ const [c0, c1, p, q, r0, ra, rt] = VIBRANCE_BOOST;
817
+ const w = vibranceSkin(hsvHue(c, mx, mn));
818
+ const a = v * (1 - w) + 0.64 * Math.pow(v, 1.6) * w;
819
+ const boost = (c0 * a + c1 * a * a) * Math.pow(t, p) * Math.pow(1 - t, q) * Math.exp((r0 + ra * a + rt * (1 - t)) * Math.max(0, 1 - mx));
820
+ t2 = Math.min(1, t * (1 + boost));
821
+ v2 = mx + v / 4 * shape;
822
+ }
823
+ const m2 = v2 * (1 - t2);
824
+ const k = (v2 - m2) / (mx - mn);
825
+ return [m2 + (c[0] - mn) * k, m2 + (c[1] - mn) * k, m2 + (c[2] - mn) * k];
826
+ }
827
+ function vibrancePixel(c, v, s) {
828
+ let l = [srgbToLinear(Math.max(0, c[0])), srgbToLinear(Math.max(0, c[1])), srgbToLinear(Math.max(0, c[2]))];
829
+ if (v !== 0) l = vibranceLinear(l, v);
830
+ if (s !== 0) {
831
+ const g = VIBRANCE_GREY[0] * l[0] + VIBRANCE_GREY[1] * l[1];
832
+ l = l.map((x) => Math.min(1, Math.max(0, g + (1 + s) * (x - g))));
833
+ }
834
+ return l.map((x) => linearToSrgb(clamp01(x)));
835
+ }
836
+ var BALANCE_LEVELS = 0.39;
837
+ function colorBalanceChannel(tones, preserveLuminosity, ch, v) {
838
+ const [shadows, midtones, highlights] = tones;
839
+ const black = (x, b) => Math.max(0, (x - b) / (1 - b));
840
+ const white = (x, w) => Math.min(1, x / w);
841
+ let out = clamp01(v);
842
+ for (const [tone2, sliders] of [
843
+ [2, highlights],
844
+ [0, shadows],
845
+ [1, midtones]
846
+ ]) {
847
+ const a = [0, 1, 2].map((i) => Math.min(1, Math.max(-1, sliders[i] / 100)));
848
+ if (preserveLuminosity) {
849
+ const own = _nullishCoalesce(a[ch], () => ( 0));
850
+ const lo = Math.min(a[0], a[1], a[2]);
851
+ const hi = Math.max(a[0], a[1], a[2]);
852
+ if (tone2 === 0) out = black(out, BALANCE_LEVELS * (hi - own));
853
+ else if (tone2 === 1) out = Math.pow(out, Math.pow(2, (hi + lo) / 2 - own));
854
+ else out = white(out, 1 - BALANCE_LEVELS * (own - lo));
855
+ continue;
856
+ }
857
+ const s = a[ch];
858
+ if (s === 0) continue;
859
+ if (tone2 === 1) out = Math.pow(out, Math.pow(2, -s));
860
+ else if (tone2 === 0 && s < 0) out = Math.pow(black(out, BALANCE_LEVELS * -s), Math.pow(2, -s / 2));
861
+ else if (tone2 === 2 && s > 0) out = Math.pow(white(out, 1 - BALANCE_LEVELS * s), Math.pow(2, -s / 2));
862
+ else out = Math.pow(out, Math.pow(2, -s / 2));
863
+ }
864
+ return out;
865
+ }
866
+ function blackWhiteGray(c, weights) {
867
+ const [r, g, b] = c;
868
+ const max = Math.max(r, g, b);
869
+ const min = Math.min(r, g, b);
870
+ const mid = r + g + b - max - min;
871
+ const primary = r >= g && r >= b ? 0 : g >= b ? 2 : 4;
872
+ const secondary = b <= r && b <= g ? 1 : r <= g ? 3 : 5;
873
+ return clamp01(min + (mid - min) * weights[secondary] / 100 + (max - mid) * weights[primary] / 100);
874
+ }
875
+ var D50 = [0.96422, 1, 0.82521];
876
+ var SRGB_TO_XYZ_D50 = [
877
+ [0.436074, 0.385064, 0.14308],
878
+ [0.222504, 0.716878, 0.060618],
879
+ [0.013932, 0.097104, 0.714173]
880
+ ];
881
+ var XYZ_D50_TO_SRGB = [
882
+ [3.133856, -1.616867, -0.490615],
883
+ [-0.978768, 1.916142, 0.033454],
884
+ [0.071945, -0.228991, 1.405243]
885
+ ];
886
+ function photoFilterMatrix(color, density, normalizeY = false) {
887
+ const d = Number.isFinite(density) ? clamp01(density) : 0;
888
+ const lin = [0, 1, 2].map((i) => srgbToLinear(clamp01(color[i])));
889
+ const xyz = SRGB_TO_XYZ_D50.map((r) => r[0] * lin[0] + r[1] * lin[1] + r[2] * lin[2]);
890
+ let s = [0, 1, 2].map((i) => 1 - d + d * xyz[i] / D50[i]);
891
+ if (normalizeY && s[1] > 1e-6) {
892
+ const y = s[1];
893
+ s = s.map((v) => v / y);
894
+ }
895
+ return [0, 1, 2].map(
896
+ (r) => [0, 1, 2].map((c) => XYZ_D50_TO_SRGB[r][0] * s[0] * SRGB_TO_XYZ_D50[0][c] + XYZ_D50_TO_SRGB[r][1] * s[1] * SRGB_TO_XYZ_D50[1][c] + XYZ_D50_TO_SRGB[r][2] * s[2] * SRGB_TO_XYZ_D50[2][c])
897
+ );
898
+ }
899
+ function posterizeValue(v, levels) {
900
+ const n = Math.min(255, Math.max(2, Math.round(levels)));
901
+ const x = Math.round(clamp01(v) * 255);
902
+ const bin = Math.min(n - 1, Math.floor(x * n / 256));
903
+ return Math.floor(bin * 255 / (n - 1)) / 255;
904
+ }
905
+ var rgbToGray = (c) => 0.299 * c[0] + 0.587 * c[1] + 0.114 * c[2];
906
+ function hexToRgb(hex) {
907
+ if (!HEX.test(hex)) return [0, 0, 0];
908
+ const n = parseInt(hex.slice(1), 16);
909
+ return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
910
+ }
911
+ function lutOp(luts) {
912
+ const [lr, lg, lb] = luts;
913
+ return (c) => {
914
+ c[0] = sampleLut(lr, c[0]);
915
+ c[1] = sampleLut(lg, c[1]);
916
+ c[2] = sampleLut(lb, c[2]);
917
+ };
918
+ }
919
+ var isIdentityCurve = (pts) => pts.length === 2 && pts[0].input === 0 && pts[0].output === 0 && pts[1].input === 255 && pts[1].output === 255;
920
+ var isIdentityLevels = (c) => c.inBlack === 0 && c.inWhite === 255 && c.gamma === 1 && c.outBlack === 0 && c.outWhite === 255;
921
+ var LEVELS_QUANTUM = 255;
922
+ function toneLuts(layer) {
923
+ const n = ADJUSTMENT_LUT_SIZE;
924
+ if (layer.kind === "curves") {
925
+ const m = curveLutFromLevels(layer.params.master);
926
+ return [layer.params.red, layer.params.green, layer.params.blue].map((pts) => {
927
+ const ch = curveLutFromLevels(pts);
928
+ for (let k = 0; k < n; k++) ch[k] = sampleLut(m, ch[k]);
929
+ return ch;
930
+ });
931
+ }
932
+ if (layer.kind === "levels") {
933
+ const m = toUnitLevels(layer.params.master);
934
+ return [layer.params.red, layer.params.green, layer.params.blue].map((rec) => {
935
+ const c = toUnitLevels(rec);
936
+ const out = new Float32Array(n);
937
+ for (let k = 0; k < n; k++) out[k] = levelsValue(m, levelsValue(c, k / (n - 1), LEVELS_QUANTUM), LEVELS_QUANTUM);
938
+ return out;
939
+ });
940
+ }
941
+ return null;
942
+ }
943
+ function compileAdjustmentLayer(layer) {
944
+ switch (layer.kind) {
945
+ case "curves": {
946
+ const p = layer.params;
947
+ if ([p.master, p.red, p.green, p.blue].every(isIdentityCurve)) return null;
948
+ return lutOp(toneLuts(layer));
949
+ }
950
+ case "levels": {
951
+ const p = layer.params;
952
+ if ([p.master, p.red, p.green, p.blue].every(isIdentityLevels)) return null;
953
+ return lutOp(toneLuts(layer));
954
+ }
955
+ case "hueSaturation": {
956
+ const p = layer.params;
957
+ const rangesActive = !p.colorize && p.ranges.some((r) => !isNeutralRange(r));
958
+ if (!p.colorize && !rangesActive && p.hue === 0 && p.saturation === 0 && p.lightness === 0) return null;
959
+ const tables = rangesActive ? hueRangeTables(p.ranges) : null;
960
+ const sat2 = p.saturation / 100;
961
+ const light = Math.min(1, Math.max(-1, p.lightness / 100));
962
+ return (c) => {
963
+ let dh = 0, ds = 0, dl = 0;
964
+ if (tables) {
965
+ const h0 = rgbToHsl(c)[0];
966
+ dh = sampleLut(tables[0], h0);
967
+ ds = sampleLut(tables[1], h0);
968
+ dl = sampleLut(tables[2], h0);
969
+ }
970
+ const out = rangeLightness(hueSaturation(c, p.hue + dh, Math.min(1, Math.max(-1, sat2 + ds)), light, p.colorize), dl);
971
+ c[0] = out[0];
972
+ c[1] = out[1];
973
+ c[2] = out[2];
974
+ };
975
+ }
976
+ case "vibrance": {
977
+ const v = layer.params.vibrance / 100;
978
+ const s = layer.params.saturation / 100;
979
+ if (v === 0 && s === 0) return null;
980
+ return (c) => {
981
+ const out = vibrancePixel(c, v, s);
982
+ c[0] = out[0];
983
+ c[1] = out[1];
984
+ c[2] = out[2];
985
+ };
986
+ }
987
+ case "colorBalance": {
988
+ const p = layer.params;
989
+ const tones = [p.shadows, p.midtones, p.highlights];
990
+ if (tones.every((t) => t[0] === 0 && t[1] === 0 && t[2] === 0)) return null;
991
+ const n = ADJUSTMENT_LUT_SIZE;
992
+ const luts = [0, 1, 2].map((ch) => {
993
+ const out = new Float32Array(n);
994
+ for (let k = 0; k < n; k++) out[k] = colorBalanceChannel(tones, p.preserveLuminosity, ch, k / (n - 1));
995
+ return out;
996
+ });
997
+ return lutOp(luts);
998
+ }
999
+ case "blackWhite": {
1000
+ const p = layer.params;
1001
+ const weights = [p.reds, p.yellows, p.greens, p.cyans, p.blues, p.magentas];
1002
+ const tint = p.tint ? hexToRgb(p.tint) : null;
1003
+ return (c) => {
1004
+ const g = blackWhiteGray(c, weights);
1005
+ if (tint) {
1006
+ const t = setLum(tint, g);
1007
+ c[0] = clamp01(t[0]);
1008
+ c[1] = clamp01(t[1]);
1009
+ c[2] = clamp01(t[2]);
1010
+ } else {
1011
+ c[0] = c[1] = c[2] = g;
1012
+ }
1013
+ };
1014
+ }
1015
+ case "photoFilter": {
1016
+ const p = layer.params;
1017
+ if (p.density <= 0) return null;
1018
+ const m = photoFilterMatrix(hexToRgb(p.color), p.density / 100, false);
1019
+ const preserve = p.preserveLuminosity;
1020
+ return (c) => {
1021
+ const l = [srgbToLinear(Math.max(0, c[0])), srgbToLinear(Math.max(0, c[1])), srgbToLinear(Math.max(0, c[2]))];
1022
+ let f = [0, 1, 2].map((r) => linearToSrgb(Math.max(0, m[r][0] * l[0] + m[r][1] * l[1] + m[r][2] * l[2])));
1023
+ if (preserve) {
1024
+ const target = 0.3 * c[0] + 0.59 * c[1] + 0.11 * c[2];
1025
+ f = setLum(f, target);
1026
+ }
1027
+ c[0] = clamp01(f[0]);
1028
+ c[1] = clamp01(f[1]);
1029
+ c[2] = clamp01(f[2]);
1030
+ };
1031
+ }
1032
+ case "invert":
1033
+ return (c) => {
1034
+ c[0] = 1 - c[0];
1035
+ c[1] = 1 - c[1];
1036
+ c[2] = 1 - c[2];
1037
+ };
1038
+ case "posterize": {
1039
+ const levels = layer.params.levels;
1040
+ return (c) => {
1041
+ c[0] = posterizeValue(c[0], levels);
1042
+ c[1] = posterizeValue(c[1], levels);
1043
+ c[2] = posterizeValue(c[2], levels);
1044
+ };
1045
+ }
1046
+ case "threshold": {
1047
+ const t = Math.round(layer.params.level);
1048
+ return (c) => {
1049
+ const v = Math.round(rgbToGray(c) * 255) >= t ? 1 : 0;
1050
+ c[0] = c[1] = c[2] = v;
1051
+ };
1052
+ }
1053
+ }
1054
+ return null;
1055
+ }
1056
+ var COMPILE_CACHE_LIMIT = 64;
1057
+ var compileCache = /* @__PURE__ */ new Map();
1058
+ function compiledFor(layer) {
1059
+ const key = `${layer.kind}:${JSON.stringify(layer.params)}`;
1060
+ if (compileCache.has(key)) {
1061
+ const op2 = compileCache.get(key);
1062
+ compileCache.delete(key);
1063
+ compileCache.set(key, op2);
1064
+ return op2;
1065
+ }
1066
+ const op = compileAdjustmentLayer(layer);
1067
+ compileCache.set(key, op);
1068
+ if (compileCache.size > COMPILE_CACHE_LIMIT) compileCache.delete(compileCache.keys().next().value);
1069
+ return op;
1070
+ }
1071
+ function activeAdjustmentLayers(layers) {
1072
+ if (!_optionalChain([layers, 'optionalAccess', _5 => _5.length])) return [];
1073
+ return layers.filter((l) => l.enabled !== false && (_nullishCoalesce(l.opacity, () => ( 1))) > 0 && compiledFor(l) !== null);
1074
+ }
1075
+ function evaluateAdjustmentLayers(color, layers) {
1076
+ const c = [color[0], color[1], color[2]];
1077
+ for (const layer of layers) applyLayer(c, layer, compiledFor(layer));
1078
+ return c;
1079
+ }
1080
+ function applyLayer(c, layer, op) {
1081
+ if (!op || layer.enabled === false) return;
1082
+ const alpha = Math.min(1, Math.max(0, _nullishCoalesce(layer.opacity, () => ( 1))));
1083
+ if (alpha <= 0) return;
1084
+ const blend = _nullishCoalesce(layer.blend, () => ( "normal"));
1085
+ if (blend === "normal" && alpha >= 1) {
1086
+ op(c);
1087
+ return;
1088
+ }
1089
+ const cb0 = c[0], cb1 = c[1], cb2 = c[2];
1090
+ op(c);
1091
+ const b = blend === "normal" ? c : blendRgb(blend, [cb0, cb1, cb2], c);
1092
+ c[0] = (1 - alpha) * cb0 + alpha * b[0];
1093
+ c[1] = (1 - alpha) * cb1 + alpha * b[1];
1094
+ c[2] = (1 - alpha) * cb2 + alpha * b[2];
1095
+ }
1096
+ function applyAdjustmentLayers(imageData, layers) {
1097
+ const active = activeAdjustmentLayers(layers);
1098
+ if (!active.length) return;
1099
+ const ops = active.map((l) => compiledFor(l));
1100
+ const data = imageData.data;
1101
+ const len = data.length;
1102
+ const c = [0, 0, 0];
1103
+ for (let i = 0; i < len; i += 4) {
1104
+ if (data[i + 3] === 0) continue;
1105
+ c[0] = data[i] / 255;
1106
+ c[1] = data[i + 1] / 255;
1107
+ c[2] = data[i + 2] / 255;
1108
+ for (let k = 0; k < active.length; k++) applyLayer(c, active[k], ops[k]);
1109
+ data[i] = Math.round(clamp01(c[0]) * 255);
1110
+ data[i + 1] = Math.round(clamp01(c[1]) * 255);
1111
+ data[i + 2] = Math.round(clamp01(c[2]) * 255);
1112
+ }
1113
+ }
1114
+ function adjustmentLayersKey(layers) {
1115
+ if (!_optionalChain([layers, 'optionalAccess', _6 => _6.length])) return "";
1116
+ return JSON.stringify(
1117
+ layers.map((l) => [l.kind, l.enabled !== false, _nullishCoalesce(l.opacity, () => ( 1)), _nullishCoalesce(l.blend, () => ( "normal")), l.params])
1118
+ );
1119
+ }
1120
+
1121
+ // src/image-editor/filters.ts
2
1122
  function clamp(value, min = 0, max = 255) {
3
1123
  return Math.max(min, Math.min(max, value));
4
1124
  }
5
1125
  function applyAdjustments(imageData, adjustments) {
6
- const { brightness, contrast, saturation, exposure, temperature, opacity, blur } = adjustments;
1126
+ if (normalizeProcessVersion(adjustments.process) === LATEST_PROCESS_VERSION) {
1127
+ applyProcess2Adjustments(imageData, adjustments);
1128
+ } else {
1129
+ applyLegacyAdjustments(imageData, adjustments);
1130
+ }
1131
+ applyAdjustmentLayers(imageData, adjustments.layers);
1132
+ if (adjustments.blur > 0) {
1133
+ applyFastBoxBlur(imageData, Math.min(Math.round(adjustments.blur), 40));
1134
+ }
1135
+ }
1136
+ function applyLegacyAdjustments(imageData, adjustments) {
1137
+ const { brightness, contrast, saturation, exposure, temperature, opacity } = adjustments;
7
1138
  const data = imageData.data;
8
1139
  const len = data.length;
9
1140
  const hasBrightness = brightness !== 0;
@@ -57,8 +1188,108 @@ function applyAdjustments(imageData, adjustments) {
57
1188
  data[i + 3] = a;
58
1189
  }
59
1190
  }
60
- if (blur > 0) {
61
- applyFastBoxBlur(imageData, Math.min(Math.round(blur), 40));
1191
+ }
1192
+ var DECODE_LUT = (() => {
1193
+ const lut = new Float32Array(256);
1194
+ for (let i = 0; i < 256; i++) lut[i] = srgbToLinear(i / 255);
1195
+ return lut;
1196
+ })();
1197
+ var ENCODE_LUT_SIZE = 65535;
1198
+ var encodeLut = null;
1199
+ function getEncodeLut() {
1200
+ if (!encodeLut) {
1201
+ encodeLut = new Uint8Array(ENCODE_LUT_SIZE + 1);
1202
+ for (let i = 0; i <= ENCODE_LUT_SIZE; i++) {
1203
+ encodeLut[i] = Math.round(linearToSrgb(i / ENCODE_LUT_SIZE) * 255);
1204
+ }
1205
+ }
1206
+ return encodeLut;
1207
+ }
1208
+ var cachedPlan = null;
1209
+ function getProcess2Plan(adjustments) {
1210
+ const exposure = adjustments.exposure || 0;
1211
+ const temperature = adjustments.temperature || 0;
1212
+ const tint = adjustments.tint || 0;
1213
+ const contrast = adjustments.contrast || 0;
1214
+ const brightness = adjustments.brightness || 0;
1215
+ const saturation = 1 + (adjustments.saturation || 0) / 100;
1216
+ const key = `${exposure},${temperature},${tint},${contrast},${brightness},${saturation}`;
1217
+ if (cachedPlan && cachedPlan.key === key) return cachedPlan;
1218
+ const gain = Math.pow(2, exposure / 50);
1219
+ const wb = whiteBalanceMatrix(temperature, tint);
1220
+ const m = new Float64Array(9);
1221
+ for (let i = 0; i < 9; i++) m[i] = wb[i] * gain;
1222
+ const luts = [];
1223
+ const scale = [0, 0, 0];
1224
+ for (let c = 0; c < 3; c++) {
1225
+ let reach = 0;
1226
+ for (let k = 0; k < 3; k++) reach += Math.max(0, m[c * 3 + k]);
1227
+ const headroom = Math.max(1, reach);
1228
+ luts[c] = buildToneLut(contrast, brightness, headroom);
1229
+ scale[c] = TONE_LUT_SIZE / headroom;
1230
+ }
1231
+ cachedPlan = { key, m, luts, scale, saturation };
1232
+ return cachedPlan;
1233
+ }
1234
+ function applyProcess2Adjustments(imageData, adjustments) {
1235
+ const data = imageData.data;
1236
+ const len = data.length;
1237
+ const opacity = _nullishCoalesce(adjustments.opacity, () => ( 1));
1238
+ const hasOpacity = opacity < 1;
1239
+ const hasColor = !!adjustments.brightness || !!adjustments.contrast || !!adjustments.saturation || !!adjustments.exposure || !!adjustments.temperature || !!adjustments.tint;
1240
+ if (!hasColor) {
1241
+ if (hasOpacity) {
1242
+ for (let i = 3; i < len; i += 4) data[i] = clamp(data[i] * opacity);
1243
+ }
1244
+ return;
1245
+ }
1246
+ const plan = getProcess2Plan(adjustments);
1247
+ const dec = DECODE_LUT;
1248
+ const enc = getEncodeLut();
1249
+ const { m, luts, scale, saturation } = plan;
1250
+ const m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7], m8 = m[8];
1251
+ const lutR = luts[0], lutG = luts[1], lutB = luts[2];
1252
+ const sR = scale[0], sG = scale[1], sB = scale[2];
1253
+ const N = TONE_LUT_SIZE;
1254
+ const hasSaturation = saturation !== 1;
1255
+ const wr = LUMA_709[0], wg = LUMA_709[1], wb = LUMA_709[2];
1256
+ for (let i = 0; i < len; i += 4) {
1257
+ const r0 = dec[data[i]];
1258
+ const g0 = dec[data[i + 1]];
1259
+ const b0 = dec[data[i + 2]];
1260
+ let r = m0 * r0 + m1 * g0 + m2 * b0;
1261
+ let g = m3 * r0 + m4 * g0 + m5 * b0;
1262
+ let b = m6 * r0 + m7 * g0 + m8 * b0;
1263
+ let f;
1264
+ let k;
1265
+ if (r <= 0) r = lutR[0];
1266
+ else if ((f = r * sR) >= N) r = lutR[N];
1267
+ else {
1268
+ k = f | 0;
1269
+ r = lutR[k] + (lutR[k + 1] - lutR[k]) * (f - k);
1270
+ }
1271
+ if (g <= 0) g = lutG[0];
1272
+ else if ((f = g * sG) >= N) g = lutG[N];
1273
+ else {
1274
+ k = f | 0;
1275
+ g = lutG[k] + (lutG[k + 1] - lutG[k]) * (f - k);
1276
+ }
1277
+ if (b <= 0) b = lutB[0];
1278
+ else if ((f = b * sB) >= N) b = lutB[N];
1279
+ else {
1280
+ k = f | 0;
1281
+ b = lutB[k] + (lutB[k + 1] - lutB[k]) * (f - k);
1282
+ }
1283
+ if (hasSaturation) {
1284
+ const y = wr * r + wg * g + wb * b;
1285
+ r = y + (r - y) * saturation;
1286
+ g = y + (g - y) * saturation;
1287
+ b = y + (b - y) * saturation;
1288
+ }
1289
+ data[i] = enc[r <= 0 ? 0 : r >= 1 ? ENCODE_LUT_SIZE : r * ENCODE_LUT_SIZE + 0.5 | 0];
1290
+ data[i + 1] = enc[g <= 0 ? 0 : g >= 1 ? ENCODE_LUT_SIZE : g * ENCODE_LUT_SIZE + 0.5 | 0];
1291
+ data[i + 2] = enc[b <= 0 ? 0 : b >= 1 ? ENCODE_LUT_SIZE : b * ENCODE_LUT_SIZE + 0.5 | 0];
1292
+ if (hasOpacity) data[i + 3] = clamp(data[i + 3] * opacity);
62
1293
  }
63
1294
  }
64
1295
  function applyFastBoxBlur(imageData, radius) {
@@ -148,6 +1379,63 @@ function removeAlpha(imageData) {
148
1379
  }
149
1380
  }
150
1381
 
1382
+ // src/image-editor/histogram.ts
1383
+ var HISTOGRAM_BINS = 256;
1384
+ function computeHistogram(imageData) {
1385
+ const red = new Uint32Array(HISTOGRAM_BINS);
1386
+ const green = new Uint32Array(HISTOGRAM_BINS);
1387
+ const blue = new Uint32Array(HISTOGRAM_BINS);
1388
+ const luminance = new Uint32Array(HISTOGRAM_BINS);
1389
+ const shadows = [0, 0, 0];
1390
+ const highlights = [0, 0, 0];
1391
+ let samples = 0;
1392
+ let transparent = 0;
1393
+ const data = imageData.data;
1394
+ const len = data.length - data.length % 4;
1395
+ for (let i = 0; i < len; i += 4) {
1396
+ if (!(data[i + 3] > 0)) {
1397
+ transparent++;
1398
+ continue;
1399
+ }
1400
+ samples++;
1401
+ const r = clampByte(data[i]);
1402
+ const g = clampByte(data[i + 1]);
1403
+ const b = clampByte(data[i + 2]);
1404
+ red[r]++;
1405
+ green[g]++;
1406
+ blue[b]++;
1407
+ luminance[Math.round(0.2126 * r + 0.7152 * g + 0.0722 * b)]++;
1408
+ if (r === 0) shadows[0]++;
1409
+ if (g === 0) shadows[1]++;
1410
+ if (b === 0) shadows[2]++;
1411
+ if (r === 255) highlights[0]++;
1412
+ if (g === 255) highlights[1]++;
1413
+ if (b === 255) highlights[2]++;
1414
+ }
1415
+ let max = 0;
1416
+ for (const bins of [red, green, blue, luminance]) {
1417
+ for (let k = 0; k < HISTOGRAM_BINS; k++) if (bins[k] > max) max = bins[k];
1418
+ }
1419
+ return { red, green, blue, luminance, samples, transparent, shadows, highlights, max };
1420
+ }
1421
+ function clampByte(v) {
1422
+ return v <= 0 ? 0 : v >= 255 ? 255 : Math.round(v);
1423
+ }
1424
+ function histogramPath(histogram, channel, width, height, options = {}) {
1425
+ const bins = histogram[channel];
1426
+ const sqrt = (_nullishCoalesce(options.scale, () => ( "sqrt"))) === "sqrt";
1427
+ const f = (v) => sqrt ? Math.sqrt(v) : v;
1428
+ const top = f(_nullishCoalesce(options.max, () => ( histogram.max))) || 1;
1429
+ const dx = width / (HISTOGRAM_BINS - 1);
1430
+ let d = `M0 ${height}`;
1431
+ for (let k = 0; k < HISTOGRAM_BINS; k++) {
1432
+ const y = height - f(bins[k]) / top * height;
1433
+ d += ` L${round2(k * dx)} ${round2(y)}`;
1434
+ }
1435
+ return `${d} L${round2(width)} ${height} Z`;
1436
+ }
1437
+ var round2 = (v) => Math.round(v * 100) / 100;
1438
+
151
1439
  // src/image-editor/geometry.ts
152
1440
  function normalizeAngle(degrees) {
153
1441
  const a = (degrees % 360 + 360) % 360;
@@ -534,12 +1822,19 @@ var ImageCache = (_class = class {constructor() { _class.prototype.__init.call(t
534
1822
  }
535
1823
  }, _class);
536
1824
  var globalImageCache = new ImageCache();
537
- var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this); }
1825
+ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this); }
538
1826
  __init2() {this.imageCache = globalImageCache}
539
1827
  __init3() {this.cachedBaseCanvas = null}
540
1828
  __init4() {this.cachedBaseKey = ""}
541
- __init5() {this.sourceIds = /* @__PURE__ */ new WeakMap()}
542
- __init6() {this.nextSourceId = 0}
1829
+ /**
1830
+ * Histogram of the last viewport base render (after adjustments and layers, before
1831
+ * annotations); `null` until a render with pixel access has happened.
1832
+ */
1833
+ __init5() {this.previewHistogram = null}
1834
+ /** Called whenever the viewport base image is re-rendered with the new histogram. */
1835
+
1836
+ __init6() {this.sourceIds = /* @__PURE__ */ new WeakMap()}
1837
+ __init7() {this.nextSourceId = 0}
543
1838
  /**
544
1839
  * Helper to create a canvas element safely in browser/JSDOM.
545
1840
  */
@@ -593,14 +1888,20 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
593
1888
  `${naturalWidth}x${naturalHeight}`,
594
1889
  `${region.x},${region.y},${region.width},${region.height}`,
595
1890
  `${projection.angle},${transform.flipH},${transform.flipV}`,
596
- `${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.blur},${adjustments.opacity}`,
597
- `${depthMask.enabled},${depthMask.maskSource},${_optionalChain([depthMask, 'access', _ => _.depthRange, 'optionalAccess', _2 => _2[0]])}-${_optionalChain([depthMask, 'access', _3 => _3.depthRange, 'optionalAccess', _4 => _4[1]])},${depthMask.softness},${depthMask.invert},${depthMask.mode}`
1891
+ `${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${_nullishCoalesce(adjustments.tint, () => ( 0))},${adjustments.blur},${adjustments.opacity},p${normalizeProcessVersion(adjustments.process)}`,
1892
+ adjustmentLayersKey(adjustments.layers),
1893
+ `${depthMask.enabled},${depthMask.maskSource},${_optionalChain([depthMask, 'access', _7 => _7.depthRange, 'optionalAccess', _8 => _8[0]])}-${_optionalChain([depthMask, 'access', _9 => _9.depthRange, 'optionalAccess', _10 => _10[1]])},${depthMask.softness},${depthMask.invert},${depthMask.mode}`
598
1894
  ].join("|");
599
1895
  let baseCanvas = this.cachedBaseCanvas;
600
1896
  if (!baseCanvas || this.cachedBaseKey !== baseKey) {
601
- baseCanvas = await this.renderBaseImage(img, naturalWidth, naturalHeight, region, transform, state);
1897
+ let histogram = null;
1898
+ baseCanvas = await this.renderBaseImage(img, naturalWidth, naturalHeight, region, transform, state, (pixels) => {
1899
+ histogram = computeHistogram(pixels);
1900
+ });
602
1901
  this.cachedBaseCanvas = baseCanvas;
603
1902
  this.cachedBaseKey = baseKey;
1903
+ this.previewHistogram = histogram;
1904
+ _optionalChain([this, 'access', _11 => _11.onPreviewHistogram, 'optionalCall', _12 => _12(histogram)]);
604
1905
  }
605
1906
  const drawW = rotated.width * projection.scale;
606
1907
  const drawH = rotated.height * projection.scale;
@@ -631,7 +1932,7 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
631
1932
  * Draws `region` of the source image with rotation/flip applied, then pixel adjustments
632
1933
  * and the depth mask (which receives the identical transform so it stays aligned).
633
1934
  */
634
- async renderBaseImage(img, naturalWidth, naturalHeight, region, transform, state) {
1935
+ async renderBaseImage(img, naturalWidth, naturalHeight, region, transform, state, onPixels) {
635
1936
  const { adjustments, depthMask } = state;
636
1937
  const rotated = getRotatedSize(region.width, region.height, transform);
637
1938
  const baseW = Math.max(1, Math.round(rotated.width));
@@ -688,6 +1989,7 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
688
1989
  } catch (e3) {
689
1990
  }
690
1991
  }
1992
+ _optionalChain([onPixels, 'optionalCall', _13 => _13(imageData)]);
691
1993
  baseCtx.putImageData(imageData, 0, 0);
692
1994
  }
693
1995
  return baseCanvas;
@@ -705,6 +2007,59 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
705
2007
  * Draws an individual annotation onto the canvas.
706
2008
  */
707
2009
  async drawAnnotation(ctx, ann) {
2010
+ const blend = normalizeBlendMode(ann.blend);
2011
+ if (blend === "normal") {
2012
+ await this.drawAnnotationContent(ctx, ann);
2013
+ return;
2014
+ }
2015
+ const op = canvasCompositeOperation(blend);
2016
+ if (op) {
2017
+ ctx.save();
2018
+ ctx.globalCompositeOperation = op;
2019
+ await this.drawAnnotationContent(ctx, ann);
2020
+ ctx.restore();
2021
+ return;
2022
+ }
2023
+ if (!await this.drawAnnotationBlended(ctx, ann)) {
2024
+ await this.drawAnnotationContent(ctx, ann);
2025
+ }
2026
+ }
2027
+ /**
2028
+ * Scalar fallback for blend modes the canvas cannot composite (Photoshop soft light, linear /
2029
+ * vivid / pin light, hard mix, subtract, divide, darker / lighter colour, linear burn / dodge):
2030
+ * draws the annotation on a same-size layer with the same transform, blends it into a copy of
2031
+ * the target's pixels with `blendImageData`, and copies the result back inside the current clip.
2032
+ */
2033
+ async drawAnnotationBlended(ctx, ann) {
2034
+ const canvas = ctx.canvas;
2035
+ if (!canvas || typeof ctx.getTransform !== "function" || typeof ctx.getImageData !== "function") return false;
2036
+ const w = canvas.width;
2037
+ const h = canvas.height;
2038
+ const layer = this.createCanvas(w, h);
2039
+ const lctx = layer.getContext("2d");
2040
+ if (!lctx) return false;
2041
+ try {
2042
+ lctx.setTransform(ctx.getTransform());
2043
+ lctx.globalAlpha = ctx.globalAlpha;
2044
+ await this.drawAnnotationContent(lctx, ann);
2045
+ const backdrop = ctx.getImageData(0, 0, w, h);
2046
+ const source = lctx.getImageData(0, 0, w, h);
2047
+ if (!backdrop || !source) return false;
2048
+ blendImageData(backdrop, source, normalizeBlendMode(ann.blend));
2049
+ lctx.setTransform(1, 0, 0, 1, 0, 0);
2050
+ lctx.putImageData(backdrop, 0, 0);
2051
+ } catch (e4) {
2052
+ return false;
2053
+ }
2054
+ ctx.save();
2055
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
2056
+ ctx.globalAlpha = 1;
2057
+ ctx.globalCompositeOperation = "copy";
2058
+ ctx.drawImage(layer, 0, 0);
2059
+ ctx.restore();
2060
+ return true;
2061
+ }
2062
+ async drawAnnotationContent(ctx, ann) {
708
2063
  ctx.save();
709
2064
  if (ann.opacity !== void 0) {
710
2065
  ctx.globalAlpha = ann.opacity;
@@ -807,7 +2162,7 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
807
2162
  try {
808
2163
  const loadedImg = await this.imageCache.get(imgAnn.src);
809
2164
  ctx.drawImage(loadedImg, imgAnn.x, imgAnn.y, imgAnn.width, imgAnn.height);
810
- } catch (e4) {
2165
+ } catch (e5) {
811
2166
  }
812
2167
  break;
813
2168
  }
@@ -960,8 +2315,8 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
960
2315
  if (!ctx) return maskCanvas;
961
2316
  ctx.fillStyle = MASK_BLACK;
962
2317
  ctx.fillRect(0, 0, naturalWidth, naturalHeight);
963
- const useCrop = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.useCrop]), () => ( true));
964
- const useAnnotations = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _6 => _6.useAnnotations]), () => ( true));
2318
+ const useCrop = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _14 => _14.useCrop]), () => ( true));
2319
+ const useAnnotations = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.useAnnotations]), () => ( true));
965
2320
  if (useCrop && state.crop) {
966
2321
  ctx.fillStyle = MASK_WHITE;
967
2322
  ctx.fillRect(state.crop.x, state.crop.y, state.crop.width, state.crop.height);
@@ -971,7 +2326,7 @@ var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__
971
2326
  this.drawAnnotationMask(ctx, ann);
972
2327
  }
973
2328
  }
974
- const imageData = _optionalChain([ctx, 'access', _7 => _7.getImageData, 'optionalCall', _8 => _8(0, 0, naturalWidth, naturalHeight)]);
2329
+ const imageData = _optionalChain([ctx, 'access', _16 => _16.getImageData, 'optionalCall', _17 => _17(0, 0, naturalWidth, naturalHeight)]);
975
2330
  if (imageData) {
976
2331
  const d = imageData.data;
977
2332
  for (let i = 0; i < d.length; i += 4) {
@@ -1058,15 +2413,45 @@ function getBaseProjection(region, transform, baseW, baseH) {
1058
2413
  }
1059
2414
 
1060
2415
  // src/image-editor/controller.ts
2416
+ var IMAGE_EDITOR_SCHEMA_VERSION = 2;
1061
2417
  var DEFAULT_ADJUSTMENTS = {
1062
2418
  brightness: 0,
1063
2419
  contrast: 0,
1064
2420
  saturation: 0,
1065
2421
  exposure: 0,
1066
2422
  temperature: 0,
2423
+ tint: 0,
1067
2424
  blur: 0,
1068
- opacity: 1
2425
+ opacity: 1,
2426
+ process: LATEST_PROCESS_VERSION
1069
2427
  };
2428
+ function normalizeStoredAdjustments(stored) {
2429
+ if (!stored) return { ...DEFAULT_ADJUSTMENTS };
2430
+ const out = {
2431
+ ...DEFAULT_ADJUSTMENTS,
2432
+ ...stored,
2433
+ process: normalizeProcessVersion(stored.process)
2434
+ };
2435
+ const layers = normalizeAdjustmentLayers(stored.layers);
2436
+ if (layers.length) out.layers = layers;
2437
+ else delete out.layers;
2438
+ return out;
2439
+ }
2440
+ function migrateSerializedImageEditorState(data) {
2441
+ const adjustments = normalizeStoredAdjustments(data.adjustments);
2442
+ return {
2443
+ version: _optionalChain([adjustments, 'access', _18 => _18.layers, 'optionalAccess', _19 => _19.length]) ? IMAGE_EDITOR_SCHEMA_VERSION : 1,
2444
+ crop: data.crop ? { ...data.crop } : null,
2445
+ transform: { ...DEFAULT_TRANSFORM, ...data.transform },
2446
+ adjustments: serializeAdjustments(adjustments),
2447
+ depthMask: {
2448
+ ...DEFAULT_DEPTH_MASK,
2449
+ ...data.depthMask,
2450
+ depthRange: [..._nullishCoalesce(_optionalChain([data, 'access', _20 => _20.depthMask, 'optionalAccess', _21 => _21.depthRange]), () => ( DEFAULT_DEPTH_MASK.depthRange))]
2451
+ },
2452
+ annotations: JSON.parse(JSON.stringify(_nullishCoalesce(data.annotations, () => ( []))))
2453
+ };
2454
+ }
1070
2455
  var DEFAULT_TRANSFORM = {
1071
2456
  rotate: 0,
1072
2457
  flipH: false,
@@ -1083,39 +2468,40 @@ var DEFAULT_DEPTH_MASK = {
1083
2468
  var ImageEditorController = (_class3 = class {
1084
2469
 
1085
2470
 
1086
- __init7() {this.imageElement = null}
1087
- __init8() {this.listeners = /* @__PURE__ */ new Set()}
2471
+ __init8() {this.imageElement = null}
2472
+ __init9() {this.listeners = /* @__PURE__ */ new Set()}
2473
+ __init10() {this.histogramListeners = /* @__PURE__ */ new Set()}
1088
2474
 
1089
2475
  // History stacks
1090
- __init9() {this.undoStack = []}
1091
- __init10() {this.redoStack = []}
2476
+ __init11() {this.undoStack = []}
2477
+ __init12() {this.redoStack = []}
1092
2478
 
1093
- __init11() {this.isApplyingHistory = false}
2479
+ __init13() {this.isApplyingHistory = false}
1094
2480
 
1095
- constructor(options = {}) {;_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);
2481
+ constructor(options = {}) {;_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);
1096
2482
  this.maxHistoryDepth = _nullishCoalesce(options.maxHistoryDepth, () => ( 50));
1097
2483
  this.onStateChange = options.onStateChange;
1098
2484
  this.renderer = new ImageEditorRenderer();
1099
- const initialAdjustments = {
1100
- ...DEFAULT_ADJUSTMENTS,
1101
- ..._optionalChain([options, 'access', _9 => _9.initialState, 'optionalAccess', _10 => _10.adjustments])
2485
+ this.renderer.onPreviewHistogram = (histogram) => {
2486
+ this.histogramListeners.forEach((l) => l(histogram));
1102
2487
  };
2488
+ const initialAdjustments = normalizeStoredAdjustments(_optionalChain([options, 'access', _22 => _22.initialState, 'optionalAccess', _23 => _23.adjustments]));
1103
2489
  const initialTransform = {
1104
2490
  ...DEFAULT_TRANSFORM,
1105
- ..._optionalChain([options, 'access', _11 => _11.initialState, 'optionalAccess', _12 => _12.transform])
2491
+ ..._optionalChain([options, 'access', _24 => _24.initialState, 'optionalAccess', _25 => _25.transform])
1106
2492
  };
1107
2493
  const initialDepthMask = {
1108
2494
  ...DEFAULT_DEPTH_MASK,
1109
- ..._optionalChain([options, 'access', _13 => _13.initialState, 'optionalAccess', _14 => _14.depthMask])
2495
+ ..._optionalChain([options, 'access', _26 => _26.initialState, 'optionalAccess', _27 => _27.depthMask])
1110
2496
  };
1111
2497
  this.state = {
1112
2498
  sourceUrl: typeof options.image === "string" ? options.image : null,
1113
2499
  imageDimensions: { width: 800, height: 600 },
1114
- crop: _nullishCoalesce(_optionalChain([options, 'access', _15 => _15.initialState, 'optionalAccess', _16 => _16.crop]), () => ( null)),
2500
+ crop: _nullishCoalesce(_optionalChain([options, 'access', _28 => _28.initialState, 'optionalAccess', _29 => _29.crop]), () => ( null)),
1115
2501
  transform: initialTransform,
1116
2502
  adjustments: initialAdjustments,
1117
2503
  depthMask: initialDepthMask,
1118
- annotations: _nullishCoalesce(_optionalChain([options, 'access', _17 => _17.initialState, 'optionalAccess', _18 => _18.annotations]), () => ( [])),
2504
+ annotations: _nullishCoalesce(_optionalChain([options, 'access', _30 => _30.initialState, 'optionalAccess', _31 => _31.annotations]), () => ( [])),
1119
2505
  selectedAnnotationId: null,
1120
2506
  activeTool: "select",
1121
2507
  zoom: 1,
@@ -1162,7 +2548,7 @@ var ImageEditorController = (_class3 = class {
1162
2548
  height: img.naturalHeight || img.height || 600
1163
2549
  }
1164
2550
  };
1165
- } catch (e5) {
2551
+ } catch (e6) {
1166
2552
  return { sourceUrl: src, element: null, dimensions: { ...this.state.imageDimensions } };
1167
2553
  }
1168
2554
  }
@@ -1191,7 +2577,7 @@ var ImageEditorController = (_class3 = class {
1191
2577
  }
1192
2578
  notify() {
1193
2579
  const currentState = { ...this.state };
1194
- _optionalChain([this, 'access', _19 => _19.onStateChange, 'optionalCall', _20 => _20(currentState)]);
2580
+ _optionalChain([this, 'access', _32 => _32.onStateChange, 'optionalCall', _33 => _33(currentState)]);
1195
2581
  this.listeners.forEach((l) => l(currentState));
1196
2582
  }
1197
2583
  // --- History (Undo / Redo / Reset) ---
@@ -1340,16 +2726,154 @@ var ImageEditorController = (_class3 = class {
1340
2726
  if (commitToHistory) {
1341
2727
  this.recordHistory();
1342
2728
  }
1343
- this.state.adjustments = {
2729
+ const next = {
1344
2730
  ...this.state.adjustments,
1345
2731
  ...partial
1346
2732
  };
2733
+ if ("layers" in partial) {
2734
+ const layers = normalizeAdjustmentLayers(partial.layers);
2735
+ if (layers.length) next.layers = layers;
2736
+ else delete next.layers;
2737
+ }
2738
+ this.state.adjustments = next;
1347
2739
  this.notify();
1348
2740
  }
2741
+ /** True when the adjustments use an older rendering process than {@link LATEST_PROCESS_VERSION}. */
2742
+ get isProcessOutdated() {
2743
+ return normalizeProcessVersion(this.state.adjustments.process) < LATEST_PROCESS_VERSION;
2744
+ }
2745
+ /**
2746
+ * Update to Current Process: re-interprets the current adjustment values with the latest
2747
+ * rendering process (one undo step). Returns false when already current.
2748
+ */
2749
+ upgradeProcess() {
2750
+ if (!this.isProcessOutdated) return false;
2751
+ this.setAdjustments({ process: LATEST_PROCESS_VERSION });
2752
+ return true;
2753
+ }
2754
+ /** Resets the base sliders (latest process). Adjustment layers are kept; see `clearAdjustmentLayers`. */
1349
2755
  resetAdjustments() {
1350
2756
  this.recordHistory();
2757
+ const layers = this.state.adjustments.layers;
1351
2758
  this.state.adjustments = { ...DEFAULT_ADJUSTMENTS };
2759
+ if (_optionalChain([layers, 'optionalAccess', _34 => _34.length])) this.state.adjustments.layers = layers;
2760
+ this.notify();
2761
+ }
2762
+ // --- Adjustment layers ---
2763
+ /** The adjustment layers, bottom (first) to top (last). */
2764
+ getAdjustmentLayers() {
2765
+ return _nullishCoalesce(this.state.adjustments.layers, () => ( []));
2766
+ }
2767
+ getAdjustmentLayer(id) {
2768
+ return this.getAdjustmentLayers().find((l) => l.id === id);
2769
+ }
2770
+ setLayers(layers) {
2771
+ const next = { ...this.state.adjustments };
2772
+ if (layers.length) next.layers = layers;
2773
+ else delete next.layers;
2774
+ this.state.adjustments = next;
2775
+ }
2776
+ /**
2777
+ * Adds an adjustment layer on top (or at `index`, 0 = bottom) as one undo step and returns
2778
+ * its id. `kind` alone gives neutral parameters.
2779
+ */
2780
+ addAdjustmentLayer(init, index) {
2781
+ const spec = typeof init === "string" ? { kind: init } : init;
2782
+ const layers = [...this.getAdjustmentLayers()];
2783
+ const layer = createAdjustmentLayer(spec.kind, {
2784
+ id: spec.id && !layers.some((l) => l.id === spec.id) ? spec.id : void 0,
2785
+ name: spec.name,
2786
+ params: spec.params,
2787
+ enabled: spec.enabled,
2788
+ opacity: spec.opacity,
2789
+ blend: spec.blend
2790
+ });
2791
+ this.recordHistory();
2792
+ const at = index === void 0 ? layers.length : Math.max(0, Math.min(layers.length, Math.round(index)));
2793
+ layers.splice(at, 0, layer);
2794
+ this.setLayers(layers);
2795
+ this.notify();
2796
+ return layer.id;
2797
+ }
2798
+ /**
2799
+ * Updates a layer (`params` merged into the current ones, then normalised). Pass
2800
+ * `commitToHistory = false` for the moves of a drag after recording its start once.
2801
+ * Returns false for an unknown id.
2802
+ */
2803
+ updateAdjustmentLayer(id, update, commitToHistory = true) {
2804
+ const layers = this.getAdjustmentLayers();
2805
+ const index = layers.findIndex((l) => l.id === id);
2806
+ if (index === -1) return false;
2807
+ const current = layers[index];
2808
+ const merged = normalizeAdjustmentLayer({
2809
+ ...current,
2810
+ ...update.enabled !== void 0 ? { enabled: update.enabled } : {},
2811
+ ...update.opacity !== void 0 ? { opacity: update.opacity } : {},
2812
+ ...update.blend !== void 0 ? { blend: update.blend } : {},
2813
+ ...update.name !== void 0 ? { name: update.name } : {},
2814
+ params: { ...current.params, ..._nullishCoalesce(update.params, () => ( {})) }
2815
+ });
2816
+ if (!merged) return false;
2817
+ if (commitToHistory) this.recordHistory();
2818
+ const next = [...layers];
2819
+ next[index] = merged;
2820
+ this.setLayers(next);
2821
+ this.notify();
2822
+ return true;
2823
+ }
2824
+ /** Removes a layer as one undo step. Returns false for an unknown id. */
2825
+ removeAdjustmentLayer(id) {
2826
+ const layers = this.getAdjustmentLayers();
2827
+ if (!layers.some((l) => l.id === id)) return false;
2828
+ this.recordHistory();
2829
+ this.setLayers(layers.filter((l) => l.id !== id));
2830
+ this.notify();
2831
+ return true;
2832
+ }
2833
+ /** Moves a layer to `toIndex` (0 = bottom) as one undo step. Returns false if nothing moved. */
2834
+ moveAdjustmentLayer(id, toIndex) {
2835
+ const layers = [...this.getAdjustmentLayers()];
2836
+ const from = layers.findIndex((l) => l.id === id);
2837
+ if (from === -1) return false;
2838
+ const to = Math.max(0, Math.min(layers.length - 1, Math.round(toIndex)));
2839
+ if (to === from) return false;
2840
+ this.recordHistory();
2841
+ const [layer] = layers.splice(from, 1);
2842
+ layers.splice(to, 0, layer);
2843
+ this.setLayers(layers);
1352
2844
  this.notify();
2845
+ return true;
2846
+ }
2847
+ /** Shows or hides a layer (flips it without `enabled`) as one undo step. */
2848
+ toggleAdjustmentLayer(id, enabled) {
2849
+ const layer = this.getAdjustmentLayer(id);
2850
+ if (!layer) return false;
2851
+ const next = _nullishCoalesce(enabled, () => ( !layer.enabled));
2852
+ if (next === layer.enabled) return false;
2853
+ return this.updateAdjustmentLayer(id, { enabled: next });
2854
+ }
2855
+ /** Removes every adjustment layer as one undo step. */
2856
+ clearAdjustmentLayers() {
2857
+ if (!this.getAdjustmentLayers().length) return;
2858
+ this.recordHistory();
2859
+ this.setLayers([]);
2860
+ this.notify();
2861
+ }
2862
+ // --- Histogram ---
2863
+ /**
2864
+ * Histogram of the last rendered preview (after adjustments and layers, before annotations),
2865
+ * or `null` before the first render or without canvas pixel access.
2866
+ */
2867
+ getHistogram() {
2868
+ return this.renderer.previewHistogram;
2869
+ }
2870
+ /** Calls `listener` now and whenever a render produces a new preview histogram. */
2871
+ subscribeHistogram(listener) {
2872
+ this.histogramListeners.add(listener);
2873
+ listener(this.renderer.previewHistogram);
2874
+ return () => {
2875
+ this.histogramListeners.delete(listener);
2876
+ };
1353
2877
  }
1354
2878
  // --- Depth Masking (Gleamforge) ---
1355
2879
  setDepthMask(partial) {
@@ -1436,7 +2960,7 @@ var ImageEditorController = (_class3 = class {
1436
2960
  if (!source) return;
1437
2961
  try {
1438
2962
  await this.renderer.renderToCanvas(targetCanvas, source, this.state, options);
1439
- } catch (e6) {
2963
+ } catch (e7) {
1440
2964
  }
1441
2965
  }
1442
2966
  async renderExport(options) {
@@ -1556,15 +3080,16 @@ var ImageEditorController = (_class3 = class {
1556
3080
  }
1557
3081
  loadJSON(data) {
1558
3082
  this.recordHistory();
1559
- this.applySerializedState(data);
3083
+ this.applySerializedState(migrateSerializedImageEditorState(data));
1560
3084
  this.notify();
1561
3085
  }
1562
3086
  serializeState() {
1563
3087
  return {
1564
- version: 1,
3088
+ // States without adjustment layers keep schema 1, so they round-trip unchanged.
3089
+ version: _optionalChain([this, 'access', _35 => _35.state, 'access', _36 => _36.adjustments, 'access', _37 => _37.layers, 'optionalAccess', _38 => _38.length]) ? IMAGE_EDITOR_SCHEMA_VERSION : 1,
1565
3090
  crop: this.state.crop ? { ...this.state.crop } : null,
1566
3091
  transform: { ...this.state.transform },
1567
- adjustments: { ...this.state.adjustments },
3092
+ adjustments: serializeAdjustments(this.state.adjustments),
1568
3093
  depthMask: {
1569
3094
  ...this.state.depthMask,
1570
3095
  depthRange: [...this.state.depthMask.depthRange]
@@ -1575,7 +3100,7 @@ var ImageEditorController = (_class3 = class {
1575
3100
  applySerializedState(data) {
1576
3101
  this.state.crop = data.crop ? { ...data.crop } : null;
1577
3102
  this.state.transform = { ...data.transform };
1578
- this.state.adjustments = { ...data.adjustments };
3103
+ this.state.adjustments = normalizeStoredAdjustments(data.adjustments);
1579
3104
  this.state.depthMask = {
1580
3105
  ...data.depthMask,
1581
3106
  depthRange: [...data.depthMask.depthRange]
@@ -1583,6 +3108,16 @@ var ImageEditorController = (_class3 = class {
1583
3108
  this.state.annotations = JSON.parse(JSON.stringify(data.annotations || []));
1584
3109
  }
1585
3110
  }, _class3);
3111
+ function serializeAdjustments(adjustments) {
3112
+ const out = { ...adjustments };
3113
+ if (_optionalChain([out, 'access', _39 => _39.layers, 'optionalAccess', _40 => _40.length])) out.layers = JSON.parse(JSON.stringify(out.layers));
3114
+ else delete out.layers;
3115
+ if (normalizeProcessVersion(out.process) === LEGACY_PROCESS_VERSION) {
3116
+ delete out.process;
3117
+ if (!out.tint) delete out.tint;
3118
+ }
3119
+ return out;
3120
+ }
1586
3121
  function scaleAnnotation(ann, sx, sy) {
1587
3122
  const k = Math.sqrt(Math.abs(sx * sy));
1588
3123
  const base = { ...ann, x: ann.x * sx, y: ann.y * sy };
@@ -1603,6 +3138,677 @@ function scaleAnnotation(ann, sx, sy) {
1603
3138
  }
1604
3139
  }
1605
3140
 
3141
+ // src/image-editor/document.ts
3142
+ var IMAGE_EDITOR_STATE_VERSION = 1;
3143
+ var imageEditorStateMigrations = {};
3144
+ var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3145
+ var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
3146
+ function expectObject(value, what) {
3147
+ if (!isObject(value)) throw new Error(`${what} must be an object`);
3148
+ return value;
3149
+ }
3150
+ function checkFields(value, types, what) {
3151
+ for (const [field, ok] of Object.entries(types)) {
3152
+ if (field in value && value[field] !== void 0 && !ok(value[field])) {
3153
+ throw new Error(`${what}.${field} has an invalid value`);
3154
+ }
3155
+ }
3156
+ }
3157
+ var isBoolean = (v) => typeof v === "boolean";
3158
+ function parseCrop(value) {
3159
+ if (value === null) return null;
3160
+ const crop = expectObject(value, "crop");
3161
+ for (const field of ["x", "y", "width", "height"]) {
3162
+ if (!isNumber(crop[field])) throw new Error(`crop.${field} must be a number`);
3163
+ }
3164
+ return { ...crop };
3165
+ }
3166
+ function parseTransform(value) {
3167
+ const transform = expectObject(value, "transform");
3168
+ checkFields(transform, { rotate: isNumber, flipH: isBoolean, flipV: isBoolean }, "transform");
3169
+ return { ...DEFAULT_TRANSFORM, ...transform };
3170
+ }
3171
+ var ADJUSTMENT_NUMBERS = [
3172
+ "brightness",
3173
+ "contrast",
3174
+ "saturation",
3175
+ "exposure",
3176
+ "temperature",
3177
+ "tint",
3178
+ "blur",
3179
+ "opacity"
3180
+ ];
3181
+ function parseAdjustments(value) {
3182
+ const adjustments = expectObject(value, "adjustments");
3183
+ checkFields(
3184
+ adjustments,
3185
+ {
3186
+ ...Object.fromEntries(ADJUSTMENT_NUMBERS.map((k) => [k, isNumber])),
3187
+ process: (v) => v === 1 || v === 2
3188
+ },
3189
+ "adjustments"
3190
+ );
3191
+ return { ...adjustments };
3192
+ }
3193
+ function parseDepthMask(value) {
3194
+ const mask = expectObject(value, "depthMask");
3195
+ checkFields(
3196
+ mask,
3197
+ {
3198
+ enabled: isBoolean,
3199
+ maskSource: (v) => v === null || typeof v === "string",
3200
+ mode: (v) => v === "threshold" || v === "brightness",
3201
+ depthRange: (v) => Array.isArray(v) && v.length === 2 && v.every(isNumber),
3202
+ softness: isNumber,
3203
+ invert: isBoolean
3204
+ },
3205
+ "depthMask"
3206
+ );
3207
+ const merged = { ...DEFAULT_DEPTH_MASK, ...mask };
3208
+ return { ...merged, depthRange: [...merged.depthRange] };
3209
+ }
3210
+ var ANNOTATION_TYPES = /* @__PURE__ */ new Set(["pen", "rect", "circle", "arrow", "line", "text", "image"]);
3211
+ var isAnnotation = (value) => isObject(value) && typeof value.id === "string" && typeof value.type === "string" && ANNOTATION_TYPES.has(value.type) && isNumber(value.x) && isNumber(value.y);
3212
+ function loadImageEditorDocument(input) {
3213
+ let dropped = 0;
3214
+ const result = _chunkD6FH2YAXcjs.loadDocument.call(void 0,
3215
+ input,
3216
+ {
3217
+ crop: { parse: parseCrop, defaults: null },
3218
+ transform: { parse: parseTransform, defaults: () => ({ ...DEFAULT_TRANSFORM }) },
3219
+ adjustments: {
3220
+ parse: parseAdjustments,
3221
+ // Same as what `loadJSON` derives from missing adjustments.
3222
+ defaults: () => normalizeStoredAdjustments(void 0)
3223
+ },
3224
+ depthMask: {
3225
+ parse: parseDepthMask,
3226
+ defaults: () => ({ ...DEFAULT_DEPTH_MASK, depthRange: [...DEFAULT_DEPTH_MASK.depthRange] })
3227
+ },
3228
+ annotations: {
3229
+ parse: (value) => {
3230
+ if (!Array.isArray(value)) throw new Error("annotations must be an array");
3231
+ const kept = value.filter(isAnnotation);
3232
+ dropped = value.length - kept.length;
3233
+ return JSON.parse(JSON.stringify(kept));
3234
+ },
3235
+ defaults: () => []
3236
+ }
3237
+ },
3238
+ {
3239
+ currentVersion: IMAGE_EDITOR_STATE_VERSION,
3240
+ steps: imageEditorStateMigrations
3241
+ }
3242
+ );
3243
+ if (dropped > 0) {
3244
+ result.report.problems.push({
3245
+ section: "annotations",
3246
+ message: `${dropped} invalid annotation${dropped === 1 ? "" : "s"} dropped`
3247
+ });
3248
+ }
3249
+ const state = { version: 1, ...result.value };
3250
+ return { state, report: result.report, unknown: result.unknown };
3251
+ }
3252
+
3253
+ // src/image-editor/controls.ts
3254
+ var plain = { kind: "plain" };
3255
+ var centered = { kind: "centered" };
3256
+ var IMAGE_ADJUSTMENT_CONTROLS = [
3257
+ { id: "brightness", label: "Brightness", section: "light", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: centered },
3258
+ { id: "contrast", label: "Contrast", section: "light", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: centered },
3259
+ {
3260
+ id: "saturation",
3261
+ label: "Saturation",
3262
+ section: "color",
3263
+ min: -100,
3264
+ max: 100,
3265
+ step: 1,
3266
+ default: 0,
3267
+ decimals: 0,
3268
+ track: { kind: "gradient", from: "#7a7a7a", to: "#e04a3a" }
3269
+ },
3270
+ { id: "exposure", label: "Exposure", section: "light", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: centered },
3271
+ { id: "temperature", label: "Warmth / Temperature", section: "color", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "temp" } },
3272
+ { id: "tint", label: "Tint", section: "color", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "tint" }, requiresProcess: 2 },
3273
+ { id: "blur", label: "Blur", section: "effects", min: 0, max: 30, step: 1, default: 0, decimals: 0, track: plain, unit: "px" },
3274
+ { id: "opacity", label: "Opacity", section: "effects", min: 0, max: 1, step: 0.05, default: 1, decimals: 0, track: plain, displayScale: 100, unit: "%" }
3275
+ ];
3276
+ var tone = (prefix, label) => [
3277
+ ["0", "Cyan \u2013 Red", "#00b7c7", "#e0343a"],
3278
+ ["1", "Magenta \u2013 Green", "#d23ad2", "#2fb34a"],
3279
+ ["2", "Yellow \u2013 Blue", "#e0c63a", "#3a5be0"]
3280
+ ].map(([i, l, from, to]) => ({
3281
+ id: `${prefix}.${i}`,
3282
+ label: `${label} ${l}`,
3283
+ section: "layer",
3284
+ min: -100,
3285
+ max: 100,
3286
+ step: 1,
3287
+ default: 0,
3288
+ decimals: 0,
3289
+ track: { kind: "gradient", from, to }
3290
+ }));
3291
+ var levelsRecord = (ch, label) => [
3292
+ { id: `${ch}.inBlack`, label: `${label} input black`, section: "layer", min: 0, max: 253, step: 1, default: 0, decimals: 0, track: plain },
3293
+ { id: `${ch}.gamma`, label: `${label} gamma`, section: "layer", min: 0.1, max: 9.99, step: 0.01, default: 1, decimals: 2, track: plain },
3294
+ { id: `${ch}.inWhite`, label: `${label} input white`, section: "layer", min: 2, max: 255, step: 1, default: 255, decimals: 0, track: plain },
3295
+ { id: `${ch}.outBlack`, label: `${label} output black`, section: "layer", min: 0, max: 255, step: 1, default: 0, decimals: 0, track: plain },
3296
+ { id: `${ch}.outWhite`, label: `${label} output white`, section: "layer", min: 0, max: 255, step: 1, default: 255, decimals: 0, track: plain }
3297
+ ];
3298
+ var RANGE_NAMES = ["Reds", "Yellows", "Greens", "Cyans", "Blues", "Magentas"];
3299
+ var ADJUSTMENT_LAYER_CONTROLS = {
3300
+ curves: [],
3301
+ levels: [
3302
+ ...levelsRecord("master", "RGB"),
3303
+ ...levelsRecord("red", "Red"),
3304
+ ...levelsRecord("green", "Green"),
3305
+ ...levelsRecord("blue", "Blue")
3306
+ ],
3307
+ hueSaturation: [
3308
+ { id: "hue", label: "Hue", section: "layer", min: -180, max: 180, step: 1, default: 0, decimals: 0, track: { kind: "rainbow" }, unit: "\xB0" },
3309
+ { id: "saturation", label: "Saturation", section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "gradient", from: "#7a7a7a", to: "#e04a3a" } },
3310
+ { id: "lightness", label: "Lightness", section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "gradient", from: "#000000", to: "#ffffff" } },
3311
+ ...RANGE_NAMES.flatMap((name, i) => [
3312
+ { id: `ranges.${i}.hue`, label: `${name} hue`, section: "layer", min: -180, max: 180, step: 1, default: 0, decimals: 0, track: { kind: "rainbow" }, unit: "\xB0" },
3313
+ { id: `ranges.${i}.saturation`, label: `${name} saturation`, section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: centered },
3314
+ { id: `ranges.${i}.lightness`, label: `${name} lightness`, section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: centered }
3315
+ ])
3316
+ ],
3317
+ vibrance: [
3318
+ { id: "vibrance", label: "Vibrance", section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "gradient", from: "#7a7a7a", to: "#d8406a" } },
3319
+ { id: "saturation", label: "Saturation", section: "layer", min: -100, max: 100, step: 1, default: 0, decimals: 0, track: { kind: "gradient", from: "#7a7a7a", to: "#e04a3a" } }
3320
+ ],
3321
+ colorBalance: [...tone("shadows", "Shadows"), ...tone("midtones", "Midtones"), ...tone("highlights", "Highlights")],
3322
+ blackWhite: [
3323
+ ["reds", "Reds", 40, "#e0343a"],
3324
+ ["yellows", "Yellows", 60, "#e0c63a"],
3325
+ ["greens", "Greens", 40, "#2fb34a"],
3326
+ ["cyans", "Cyans", 60, "#00b7c7"],
3327
+ ["blues", "Blues", 20, "#3a5be0"],
3328
+ ["magentas", "Magentas", 80, "#d23ad2"]
3329
+ ].map(([id, label, def, to]) => ({
3330
+ id,
3331
+ label,
3332
+ section: "layer",
3333
+ min: -200,
3334
+ max: 300,
3335
+ step: 1,
3336
+ default: def,
3337
+ decimals: 0,
3338
+ track: { kind: "gradient", from: "#000000", to },
3339
+ unit: "%"
3340
+ })),
3341
+ invert: [],
3342
+ posterize: [{ id: "levels", label: "Levels", section: "layer", min: 2, max: 255, step: 1, default: 4, decimals: 0, track: plain }],
3343
+ threshold: [{ id: "level", label: "Threshold level", section: "layer", min: 1, max: 255, step: 1, default: 128, decimals: 0, track: { kind: "gradient", from: "#000000", to: "#ffffff" } }],
3344
+ photoFilter: [{ id: "density", label: "Density", section: "layer", min: 0, max: 100, step: 1, default: 25, decimals: 0, track: plain, unit: "%" }]
3345
+ };
3346
+ var ADJUSTMENT_LAYER_OPACITY_CONTROL = {
3347
+ id: "opacity",
3348
+ label: "Opacity",
3349
+ section: "layer",
3350
+ min: 0,
3351
+ max: 1,
3352
+ step: 0.01,
3353
+ default: 1,
3354
+ decimals: 0,
3355
+ track: plain,
3356
+ displayScale: 100,
3357
+ unit: "%"
3358
+ };
3359
+ var BASE_BY_ID = new Map(IMAGE_ADJUSTMENT_CONTROLS.map((c) => [c.id, c]));
3360
+ function findAdjustmentControl(id) {
3361
+ return BASE_BY_ID.get(id);
3362
+ }
3363
+ function findLayerControl(kind, id) {
3364
+ if (id === "opacity") return ADJUSTMENT_LAYER_OPACITY_CONTROL;
3365
+ return _optionalChain([ADJUSTMENT_LAYER_CONTROLS, 'access', _41 => _41[kind], 'optionalAccess', _42 => _42.find, 'call', _43 => _43((c) => c.id === id)]);
3366
+ }
3367
+ function clampControlValue(spec, v) {
3368
+ if (typeof v !== "number" || !Number.isFinite(v)) return spec.default;
3369
+ const clamped = Math.min(spec.max, Math.max(spec.min, v));
3370
+ const steps = Math.round((clamped - spec.min) / spec.step);
3371
+ const snapped = spec.min + steps * spec.step;
3372
+ return Math.min(spec.max, Number(snapped.toFixed(6)));
3373
+ }
3374
+ function formatControlValue(spec, v) {
3375
+ const shown = (Number.isFinite(v) ? v : spec.default) * (_nullishCoalesce(spec.displayScale, () => ( 1)));
3376
+ const s = shown.toFixed(spec.decimals);
3377
+ return `${s}${_nullishCoalesce(spec.unit, () => ( ""))}`;
3378
+ }
3379
+ function controlPosition(spec, v) {
3380
+ const p = (clampControlValue(spec, v) - spec.min) / (spec.max - spec.min || 1);
3381
+ return Math.min(1, Math.max(0, p));
3382
+ }
3383
+ function controlTrackBackground(spec) {
3384
+ const t = spec.track;
3385
+ switch (t.kind) {
3386
+ case "temp":
3387
+ return "linear-gradient(to right, #3a7bd5, #d8d8d8, #f2c94c)";
3388
+ case "tint":
3389
+ return "linear-gradient(to right, #3fbf4f, #d8d8d8, #d23ad2)";
3390
+ case "gradient":
3391
+ return `linear-gradient(to right, ${t.from}, ${t.to})`;
3392
+ case "rainbow":
3393
+ return "linear-gradient(to right, #00ffff, #0000ff, #ff00ff, #ff0000, #ffff00, #00ff00, #00ffff)";
3394
+ default:
3395
+ return void 0;
3396
+ }
3397
+ }
3398
+ function getControlPath(obj, path) {
3399
+ let cur = obj;
3400
+ for (const part of path.split(".")) {
3401
+ if (cur === null || cur === void 0) return void 0;
3402
+ cur = cur[part];
3403
+ }
3404
+ return cur;
3405
+ }
3406
+ function setControlPath(obj, path, value) {
3407
+ const parts = path.split(".");
3408
+ const root = Array.isArray(obj) ? [...obj] : { ...obj };
3409
+ let cur = root;
3410
+ for (let i = 0; i < parts.length - 1; i++) {
3411
+ const next = cur[parts[i]];
3412
+ cur[parts[i]] = Array.isArray(next) ? [...next] : next && typeof next === "object" ? { ...next } : {};
3413
+ cur = cur[parts[i]];
3414
+ }
3415
+ cur[parts[parts.length - 1]] = value;
3416
+ return root;
3417
+ }
3418
+ function readAdjustmentControls(adjustments) {
3419
+ const out = {};
3420
+ for (const c of IMAGE_ADJUSTMENT_CONTROLS) {
3421
+ const v = adjustments[c.id];
3422
+ out[c.id] = typeof v === "number" && Number.isFinite(v) ? v : c.default;
3423
+ }
3424
+ return out;
3425
+ }
3426
+
3427
+ // src/image-editor/tools.ts
3428
+ var LAYER_PATH = /^layers\.([^.]+)\.(.+)$/;
3429
+ function adjustmentsSummary(adjustments) {
3430
+ return {
3431
+ process: normalizeProcessVersion(adjustments.process),
3432
+ values: readAdjustmentControls(adjustments),
3433
+ layers: (_nullishCoalesce(adjustments.layers, () => ( []))).map((l) => JSON.parse(JSON.stringify(l)))
3434
+ };
3435
+ }
3436
+ function createImageEditorToolProvider(controller, options = {}) {
3437
+ const getAdjustments = {
3438
+ name: "get_adjustments",
3439
+ title: "Read image adjustments",
3440
+ description: "Returns the image editor adjustment values by control id, the rendering process version and the adjustment layers (bottom to top).",
3441
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
3442
+ annotations: {
3443
+ title: "Read image adjustments",
3444
+ readOnlyHint: true,
3445
+ destructiveHint: false,
3446
+ idempotentHint: true,
3447
+ openWorldHint: false
3448
+ },
3449
+ handler: () => adjustmentsSummary(controller.getState().adjustments)
3450
+ };
3451
+ const listControls = {
3452
+ name: "list_controls",
3453
+ title: "List adjustment controls",
3454
+ description: "Lists every adjustment control (id, label, section, min, max, step, default), the parameters of each adjustment-layer kind, and the blend modes.",
3455
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
3456
+ annotations: {
3457
+ title: "List adjustment controls",
3458
+ readOnlyHint: true,
3459
+ destructiveHint: false,
3460
+ idempotentHint: true,
3461
+ openWorldHint: false
3462
+ },
3463
+ handler: () => ({
3464
+ controls: IMAGE_ADJUSTMENT_CONTROLS.map(({ id, label, section, min, max, step, default: def, requiresProcess }) => ({
3465
+ id,
3466
+ label,
3467
+ section,
3468
+ min,
3469
+ max,
3470
+ step,
3471
+ default: def,
3472
+ ...requiresProcess ? { requiresProcess } : {}
3473
+ })),
3474
+ layerKinds: ADJUSTMENT_KINDS.map(({ kind, label }) => ({
3475
+ kind,
3476
+ label,
3477
+ controls: [...ADJUSTMENT_LAYER_CONTROLS[kind], ADJUSTMENT_LAYER_OPACITY_CONTROL].map(({ id, label: l, min, max, step, default: def }) => ({
3478
+ id,
3479
+ label: l,
3480
+ min,
3481
+ max,
3482
+ step,
3483
+ default: def
3484
+ }))
3485
+ })),
3486
+ blendModes: BLEND_MODES.map((m) => m.id),
3487
+ layerPathFormat: "layers.<layerId>.<controlId>"
3488
+ })
3489
+ };
3490
+ const setAdjustments = {
3491
+ name: "set_adjustments",
3492
+ title: "Set image adjustments",
3493
+ description: 'Sets adjustment values by control id in one undoable step. Base controls use their id (e.g. "exposure"); layer parameters use "layers.<layerId>.<controlId>" (e.g. "layers.adj_1.vibrance"). Values are clamped to each control range; the applied values are returned.',
3494
+ inputSchema: {
3495
+ type: "object",
3496
+ properties: {
3497
+ values: {
3498
+ type: "object",
3499
+ description: "Map of control id to number. See list_controls for ids and ranges.",
3500
+ additionalProperties: { type: "number" }
3501
+ },
3502
+ label: { type: "string", description: "Short description of the change (echoed back)." }
3503
+ },
3504
+ required: ["values"],
3505
+ additionalProperties: false
3506
+ },
3507
+ annotations: {
3508
+ title: "Set image adjustments",
3509
+ readOnlyHint: false,
3510
+ destructiveHint: false,
3511
+ idempotentHint: true,
3512
+ openWorldHint: false
3513
+ },
3514
+ handler: ({ values, label }) => {
3515
+ const entries = Object.entries(_nullishCoalesce(values, () => ( {})));
3516
+ if (!entries.length) throw new Error('No values given. Pass {"values": {"<control id>": number}}.');
3517
+ const state = controller.getState();
3518
+ const base = {};
3519
+ const layers = (_nullishCoalesce(state.adjustments.layers, () => ( []))).map((l) => ({ ...l }));
3520
+ const applied = {};
3521
+ const clamped = [];
3522
+ const unknown = [];
3523
+ for (const [id, raw] of entries) {
3524
+ const m = LAYER_PATH.exec(id);
3525
+ if (m) {
3526
+ const layer = layers.find((l) => l.id === m[1]);
3527
+ const spec2 = layer ? findLayerControl(layer.kind, m[2]) : void 0;
3528
+ if (!layer || !spec2) {
3529
+ unknown.push(id);
3530
+ continue;
3531
+ }
3532
+ const v2 = clampControlValue(spec2, raw);
3533
+ if (v2 !== raw) clamped.push(id);
3534
+ if (spec2.id === "opacity") layer.opacity = v2;
3535
+ else layer.params = setControlPath(layer.params, spec2.id, v2);
3536
+ applied[id] = v2;
3537
+ continue;
3538
+ }
3539
+ const spec = findAdjustmentControl(id);
3540
+ if (!spec) {
3541
+ unknown.push(id);
3542
+ continue;
3543
+ }
3544
+ const v = clampControlValue(spec, raw);
3545
+ if (v !== raw) clamped.push(id);
3546
+ base[id] = v;
3547
+ applied[id] = v;
3548
+ }
3549
+ if (unknown.length) {
3550
+ const accepted = IMAGE_ADJUSTMENT_CONTROLS.map((c) => c.id).join(", ");
3551
+ throw new Error(
3552
+ `Unknown control id(s): ${unknown.join(", ")}. Accepted: ${accepted}, or layers.<layerId>.<controlId> for an existing layer.`
3553
+ );
3554
+ }
3555
+ const changedLayers = Object.keys(applied).some((id) => id.startsWith("layers."));
3556
+ controller.setAdjustments(changedLayers ? { ...base, layers } : base);
3557
+ const result = { applied, clamped };
3558
+ if (typeof label === "string" && label) result.label = label;
3559
+ const notes = [];
3560
+ if ("tint" in applied && normalizeProcessVersion(controller.getState().adjustments.process) < 2) {
3561
+ notes.push("Tint has no effect on process 1 edits.");
3562
+ }
3563
+ if (notes.length) result.notes = notes;
3564
+ return result;
3565
+ }
3566
+ };
3567
+ const addLayer = {
3568
+ name: "add_adjustment_layer",
3569
+ title: "Add an adjustment layer",
3570
+ description: "Adds a non-destructive adjustment layer on top of the stack (one undoable step) and returns it. Kinds: " + ADJUSTMENT_KINDS.map((k) => k.kind).join(", ") + ". Omitted params are neutral.",
3571
+ inputSchema: {
3572
+ type: "object",
3573
+ properties: {
3574
+ kind: { type: "string", enum: ADJUSTMENT_KINDS.map((k) => k.kind) },
3575
+ params: { type: "object", description: "Initial parameters (see list_controls); merged with neutral defaults." },
3576
+ opacity: { type: "number", minimum: 0, maximum: 1 },
3577
+ blend: { type: "string", enum: BLEND_MODES.map((m) => m.id) },
3578
+ name: { type: "string" }
3579
+ },
3580
+ required: ["kind"],
3581
+ additionalProperties: false
3582
+ },
3583
+ annotations: {
3584
+ title: "Add an adjustment layer",
3585
+ readOnlyHint: false,
3586
+ destructiveHint: false,
3587
+ idempotentHint: false,
3588
+ openWorldHint: false
3589
+ },
3590
+ handler: ({ kind, params, opacity, blend, name }) => {
3591
+ if (!isAdjustmentKind(kind)) throw new Error(`Unknown adjustment kind "${kind}".`);
3592
+ if (blend !== void 0 && !isBlendMode(blend)) throw new Error(`Unknown blend mode "${blend}".`);
3593
+ const id = controller.addAdjustmentLayer({ kind, params, opacity, blend, name });
3594
+ const layer = controller.getAdjustmentLayer(id);
3595
+ return { layer: JSON.parse(JSON.stringify(layer)), index: controller.getAdjustmentLayers().length - 1 };
3596
+ }
3597
+ };
3598
+ const renderPreview = {
3599
+ name: "render_preview",
3600
+ title: "Render a preview",
3601
+ description: "Renders the edited image (crop, adjustments, layers, annotations) as a PNG whose longest side is at most maxSide pixels.",
3602
+ inputSchema: {
3603
+ type: "object",
3604
+ properties: {
3605
+ maxSide: { type: "integer", minimum: 16, maximum: 2048, description: "Longest side in pixels (default 512)." }
3606
+ },
3607
+ additionalProperties: false
3608
+ },
3609
+ annotations: {
3610
+ title: "Render a preview",
3611
+ readOnlyHint: true,
3612
+ destructiveHint: false,
3613
+ idempotentHint: true,
3614
+ openWorldHint: false
3615
+ },
3616
+ handler: async ({ maxSide }) => {
3617
+ const side = Math.max(16, Math.min(2048, Math.round(typeof maxSide === "number" ? maxSide : 512)));
3618
+ const state = controller.getState();
3619
+ const crop = _nullishCoalesce(state.crop, () => ( { x: 0, y: 0, ...state.imageDimensions }));
3620
+ const quarter = Math.abs(Math.round(state.transform.rotate % 180 / 90)) % 2 === 1;
3621
+ const w = quarter ? crop.height : crop.width;
3622
+ const h = quarter ? crop.width : crop.height;
3623
+ const k = Math.min(1, side / Math.max(w, h, 1));
3624
+ const dataUrl = await controller.toDataURL({
3625
+ format: "image/png",
3626
+ width: Math.max(1, Math.round(w * k)),
3627
+ height: Math.max(1, Math.round(h * k))
3628
+ });
3629
+ const comma = dataUrl.indexOf(",");
3630
+ if (!dataUrl.startsWith("data:image/png") || comma < 0 || comma === dataUrl.length - 1) {
3631
+ throw new Error("Rendering is not available in this environment.");
3632
+ }
3633
+ return {
3634
+ content: [
3635
+ { type: "image", data: dataUrl.slice(comma + 1), mimeType: "image/png" },
3636
+ { type: "text", text: `Preview ${Math.round(w * k)}\xD7${Math.round(h * k)}` }
3637
+ ]
3638
+ };
3639
+ }
3640
+ };
3641
+ return {
3642
+ id: _nullishCoalesce(options.id, () => ( "image-editor")),
3643
+ namespace: options.namespace,
3644
+ tools: [getAdjustments, listControls, setAdjustments, addLayer, renderPreview]
3645
+ };
3646
+ }
3647
+
3648
+ // src/image-editor/curve-editing.ts
3649
+ var clampLevel = (v) => Math.min(255, Math.max(0, Math.round(v)));
3650
+ function curvePointToBox(p, width, height) {
3651
+ return { x: p.input / 255 * width, y: height - p.output / 255 * height };
3652
+ }
3653
+ function boxToCurvePoint(x, y, width, height) {
3654
+ return { input: clampLevel(x / (width || 1) * 255), output: clampLevel((height - y) / (height || 1) * 255) };
3655
+ }
3656
+ function curveSvgPath(points, width, height, samples = 64) {
3657
+ const lut = curveLutFromLevels(points);
3658
+ const n = lut.length - 1;
3659
+ let d = "";
3660
+ for (let i = 0; i <= samples; i++) {
3661
+ const t = i / samples;
3662
+ const y = lut[Math.round(t * n)];
3663
+ d += `${i === 0 ? "M" : " L"}${round22(t * width)} ${round22(height - y * height)}`;
3664
+ }
3665
+ return d;
3666
+ }
3667
+ function hitTestCurvePoint(points, x, y, width, height, radius = 8) {
3668
+ let best = -1;
3669
+ let bestD = radius * radius;
3670
+ points.forEach((p, i) => {
3671
+ const b = curvePointToBox(p, width, height);
3672
+ const d = (b.x - x) ** 2 + (b.y - y) ** 2;
3673
+ if (d <= bestD) {
3674
+ best = i;
3675
+ bestD = d;
3676
+ }
3677
+ });
3678
+ return best;
3679
+ }
3680
+ function insertCurvePoint(points, p) {
3681
+ const point = { input: clampLevel(p.input), output: clampLevel(p.output) };
3682
+ const out = points.filter((q) => q.input !== point.input).map((q) => ({ ...q }));
3683
+ let index = out.findIndex((q) => q.input > point.input);
3684
+ if (index === -1) index = out.length;
3685
+ out.splice(index, 0, point);
3686
+ return { points: out, index };
3687
+ }
3688
+ function moveCurvePoint(points, index, p) {
3689
+ const out = points.map((q) => ({ ...q }));
3690
+ if (index < 0 || index >= out.length) return out;
3691
+ const lo = index > 0 ? out[index - 1].input + 1 : 0;
3692
+ const hi = index < out.length - 1 ? out[index + 1].input - 1 : 255;
3693
+ out[index] = { input: Math.min(hi, Math.max(lo, clampLevel(p.input))), output: clampLevel(p.output) };
3694
+ return out;
3695
+ }
3696
+ function removeCurvePoint(points, index) {
3697
+ if (points.length <= 2 || index < 0 || index >= points.length) return points.map((q) => ({ ...q }));
3698
+ return points.filter((_, i) => i !== index).map((q) => ({ ...q }));
3699
+ }
3700
+ var round22 = (v) => Math.round(v * 100) / 100;
3701
+
3702
+
3703
+
3704
+
3705
+
3706
+
3707
+
3708
+
3709
+
3710
+
3711
+
3712
+
3713
+
3714
+
3715
+
3716
+
3717
+
3718
+
3719
+
3720
+
3721
+
3722
+
3723
+
3724
+
3725
+
3726
+
3727
+
3728
+
3729
+
3730
+
3731
+
3732
+
3733
+
3734
+
3735
+
3736
+
3737
+
3738
+
3739
+
3740
+
3741
+
3742
+
3743
+
3744
+
3745
+
3746
+
3747
+
3748
+
3749
+
3750
+
3751
+
3752
+
3753
+
3754
+
3755
+
3756
+
3757
+
3758
+
3759
+
3760
+
3761
+
3762
+
3763
+
3764
+
3765
+
3766
+
3767
+
3768
+
3769
+
3770
+
3771
+
3772
+
3773
+
3774
+
3775
+
3776
+
3777
+
3778
+
3779
+
3780
+
3781
+
3782
+
3783
+
3784
+
3785
+
3786
+
3787
+
3788
+
3789
+
3790
+
3791
+
3792
+
3793
+
3794
+
3795
+
3796
+
3797
+
3798
+
3799
+
3800
+
3801
+
3802
+
3803
+
3804
+
3805
+
3806
+
3807
+
3808
+
3809
+
3810
+
3811
+
1606
3812
 
1607
3813
 
1608
3814
 
@@ -1630,5 +3836,5 @@ function scaleAnnotation(ann, sx, sy) {
1630
3836
 
1631
3837
 
1632
3838
 
1633
- exports.DEFAULT_ADJUSTMENTS = DEFAULT_ADJUSTMENTS; exports.DEFAULT_DEPTH_MASK = DEFAULT_DEPTH_MASK; exports.DEFAULT_TRANSFORM = DEFAULT_TRANSFORM; exports.ImageEditorController = ImageEditorController; exports.ImageEditorRenderer = ImageEditorRenderer; exports.applyAdjustments = applyAdjustments; exports.applyDepthMask = applyDepthMask; exports.applyImageProjectionToContext = applyImageProjectionToContext; exports.calculateCropDrag = calculateCropDrag; exports.canvasToImagePoint = canvasToImagePoint; exports.clampCropToImage = clampCropToImage; exports.getAnnotationBounds = getAnnotationBounds; exports.getCropCursor = getCropCursor; exports.getRotatedSize = getRotatedSize; exports.getViewportMetrics = getViewportMetrics; exports.getViewportProjection = getViewportProjection; exports.globalImageCache = globalImageCache; exports.hitTestAnnotation = hitTestAnnotation; exports.hitTestCrop = hitTestCrop; exports.imageToCanvasPoint = imageToCanvasPoint; exports.isQuarterTurnSwapped = isQuarterTurnSwapped; exports.mapCropHandle = mapCropHandle; exports.normalizeAngle = normalizeAngle; exports.projectImagePoint = projectImagePoint; exports.projectImageRect = projectImageRect; exports.removeAlpha = removeAlpha; exports.unprojectCanvasPoint = unprojectCanvasPoint;
3839
+ exports.ADJUSTMENT_KINDS = ADJUSTMENT_KINDS; exports.ADJUSTMENT_LAYER_CONTROLS = ADJUSTMENT_LAYER_CONTROLS; exports.ADJUSTMENT_LAYER_OPACITY_CONTROL = ADJUSTMENT_LAYER_OPACITY_CONTROL; exports.ADJUSTMENT_LUT_SIZE = ADJUSTMENT_LUT_SIZE; exports.AS_SHOT_KELVIN = AS_SHOT_KELVIN; exports.BLEND_MODES = BLEND_MODES; exports.D65 = D65; exports.DEFAULT_ADJUSTMENTS = DEFAULT_ADJUSTMENTS; exports.DEFAULT_DEPTH_MASK = DEFAULT_DEPTH_MASK; exports.DEFAULT_TRANSFORM = DEFAULT_TRANSFORM; exports.HISTOGRAM_BINS = HISTOGRAM_BINS; exports.HUE_RANGE_LABELS = HUE_RANGE_LABELS; exports.IDENTITY_MATRIX3 = IDENTITY_MATRIX3; exports.IMAGE_ADJUSTMENT_CONTROLS = IMAGE_ADJUSTMENT_CONTROLS; exports.IMAGE_EDITOR_SCHEMA_VERSION = IMAGE_EDITOR_SCHEMA_VERSION; exports.IMAGE_EDITOR_STATE_VERSION = IMAGE_EDITOR_STATE_VERSION; exports.ImageEditorController = ImageEditorController; exports.ImageEditorRenderer = ImageEditorRenderer; exports.LATEST_PROCESS_VERSION = LATEST_PROCESS_VERSION; exports.LEGACY_PROCESS_VERSION = LEGACY_PROCESS_VERSION; exports.LUMA_709 = LUMA_709; exports.MAX_KELVIN = MAX_KELVIN; exports.MIN_KELVIN = MIN_KELVIN; exports.MIRED_PER_TEMPERATURE_UNIT = MIRED_PER_TEMPERATURE_UNIT; exports.TINT_SCALE = TINT_SCALE; exports.TONE_LUT_SIZE = TONE_LUT_SIZE; exports.VIBRANCE_GREY = VIBRANCE_GREY; exports.activeAdjustmentLayers = activeAdjustmentLayers; exports.adjustmentKindLabel = adjustmentKindLabel; exports.adjustmentLayersKey = adjustmentLayersKey; exports.applyAdjustmentLayers = applyAdjustmentLayers; exports.applyAdjustments = applyAdjustments; exports.applyDepthMask = applyDepthMask; exports.applyImageProjectionToContext = applyImageProjectionToContext; exports.applyLegacyAdjustments = applyLegacyAdjustments; exports.applyMatrix3 = applyMatrix3; exports.applyProcess2Adjustments = applyProcess2Adjustments; exports.blackWhiteGray = blackWhiteGray; exports.blendChannel = blendChannel; exports.blendImageData = blendImageData; exports.blendLum = blendLum; exports.blendPixel = blendPixel; exports.blendRgb = blendRgb; exports.boxToCurvePoint = boxToCurvePoint; exports.bradfordAdaptation = bradfordAdaptation; exports.buildToneLut = buildToneLut; exports.calculateCropDrag = calculateCropDrag; exports.canvasCompositeOperation = canvasCompositeOperation; exports.canvasToImagePoint = canvasToImagePoint; exports.clampControlValue = clampControlValue; exports.clampCropToImage = clampCropToImage; exports.colorBalanceChannel = colorBalanceChannel; exports.compileAdjustmentLayer = compileAdjustmentLayer; exports.computeHistogram = computeHistogram; exports.controlPosition = controlPosition; exports.controlTrackBackground = controlTrackBackground; exports.createAdjustmentLayer = createAdjustmentLayer; exports.createImageEditorToolProvider = createImageEditorToolProvider; exports.curveLut = curveLut; exports.curveLutFromLevels = curveLutFromLevels; exports.curvePointToBox = curvePointToBox; exports.curveSvgPath = curveSvgPath; exports.defaultAdjustmentParams = defaultAdjustmentParams; exports.evaluateAdjustmentLayers = evaluateAdjustmentLayers; exports.findAdjustmentControl = findAdjustmentControl; exports.findLayerControl = findLayerControl; exports.formatControlValue = formatControlValue; exports.getAnnotationBounds = getAnnotationBounds; exports.getControlPath = getControlPath; exports.getCropCursor = getCropCursor; exports.getRotatedSize = getRotatedSize; exports.getViewportMetrics = getViewportMetrics; exports.getViewportProjection = getViewportProjection; exports.globalImageCache = globalImageCache; exports.hexToRgb = hexToRgb; exports.histogramPath = histogramPath; exports.hitTestAnnotation = hitTestAnnotation; exports.hitTestCrop = hitTestCrop; exports.hitTestCurvePoint = hitTestCurvePoint; exports.hslToRgb = hslToRgb; exports.hueRangeTables = hueRangeTables; exports.hueRangeWeight = hueRangeWeight; exports.hueSaturation = hueSaturation; exports.identityCurve = identityCurve; exports.identityLevels = identityLevels; exports.imageEditorStateMigrations = imageEditorStateMigrations; exports.imageToCanvasPoint = imageToCanvasPoint; exports.insertCurvePoint = insertCurvePoint; exports.invertMatrix3 = invertMatrix3; exports.isAdjustmentKind = isAdjustmentKind; exports.isBlendMode = isBlendMode; exports.isQuarterTurnSwapped = isQuarterTurnSwapped; exports.isSeparableBlendMode = isSeparableBlendMode; exports.kelvinToTemperature = kelvinToTemperature; exports.levelsValue = levelsValue; exports.linearSrgbToXy = linearSrgbToXy; exports.linearToSrgb = linearToSrgb; exports.loadImageEditorDocument = loadImageEditorDocument; exports.mapCropHandle = mapCropHandle; exports.migrateSerializedImageEditorState = migrateSerializedImageEditorState; exports.moveCurvePoint = moveCurvePoint; exports.mulMatrix3 = mulMatrix3; exports.neutralHueRange = neutralHueRange; exports.normalizeAdjustmentLayer = normalizeAdjustmentLayer; exports.normalizeAdjustmentLayers = normalizeAdjustmentLayers; exports.normalizeAngle = normalizeAngle; exports.normalizeBlendMode = normalizeBlendMode; exports.normalizeProcessVersion = normalizeProcessVersion; exports.normalizeStoredAdjustments = normalizeStoredAdjustments; exports.photoFilterMatrix = photoFilterMatrix; exports.posterizeValue = posterizeValue; exports.projectImagePoint = projectImagePoint; exports.projectImageRect = projectImageRect; exports.rangeLightness = rangeLightness; exports.readAdjustmentControls = readAdjustmentControls; exports.removeAlpha = removeAlpha; exports.removeCurvePoint = removeCurvePoint; exports.rgbToGray = rgbToGray; exports.rgbToHsl = rgbToHsl; exports.sampleLut = sampleLut; exports.sampleToneLut = sampleToneLut; exports.setControlPath = setControlPath; exports.setLum = setLum; exports.srgbToLinear = srgbToLinear; exports.tempTintToXy = tempTintToXy; exports.temperatureToKelvin = temperatureToKelvin; exports.toneCurve = toneCurve; exports.toneLuts = toneLuts; exports.unprojectCanvasPoint = unprojectCanvasPoint; exports.uvToXy = uvToXy; exports.vibranceLinear = vibranceLinear; exports.vibrancePixel = vibrancePixel; exports.wbMatrix = wbMatrix; exports.whiteBalanceFromSample = whiteBalanceFromSample; exports.whiteBalanceMatrix = whiteBalanceMatrix; exports.xyToTempTint = xyToTempTint; exports.xyToUv = xyToUv;
1634
3840
  //# sourceMappingURL=index.cjs.map