@pixerate/flair 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +254 -2
  4. package/dist/catalog/index.d.ts +74 -0
  5. package/dist/catalog/index.js +47 -0
  6. package/dist/catalog/index.js.map +1 -0
  7. package/dist/chunk-36DTNSHU.js +160 -0
  8. package/dist/chunk-36DTNSHU.js.map +1 -0
  9. package/dist/chunk-3FAUZOGL.js +331 -0
  10. package/dist/chunk-3FAUZOGL.js.map +1 -0
  11. package/dist/chunk-CFCLT2XL.js +1 -0
  12. package/dist/chunk-CFCLT2XL.js.map +1 -0
  13. package/dist/chunk-EHDJ3CPJ.js +327 -0
  14. package/dist/chunk-EHDJ3CPJ.js.map +1 -0
  15. package/dist/chunk-G5FNZDTA.js +536 -0
  16. package/dist/chunk-G5FNZDTA.js.map +1 -0
  17. package/dist/chunk-HKXOHRCD.js +763 -0
  18. package/dist/chunk-HKXOHRCD.js.map +1 -0
  19. package/dist/chunk-J55UBNAT.js +164 -0
  20. package/dist/chunk-J55UBNAT.js.map +1 -0
  21. package/dist/chunk-JWZCPMFG.js +2074 -0
  22. package/dist/chunk-JWZCPMFG.js.map +1 -0
  23. package/dist/chunk-MWVTQFV5.js +1894 -0
  24. package/dist/chunk-MWVTQFV5.js.map +1 -0
  25. package/dist/chunk-UQQNZSYL.js +594 -0
  26. package/dist/chunk-UQQNZSYL.js.map +1 -0
  27. package/dist/chunk-WMZQCUFJ.js +73 -0
  28. package/dist/chunk-WMZQCUFJ.js.map +1 -0
  29. package/dist/cli.d.ts +1 -0
  30. package/dist/cli.js +331 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/clock-BhdXWH-n.d.ts +47 -0
  33. package/dist/controls-DdXMbovi.d.ts +97 -0
  34. package/dist/editor/index.d.ts +413 -0
  35. package/dist/editor/index.js +1009 -0
  36. package/dist/editor/index.js.map +1 -0
  37. package/dist/export-FEBPEEO7.js +22 -0
  38. package/dist/export-FEBPEEO7.js.map +1 -0
  39. package/dist/index.d.ts +1431 -0
  40. package/dist/index.js +1263 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/motion-D0TVv2CF.d.ts +148 -0
  43. package/dist/node.d.ts +24 -0
  44. package/dist/node.js +10 -0
  45. package/dist/node.js.map +1 -0
  46. package/dist/recorder-WJTWMEQN.js +10 -0
  47. package/dist/recorder-WJTWMEQN.js.map +1 -0
  48. package/dist/renderer-Oyynz-fs.d.ts +293 -0
  49. package/dist/types-CkKkavvB.d.ts +71 -0
  50. package/dist/vector/index.d.ts +83 -0
  51. package/dist/vector/index.js +39 -0
  52. package/dist/vector/index.js.map +1 -0
  53. package/package.json +96 -4
@@ -0,0 +1,763 @@
1
+ import {
2
+ controlValue
3
+ } from "./chunk-J55UBNAT.js";
4
+
5
+ // src/shader/textures.ts
6
+ var ImageLoadError = class extends Error {
7
+ url;
8
+ constructor(url, cause) {
9
+ super(
10
+ `Could not load the image at ${shortUrl(url)}. If it is on another site, that site must allow cross-origin use (CORS).`,
11
+ { cause }
12
+ );
13
+ this.name = "ImageLoadError";
14
+ this.url = url;
15
+ }
16
+ };
17
+ function shortUrl(url) {
18
+ if (url.startsWith("data:")) return `${url.slice(0, Math.min(url.indexOf(",") + 1 || 40, 40))}\u2026`;
19
+ return url.length > 120 ? `${url.slice(0, 119)}\u2026` : url;
20
+ }
21
+ var SVG_EDGE = 2048;
22
+ var SVG_TYPE = "image/svg+xml";
23
+ function isSvgUrl(url) {
24
+ return /^data:image\/svg\+xml[;,]/i.test(url) || /^(?!data:)[^?#]*\.svg([?#]|$)/i.test(url);
25
+ }
26
+ function svgLength(value) {
27
+ const match = value?.trim().match(/^([\d.]+)(px)?$/);
28
+ const n = match ? Number(match[1]) : NaN;
29
+ return n > 0 ? n : void 0;
30
+ }
31
+ function sizeSvg(markup, edge) {
32
+ const doc = new DOMParser().parseFromString(markup, SVG_TYPE);
33
+ const svg = doc.documentElement;
34
+ if (svg.localName !== "svg" || doc.getElementsByTagName("parsererror").length) {
35
+ throw new Error("Not an SVG document.");
36
+ }
37
+ const box = (svg.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
38
+ const hasBox = box.length === 4 && box[2] > 0 && box[3] > 0;
39
+ const width = svgLength(svg.getAttribute("width")) ?? (hasBox ? box[2] : 300);
40
+ const height = svgLength(svg.getAttribute("height")) ?? (hasBox ? box[3] : 150);
41
+ if (!hasBox) svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
42
+ const scale = edge / Math.max(width, height);
43
+ svg.setAttribute("width", String(Math.max(1, Math.round(width * scale))));
44
+ svg.setAttribute("height", String(Math.max(1, Math.round(height * scale))));
45
+ return new XMLSerializer().serializeToString(svg);
46
+ }
47
+ async function loadImage(url) {
48
+ if (isSvgUrl(url)) return loadSvg(url);
49
+ return decode(url, url, !/^(data|blob):/i.test(url));
50
+ }
51
+ async function loadSvg(url) {
52
+ let markup;
53
+ try {
54
+ const response = await fetch(url);
55
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
56
+ markup = sizeSvg(await response.text(), SVG_EDGE);
57
+ } catch (e) {
58
+ throw new ImageLoadError(url, e);
59
+ }
60
+ const sized = URL.createObjectURL(new Blob([markup], { type: SVG_TYPE }));
61
+ try {
62
+ return await decode(sized, url, false);
63
+ } finally {
64
+ URL.revokeObjectURL(sized);
65
+ }
66
+ }
67
+ async function decode(src, url, cors) {
68
+ const image = new Image();
69
+ if (cors) image.crossOrigin = "anonymous";
70
+ image.src = src;
71
+ try {
72
+ await image.decode();
73
+ } catch (e) {
74
+ throw new ImageLoadError(url, e);
75
+ }
76
+ return image;
77
+ }
78
+ var preloaded = /* @__PURE__ */ new Map();
79
+ var decoded = /* @__PURE__ */ new Map();
80
+ async function preloadImages(urls) {
81
+ const loads = [...new Set(urls)].filter(Boolean).map((url) => {
82
+ let load = preloaded.get(url);
83
+ if (!load) {
84
+ load = loadImage(url).then(
85
+ (image) => {
86
+ if (preloaded.get(url) === load) decoded.set(url, image);
87
+ return image;
88
+ },
89
+ (e) => {
90
+ if (preloaded.get(url) === load) preloaded.delete(url);
91
+ throw e;
92
+ }
93
+ );
94
+ preloaded.set(url, load);
95
+ }
96
+ return load;
97
+ });
98
+ const results = await Promise.allSettled(loads);
99
+ const failed = results.find((r) => r.status === "rejected");
100
+ if (failed) throw failed.reason;
101
+ }
102
+ function releaseImages(urls) {
103
+ for (const url of urls ?? [...preloaded.keys()]) {
104
+ preloaded.delete(url);
105
+ decoded.delete(url);
106
+ }
107
+ }
108
+ var TextureCache = class {
109
+ gl;
110
+ entries = /* @__PURE__ */ new Map();
111
+ onLoad;
112
+ load;
113
+ placeholder = null;
114
+ disposed = false;
115
+ constructor(gl, onLoad = () => {
116
+ }, load = loadImage) {
117
+ this.gl = gl;
118
+ this.onLoad = onLoad;
119
+ this.load = load;
120
+ }
121
+ texture(url) {
122
+ return url && this.entry(url).texture || this.blank();
123
+ }
124
+ /** Starts loading `url` if it is new. */
125
+ request(url) {
126
+ if (url) this.entry(url);
127
+ }
128
+ entry(url) {
129
+ const existing = this.entries.get(url);
130
+ if (existing) return existing;
131
+ const created = { texture: null, loaded: Promise.resolve(), error: null };
132
+ const ready = decoded.get(url);
133
+ if (ready && !this.gl.isContextLost()) {
134
+ created.texture = this.upload(ready);
135
+ this.entries.set(url, created);
136
+ return created;
137
+ }
138
+ created.loaded = this.load(url).then(
139
+ (image) => {
140
+ if (this.entries.get(url) !== created || this.disposed || this.gl.isContextLost()) return;
141
+ created.texture = this.upload(image);
142
+ this.onLoad(url, null);
143
+ },
144
+ (e) => {
145
+ if (this.entries.get(url) !== created || this.disposed) return;
146
+ created.error = e instanceof ImageLoadError ? e : new ImageLoadError(url, e);
147
+ this.onLoad(url, created.error);
148
+ }
149
+ );
150
+ this.entries.set(url, created);
151
+ return created;
152
+ }
153
+ /** The error for `url`, if it failed to load. */
154
+ error(url) {
155
+ return this.entries.get(url)?.error ?? null;
156
+ }
157
+ /**
158
+ * Settles once every one of `urls` has loaded or failed (starting any that
159
+ * are new). Rejects with the first {@link ImageLoadError}.
160
+ */
161
+ async ready(urls) {
162
+ const entries = [...urls].filter(Boolean).map((url) => this.entry(url));
163
+ await Promise.all(entries.map((e) => e.loaded));
164
+ const failed = entries.find((e) => e.error);
165
+ if (failed) throw failed.error;
166
+ }
167
+ /** Deletes the textures of every URL not in `urls`; a later request loads them again. */
168
+ retain(urls) {
169
+ const keep = new Set(urls);
170
+ for (const [url, entry] of this.entries) {
171
+ if (keep.has(url)) continue;
172
+ if (entry.texture) this.gl.deleteTexture(entry.texture);
173
+ this.entries.delete(url);
174
+ }
175
+ }
176
+ /** Deletes every texture; loads still in flight are dropped. */
177
+ dispose() {
178
+ this.retain([]);
179
+ if (this.placeholder) this.gl.deleteTexture(this.placeholder);
180
+ this.placeholder = null;
181
+ this.disposed = true;
182
+ }
183
+ blank() {
184
+ if (this.placeholder) return this.placeholder;
185
+ const gl = this.gl;
186
+ this.placeholder = gl.createTexture();
187
+ gl.bindTexture(gl.TEXTURE_2D, this.placeholder);
188
+ const black = new Uint8Array([0, 0, 0, 255]);
189
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, black);
190
+ return this.placeholder;
191
+ }
192
+ upload(image) {
193
+ const gl = this.gl;
194
+ const texture = gl.createTexture();
195
+ gl.bindTexture(gl.TEXTURE_2D, texture);
196
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
197
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
198
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
199
+ gl.generateMipmap(gl.TEXTURE_2D);
200
+ return texture;
201
+ }
202
+ };
203
+ function applyTextureSettings(gl, settings) {
204
+ const min = { mipmap: gl.LINEAR_MIPMAP_LINEAR, linear: gl.LINEAR, nearest: gl.NEAREST }[settings.filter];
205
+ const wrap = { repeat: gl.REPEAT, clamp: gl.CLAMP_TO_EDGE, mirror: gl.MIRRORED_REPEAT }[settings.wrap];
206
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, min);
207
+ gl.texParameteri(
208
+ gl.TEXTURE_2D,
209
+ gl.TEXTURE_MAG_FILTER,
210
+ settings.filter === "nearest" ? gl.NEAREST : gl.LINEAR
211
+ );
212
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
213
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
214
+ }
215
+
216
+ // src/shader/program.ts
217
+ var BUILTIN_UNIFORMS = [
218
+ { name: "iResolution", type: "vec3", doc: "viewport size in pixels (z = 1)" },
219
+ { name: "iTime", type: "float", doc: "seconds since the shader started" },
220
+ { name: "iTimeDelta", type: "float", doc: "seconds since the previous frame" },
221
+ { name: "iFrame", type: "int", doc: "frame number" },
222
+ {
223
+ name: "iMouse",
224
+ type: "vec4",
225
+ doc: "xy = pointer while pressed (pixels), zw = press position (negative once released)"
226
+ }
227
+ ];
228
+ var PRELUDE = `#version 300 es
229
+ precision highp float;
230
+ precision highp int;
231
+ uniform vec3 iResolution;
232
+ uniform float iTime;
233
+ uniform float iTimeDelta;
234
+ uniform int iFrame;
235
+ uniform vec4 iMouse;
236
+ out vec4 flair_FragColor;
237
+ `;
238
+ var EPILOGUE = `
239
+ void main() {
240
+ vec4 flair_color = vec4(0.0, 0.0, 0.0, 1.0);
241
+ mainImage(flair_color, gl_FragCoord.xy);
242
+ flair_FragColor = vec4(flair_color.rgb, 1.0);
243
+ }
244
+ `;
245
+ var VERTEX_SOURCE = `#version 300 es
246
+ void main() {
247
+ vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
248
+ gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
249
+ }
250
+ `;
251
+ var ShaderCompileError = class extends Error {
252
+ errors;
253
+ constructor(errors) {
254
+ super(formatCompileError(errors));
255
+ this.name = "ShaderCompileError";
256
+ this.errors = errors;
257
+ }
258
+ };
259
+ function formatCompileError(errors) {
260
+ const first = errors[0];
261
+ return `The shader does not compile${first?.line ? ` (line ${first.line})` : ""}: ${first?.message ?? "unknown error"}`;
262
+ }
263
+ function wrapSource(source) {
264
+ return `${PRELUDE}#line 1
265
+ ${source.replace(/^\s*#version[^\n]*/m, "")}
266
+ ${EPILOGUE}`;
267
+ }
268
+ function parseCompileLog(log, lineCount) {
269
+ const errors = [];
270
+ for (const raw of log.split("\n")) {
271
+ const text = raw.trim();
272
+ if (!text) continue;
273
+ const m = /^(?:ERROR|WARNING):\s*\d+:(\d+):\s*(.*)$/.exec(text);
274
+ if (m) {
275
+ const line = Number(m[1]);
276
+ errors.push({ line: line >= 1 && line <= lineCount ? line : 0, message: m[2].trim() });
277
+ } else if (!/^\W*$/.test(text)) {
278
+ errors.push({ line: 0, message: text.replace(/^ERROR:\s*/, "") });
279
+ }
280
+ }
281
+ if (!errors.length) errors.push({ line: 0, message: "The shader did not compile." });
282
+ const seen = /* @__PURE__ */ new Set();
283
+ return errors.filter((e) => {
284
+ const key = `${e.line}:${e.message}`;
285
+ if (seen.has(key)) return false;
286
+ seen.add(key);
287
+ return true;
288
+ });
289
+ }
290
+ var vertexCache = /* @__PURE__ */ new WeakMap();
291
+ function vertexShader(gl) {
292
+ let vs = vertexCache.get(gl);
293
+ if (!vs) {
294
+ vs = gl.createShader(gl.VERTEX_SHADER);
295
+ gl.shaderSource(vs, VERTEX_SOURCE);
296
+ gl.compileShader(vs);
297
+ vertexCache.set(gl, vs);
298
+ }
299
+ return vs;
300
+ }
301
+ function resetGlCaches() {
302
+ vertexCache = /* @__PURE__ */ new WeakMap();
303
+ }
304
+ function compileShader(gl, source, controls) {
305
+ const lineCount = source.split("\n").length;
306
+ if (gl.isContextLost()) {
307
+ return {
308
+ ok: false,
309
+ errors: [
310
+ {
311
+ line: 0,
312
+ message: "The graphics context was lost; reload the page if the preview does not come back."
313
+ }
314
+ ]
315
+ };
316
+ }
317
+ if (!/\bmainImage\s*\(/.test(source)) {
318
+ return {
319
+ ok: false,
320
+ errors: [
321
+ { line: 0, message: "Define void mainImage(out vec4 fragColor, in vec2 fragCoord)." }
322
+ ]
323
+ };
324
+ }
325
+ const fs = gl.createShader(gl.FRAGMENT_SHADER);
326
+ if (!fs) {
327
+ return {
328
+ ok: false,
329
+ errors: [{ line: 0, message: "WebGL could not create a shader (the context may be lost)." }]
330
+ };
331
+ }
332
+ gl.shaderSource(fs, wrapSource(source));
333
+ gl.compileShader(fs);
334
+ if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
335
+ const errors = parseCompileLog(gl.getShaderInfoLog(fs) ?? "", lineCount);
336
+ gl.deleteShader(fs);
337
+ return { ok: false, errors };
338
+ }
339
+ const program = gl.createProgram();
340
+ gl.attachShader(program, vertexShader(gl));
341
+ gl.attachShader(program, fs);
342
+ gl.linkProgram(program);
343
+ gl.deleteShader(fs);
344
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
345
+ const errors = parseCompileLog(gl.getProgramInfoLog(program) ?? "", lineCount);
346
+ gl.deleteProgram(program);
347
+ return { ok: false, errors };
348
+ }
349
+ const builtins = {};
350
+ for (const u of BUILTIN_UNIFORMS) builtins[u.name] = gl.getUniformLocation(program, u.name);
351
+ const uniforms = new Map(
352
+ controls.map((c) => [c.name, gl.getUniformLocation(program, c.name)])
353
+ );
354
+ return { ok: true, shader: { program, builtins, uniforms } };
355
+ }
356
+ function drawFrame(gl, shader, controls, values, state, textures) {
357
+ gl.viewport(0, 0, state.width, state.height);
358
+ gl.useProgram(shader.program);
359
+ const b = shader.builtins;
360
+ if (b.iResolution) gl.uniform3f(b.iResolution, state.width, state.height, 1);
361
+ if (b.iTime) gl.uniform1f(b.iTime, state.time);
362
+ if (b.iTimeDelta) gl.uniform1f(b.iTimeDelta, state.delta);
363
+ if (b.iFrame) gl.uniform1i(b.iFrame, state.frame);
364
+ if (b.iMouse) gl.uniform4f(b.iMouse, ...state.mouse);
365
+ let unit = 0;
366
+ for (const control of controls) {
367
+ const loc = shader.uniforms.get(control.name);
368
+ if (!loc) continue;
369
+ const v = controlValue(control, values);
370
+ switch (control.type) {
371
+ case "float":
372
+ gl.uniform1f(loc, v);
373
+ break;
374
+ case "int":
375
+ gl.uniform1i(loc, Math.round(v));
376
+ break;
377
+ case "bool":
378
+ gl.uniform1i(loc, v ? 1 : 0);
379
+ break;
380
+ case "vec2":
381
+ gl.uniform2fv(loc, v);
382
+ break;
383
+ case "vec3":
384
+ gl.uniform3fv(loc, v);
385
+ break;
386
+ case "vec4":
387
+ gl.uniform4fv(loc, v);
388
+ break;
389
+ case "sampler2D": {
390
+ gl.activeTexture(gl.TEXTURE0 + unit);
391
+ const texture = textures?.texture(v) ?? null;
392
+ gl.bindTexture(gl.TEXTURE_2D, texture);
393
+ if (texture && control.texture) applyTextureSettings(gl, control.texture);
394
+ gl.uniform1i(loc, unit++);
395
+ break;
396
+ }
397
+ }
398
+ }
399
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
400
+ }
401
+ function createContext(canvas, preserveDrawingBuffer = false) {
402
+ return canvas.getContext("webgl2", {
403
+ alpha: false,
404
+ antialias: false,
405
+ depth: false,
406
+ stencil: false,
407
+ premultipliedAlpha: false,
408
+ preserveDrawingBuffer,
409
+ powerPreference: "high-performance"
410
+ });
411
+ }
412
+
413
+ // src/shader/color.ts
414
+ function srgbToLinear(c) {
415
+ const v = Math.max(0, c);
416
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
417
+ }
418
+ function linearToSrgb(c) {
419
+ const v = Math.max(0, c);
420
+ return v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
421
+ }
422
+ function srgbToLinearRgb(rgb) {
423
+ return [srgbToLinear(rgb[0]), srgbToLinear(rgb[1]), srgbToLinear(rgb[2])];
424
+ }
425
+ function linearRgbToSrgb(rgb) {
426
+ return [linearToSrgb(rgb[0]), linearToSrgb(rgb[1]), linearToSrgb(rgb[2])];
427
+ }
428
+ function linearSrgbToOklab(rgb) {
429
+ const [r, g, b] = rgb;
430
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
431
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
432
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
433
+ const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
434
+ const a = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
435
+ const bVal = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
436
+ return [L, a, bVal];
437
+ }
438
+ function oklabToLinearSrgb(lab) {
439
+ const [L, a, b] = lab;
440
+ const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
441
+ const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
442
+ const s_ = L - 0.0894841775 * a - 1.291485548 * b;
443
+ const l = l_ * l_ * l_;
444
+ const m = m_ * m_ * m_;
445
+ const s = s_ * s_ * s_;
446
+ const r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
447
+ const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
448
+ const bVal = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
449
+ return [r, g, bVal];
450
+ }
451
+ function srgbToOklab(rgb) {
452
+ return linearSrgbToOklab(srgbToLinearRgb(rgb));
453
+ }
454
+ function oklabToSrgb(lab) {
455
+ const linear = oklabToLinearSrgb(lab);
456
+ return linearRgbToSrgb(linear);
457
+ }
458
+ function oklabToOklch(lab) {
459
+ const [L, a, b] = lab;
460
+ const C = Math.sqrt(a * a + b * b);
461
+ let h = Math.atan2(b, a);
462
+ if (h < 0) h += 2 * Math.PI;
463
+ return [L, C, h];
464
+ }
465
+ function oklchToOklab(lch) {
466
+ const [L, C, h] = lch;
467
+ return [L, C * Math.cos(h), C * Math.sin(h)];
468
+ }
469
+ function interpolateOklab(c1, c2, t) {
470
+ const lab1 = srgbToOklab(c1);
471
+ const lab2 = srgbToOklab(c2);
472
+ const clampedT = Math.max(0, Math.min(1, t));
473
+ const lab = [
474
+ lab1[0] + (lab2[0] - lab1[0]) * clampedT,
475
+ lab1[1] + (lab2[1] - lab1[1]) * clampedT,
476
+ lab1[2] + (lab2[2] - lab1[2]) * clampedT
477
+ ];
478
+ const res = oklabToSrgb(lab);
479
+ return [
480
+ Math.max(0, Math.min(1, res[0])),
481
+ Math.max(0, Math.min(1, res[1])),
482
+ Math.max(0, Math.min(1, res[2]))
483
+ ];
484
+ }
485
+ function interpolateOklch(c1, c2, t) {
486
+ const lch1 = oklabToOklch(srgbToOklab(c1));
487
+ const lch2 = oklabToOklch(srgbToOklab(c2));
488
+ const clampedT = Math.max(0, Math.min(1, t));
489
+ const L = lch1[0] + (lch2[0] - lch1[0]) * clampedT;
490
+ const C = lch1[1] + (lch2[1] - lch1[1]) * clampedT;
491
+ let dHue = lch2[2] - lch1[2];
492
+ if (dHue > Math.PI) dHue -= 2 * Math.PI;
493
+ if (dHue < -Math.PI) dHue += 2 * Math.PI;
494
+ let h = lch1[2] + dHue * clampedT;
495
+ if (h < 0) h += 2 * Math.PI;
496
+ if (h >= 2 * Math.PI) h -= 2 * Math.PI;
497
+ const res = oklabToSrgb(oklchToOklab([L, C, h]));
498
+ return [
499
+ Math.max(0, Math.min(1, res[0])),
500
+ Math.max(0, Math.min(1, res[1])),
501
+ Math.max(0, Math.min(1, res[2]))
502
+ ];
503
+ }
504
+ function mulMat3(m, v) {
505
+ return [
506
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
507
+ m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
508
+ m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
509
+ ];
510
+ }
511
+ var MAT_LINEAR_SRGB_TO_P3 = [
512
+ 0.822462,
513
+ 0.177538,
514
+ 0,
515
+ 0.033194,
516
+ 0.966806,
517
+ 0,
518
+ 0.017083,
519
+ 0.072397,
520
+ 0.91052
521
+ ];
522
+ var MAT_P3_TO_LINEAR_SRGB = [
523
+ 1.22494,
524
+ -0.22494,
525
+ 0,
526
+ -0.042057,
527
+ 1.042057,
528
+ 0,
529
+ -0.019638,
530
+ -0.078636,
531
+ 1.098274
532
+ ];
533
+ var MAT_LINEAR_SRGB_TO_ACESCG = [
534
+ 0.613097,
535
+ 0.339523,
536
+ 0.047379,
537
+ 0.070194,
538
+ 0.916354,
539
+ 0.013452,
540
+ 0.020616,
541
+ 0.10957,
542
+ 0.869815
543
+ ];
544
+ var MAT_ACESCG_TO_LINEAR_SRGB = [
545
+ 1.704859,
546
+ -0.621714,
547
+ -0.083299,
548
+ -0.130078,
549
+ 1.140735,
550
+ -0.01056,
551
+ -0.023964,
552
+ -0.128975,
553
+ 1.153014
554
+ ];
555
+ function convertColorSpace(color, from, to) {
556
+ if (from === to) return [...color];
557
+ let lin;
558
+ switch (from) {
559
+ case "srgb":
560
+ lin = srgbToLinearRgb(color);
561
+ break;
562
+ case "linear-srgb":
563
+ lin = [...color];
564
+ break;
565
+ case "rec709":
566
+ lin = [
567
+ Math.pow(Math.max(0, color[0]), 2.4),
568
+ Math.pow(Math.max(0, color[1]), 2.4),
569
+ Math.pow(Math.max(0, color[2]), 2.4)
570
+ ];
571
+ break;
572
+ case "display-p3": {
573
+ const p3Lin = srgbToLinearRgb(color);
574
+ lin = mulMat3(MAT_P3_TO_LINEAR_SRGB, p3Lin);
575
+ break;
576
+ }
577
+ case "acescg":
578
+ lin = mulMat3(MAT_ACESCG_TO_LINEAR_SRGB, color);
579
+ break;
580
+ }
581
+ switch (to) {
582
+ case "linear-srgb":
583
+ return lin;
584
+ case "srgb":
585
+ return linearRgbToSrgb(lin);
586
+ case "rec709":
587
+ return [
588
+ Math.pow(Math.max(0, lin[0]), 1 / 2.4),
589
+ Math.pow(Math.max(0, lin[1]), 1 / 2.4),
590
+ Math.pow(Math.max(0, lin[2]), 1 / 2.4)
591
+ ];
592
+ case "display-p3": {
593
+ const p3Lin = mulMat3(MAT_LINEAR_SRGB_TO_P3, lin);
594
+ return linearRgbToSrgb(p3Lin);
595
+ }
596
+ case "acescg":
597
+ return mulMat3(MAT_LINEAR_SRGB_TO_ACESCG, lin);
598
+ }
599
+ }
600
+ function applyAscCdl(color, cdl) {
601
+ const slope = cdl.slope ?? [1, 1, 1];
602
+ const offset = cdl.offset ?? [0, 0, 0];
603
+ const power = cdl.power ?? [1, 1, 1];
604
+ const sat = cdl.saturation ?? 1;
605
+ let r = Math.pow(Math.max(0, color[0] * slope[0] + offset[0]), power[0]);
606
+ let g = Math.pow(Math.max(0, color[1] * slope[1] + offset[1]), power[1]);
607
+ let b = Math.pow(Math.max(0, color[2] * slope[2] + offset[2]), power[2]);
608
+ if (sat !== 1) {
609
+ const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
610
+ r = luma + sat * (r - luma);
611
+ g = luma + sat * (g - luma);
612
+ b = luma + sat * (b - luma);
613
+ }
614
+ return [Math.max(0, r), Math.max(0, g), Math.max(0, b)];
615
+ }
616
+ function applyLiftGammaGain(color, lgg) {
617
+ const lift = lgg.lift ?? [0, 0, 0];
618
+ const gamma = lgg.gamma ?? [1, 1, 1];
619
+ const gain = lgg.gain ?? [1, 1, 1];
620
+ const processChannel = (val, l, gm, gn) => {
621
+ const lifted = val + l * (1 - val);
622
+ const gained = Math.max(0, lifted * gn);
623
+ const safeGamma = Math.max(1e-4, gm);
624
+ return Math.pow(gained, 1 / safeGamma);
625
+ };
626
+ return [
627
+ processChannel(color[0], lift[0], gamma[0], gain[0]),
628
+ processChannel(color[1], lift[1], gamma[1], gain[1]),
629
+ processChannel(color[2], lift[2], gamma[2], gain[2])
630
+ ];
631
+ }
632
+ function applyVibrance(color, amount) {
633
+ if (Math.abs(amount) < 1e-4) return [...color];
634
+ const [r, g, b] = color;
635
+ const maxC = Math.max(r, g, b);
636
+ const minC = Math.min(r, g, b);
637
+ const sat = maxC > 1e-5 ? (maxC - minC) / maxC : 0;
638
+ let skinProtection = 1;
639
+ if (r > g && g > b) {
640
+ const skinFactor = (g - b) / (r - b + 1e-5);
641
+ skinProtection = Math.max(0.2, 1 - skinFactor * 0.5);
642
+ }
643
+ const boost = amount * (1 - sat) * skinProtection;
644
+ const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
645
+ const newR = Math.max(0, Math.min(1, luma + (1 + boost) * (r - luma)));
646
+ const newG = Math.max(0, Math.min(1, luma + (1 + boost) * (g - luma)));
647
+ const newB = Math.max(0, Math.min(1, luma + (1 + boost) * (b - luma)));
648
+ return [newR, newG, newB];
649
+ }
650
+ function getWhiteBalanceGains(kelvin, tint = 0) {
651
+ const k = Math.max(2e3, Math.min(12e3, kelvin)) / 100;
652
+ let r;
653
+ let g;
654
+ let b;
655
+ if (k <= 66) {
656
+ r = 255;
657
+ g = 99.4708025861 * Math.log(k) - 161.1195681661;
658
+ b = k <= 19 ? 0 : 138.5177312231 * Math.log(k - 10) - 305.0447927307;
659
+ } else {
660
+ r = 329.698727446 * Math.pow(k - 60, -0.1332047592);
661
+ g = 288.1221695283 * Math.pow(k - 60, -0.0755148492);
662
+ b = 255;
663
+ }
664
+ const d65R = 255;
665
+ const d65G = 250.7;
666
+ const d65B = 255;
667
+ let gainR = r / d65R;
668
+ let gainG = g / d65G;
669
+ let gainB = b / d65B;
670
+ const tintFactor = tint / 100;
671
+ gainG *= 1 - tintFactor * 0.15;
672
+ gainR *= 1 + tintFactor * 0.05;
673
+ gainB *= 1 + tintFactor * 0.05;
674
+ return [gainR, gainG, gainB];
675
+ }
676
+ function getOklabGLSL() {
677
+ return `
678
+ // Oklab <-> Linear sRGB (Bj\xF6rn Ottosson)
679
+ vec3 flair_linear_to_oklab(vec3 c) {
680
+ float l = pow(0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b, 1.0 / 3.0);
681
+ float m = pow(0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b, 1.0 / 3.0);
682
+ float s = pow(0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b, 1.0 / 3.0);
683
+ return vec3(
684
+ 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
685
+ 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
686
+ 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s
687
+ );
688
+ }
689
+
690
+ vec3 flair_oklab_to_linear(vec3 lab) {
691
+ float l_ = lab.x + 0.3963377774 * lab.y + 0.2158037573 * lab.z;
692
+ float m_ = lab.x - 0.1055613458 * lab.y - 0.0638541728 * lab.z;
693
+ float s_ = lab.x - 0.0894841775 * lab.y - 1.2914855480 * lab.z;
694
+ float l = l_ * l_ * l_;
695
+ float m = m_ * m_ * m_;
696
+ float s = s_ * s_ * s_;
697
+ return vec3(
698
+ +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
699
+ -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
700
+ -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
701
+ );
702
+ }
703
+ `.trim();
704
+ }
705
+ function getColorGradingGLSL() {
706
+ return `
707
+ // ASC CDL Grading
708
+ vec3 flair_cdl(vec3 color, vec3 slope, vec3 offset, vec3 power, float sat) {
709
+ vec3 c = pow(max(color * slope + offset, vec3(0.0)), power);
710
+ float luma = dot(c, vec3(0.2126, 0.7152, 0.0722));
711
+ return mix(vec3(luma), c, sat);
712
+ }
713
+
714
+ // 3-Way Lift / Gamma / Gain Grading
715
+ vec3 flair_lift_gamma_gain(vec3 color, vec3 lift, vec3 gamma, vec3 gain) {
716
+ vec3 lifted = color + lift * (vec3(1.0) - color);
717
+ vec3 gained = max(lifted * gain, vec3(0.0));
718
+ return pow(gained, 1.0 / max(gamma, vec3(0.0001)));
719
+ }
720
+ `.trim();
721
+ }
722
+
723
+ export {
724
+ ImageLoadError,
725
+ SVG_EDGE,
726
+ isSvgUrl,
727
+ sizeSvg,
728
+ loadImage,
729
+ preloadImages,
730
+ releaseImages,
731
+ TextureCache,
732
+ applyTextureSettings,
733
+ BUILTIN_UNIFORMS,
734
+ VERTEX_SOURCE,
735
+ ShaderCompileError,
736
+ formatCompileError,
737
+ wrapSource,
738
+ parseCompileLog,
739
+ resetGlCaches,
740
+ compileShader,
741
+ drawFrame,
742
+ createContext,
743
+ srgbToLinear,
744
+ linearToSrgb,
745
+ srgbToLinearRgb,
746
+ linearRgbToSrgb,
747
+ linearSrgbToOklab,
748
+ oklabToLinearSrgb,
749
+ srgbToOklab,
750
+ oklabToSrgb,
751
+ oklabToOklch,
752
+ oklchToOklab,
753
+ interpolateOklab,
754
+ interpolateOklch,
755
+ convertColorSpace,
756
+ applyAscCdl,
757
+ applyLiftGammaGain,
758
+ applyVibrance,
759
+ getWhiteBalanceGains,
760
+ getOklabGLSL,
761
+ getColorGradingGLSL
762
+ };
763
+ //# sourceMappingURL=chunk-HKXOHRCD.js.map