@edinstvo/glass 0.0.0-stage → 0.1.0-beta.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { EdinstvoGlass, EdinstvoGlassGroup, defineGlass } from "./element.js";
2
+ export { GlassSurface, attachGlass, type GlassOptions } from "./surface.js";
3
+ export { type Material, defaults, ranges, resolveMaterial } from "./material.js";
4
+ export { type Motion, motionDefaults, motionRanges, resolveMotion } from "./interaction.js";
5
+ export type { GlassImage, GlassSource } from "./backdrop.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { EdinstvoGlass, EdinstvoGlassGroup, defineGlass } from "./element.js";
2
+ export { GlassSurface, attachGlass } from "./surface.js";
3
+ export { defaults, ranges, resolveMaterial } from "./material.js";
4
+ export { motionDefaults, motionRanges, resolveMotion } from "./interaction.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAqB,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAiB,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAe,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,61 @@
1
+ export interface Motion {
2
+ follow: number;
3
+ press: number;
4
+ jelly: number;
5
+ glow: number;
6
+ morph: number;
7
+ merge: number;
8
+ }
9
+ export declare const motionRanges: Record<keyof Motion, readonly [number, number]>;
10
+ export declare const motionKeys: (keyof Motion)[];
11
+ export declare const motionDefaults: Motion;
12
+ export declare const still: Motion;
13
+ export declare function resolveMotion(...layers: (Partial<Motion> | undefined)[]): Motion;
14
+ export declare class Spring {
15
+ private stiffness;
16
+ private damping;
17
+ value: number;
18
+ target: number;
19
+ velocity: number;
20
+ constructor(value?: number, stiffness?: number, damping?: number);
21
+ step(dt: number): void;
22
+ tune(stiffness: number): void;
23
+ snap(value: number): void;
24
+ shift(delta: number): void;
25
+ get settled(): boolean;
26
+ }
27
+ export interface ShapeBox {
28
+ x: number;
29
+ y: number;
30
+ halfW: number;
31
+ halfH: number;
32
+ radius: number;
33
+ join: number;
34
+ }
35
+ export declare class Shape {
36
+ readonly element: HTMLElement;
37
+ private wake;
38
+ x: Spring;
39
+ y: Spring;
40
+ w: Spring;
41
+ h: Spring;
42
+ pointerX: Spring;
43
+ pointerY: Spring;
44
+ held: Spring;
45
+ glow: Spring;
46
+ touchX: number;
47
+ touchY: number;
48
+ radius: number;
49
+ placed: boolean;
50
+ motion: Motion;
51
+ private springs;
52
+ private cleanup;
53
+ constructor(element: HTMLElement, wake: () => void);
54
+ get moving(): boolean;
55
+ get pressed(): boolean;
56
+ attach(): void;
57
+ detach(): void;
58
+ read(rect: DOMRect, scrolled: boolean, grow: boolean): void;
59
+ step(dt: number): void;
60
+ box(scale: number): ShapeBox;
61
+ }
@@ -0,0 +1,184 @@
1
+ export const motionRanges = {
2
+ follow: [0, 1],
3
+ press: [0, 1],
4
+ jelly: [0, 1],
5
+ glow: [0, 1],
6
+ morph: [0, 1],
7
+ merge: [0, 64],
8
+ };
9
+ export const motionKeys = Object.keys(motionRanges);
10
+ export const motionDefaults = { follow: 0.5, press: 0.5, jelly: 0.6, glow: 0.5, morph: 0.6, merge: 0 };
11
+ export const still = { follow: 0, press: 0, jelly: 0, glow: 0, morph: 0, merge: 0 };
12
+ export function resolveMotion(...layers) {
13
+ const result = { ...motionDefaults };
14
+ for (const layer of layers) {
15
+ for (const key of motionKeys) {
16
+ const value = layer?.[key];
17
+ if (typeof value === "number" && Number.isFinite(value))
18
+ result[key] = Math.min(motionRanges[key][1], Math.max(motionRanges[key][0], value));
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+ export class Spring {
24
+ stiffness;
25
+ damping;
26
+ value;
27
+ target;
28
+ velocity = 0;
29
+ constructor(value = 0, stiffness = 260, damping = 20) {
30
+ this.stiffness = stiffness;
31
+ this.damping = damping;
32
+ this.value = this.target = value;
33
+ }
34
+ step(dt) {
35
+ this.velocity += ((this.target - this.value) * this.stiffness - this.velocity * this.damping) * dt;
36
+ this.value += this.velocity * dt;
37
+ if (this.settled)
38
+ this.snap(this.target);
39
+ }
40
+ tune(stiffness) {
41
+ this.stiffness = stiffness;
42
+ this.damping = Math.sqrt(stiffness) * 1.1;
43
+ }
44
+ snap(value) {
45
+ this.value = this.target = value;
46
+ this.velocity = 0;
47
+ }
48
+ shift(delta) {
49
+ this.value += delta;
50
+ this.target += delta;
51
+ }
52
+ get settled() {
53
+ return Math.abs(this.target - this.value) < 0.01 && Math.abs(this.velocity) < 0.05;
54
+ }
55
+ }
56
+ const liquid = () => new Spring(0, 190, 15);
57
+ const clamp = (value, limit) => Math.max(-limit, Math.min(limit, value));
58
+ export class Shape {
59
+ element;
60
+ wake;
61
+ x = liquid();
62
+ y = liquid();
63
+ w = liquid();
64
+ h = liquid();
65
+ pointerX = new Spring();
66
+ pointerY = new Spring();
67
+ held = new Spring(0, 320, 22);
68
+ glow = new Spring(0, 60, 14);
69
+ touchX = 0;
70
+ touchY = 0;
71
+ radius = 0;
72
+ placed = false;
73
+ motion = motionDefaults;
74
+ springs = [this.x, this.y, this.w, this.h, this.pointerX, this.pointerY, this.held, this.glow];
75
+ cleanup = null;
76
+ constructor(element, wake) {
77
+ this.element = element;
78
+ this.wake = wake;
79
+ }
80
+ get moving() {
81
+ return this.springs.some((spring) => !spring.settled);
82
+ }
83
+ get pressed() {
84
+ return this.held.target === 1;
85
+ }
86
+ attach() {
87
+ const { element } = this;
88
+ const aim = (event) => {
89
+ const rect = element.getBoundingClientRect();
90
+ this.pointerX.target = clamp(((event.clientX - rect.left) / rect.width) * 2 - 1, 1);
91
+ this.pointerY.target = clamp(((event.clientY - rect.top) / rect.height) * 2 - 1, 1);
92
+ this.touchX = event.clientX;
93
+ this.touchY = event.clientY;
94
+ this.wake();
95
+ };
96
+ const leave = () => {
97
+ if (this.pressed)
98
+ return;
99
+ this.pointerX.target = this.pointerY.target = 0;
100
+ this.wake();
101
+ };
102
+ const down = (event) => {
103
+ aim(event);
104
+ this.held.target = 1;
105
+ this.glow.target = 1;
106
+ this.wake();
107
+ addEventListener("pointermove", aim);
108
+ };
109
+ const up = () => {
110
+ if (!this.pressed)
111
+ return;
112
+ removeEventListener("pointermove", aim);
113
+ this.held.target = this.glow.target = 0;
114
+ this.pointerX.target = this.pointerY.target = 0;
115
+ this.wake();
116
+ };
117
+ element.addEventListener("pointermove", aim);
118
+ element.addEventListener("pointerleave", leave);
119
+ element.addEventListener("pointerdown", down);
120
+ addEventListener("pointerup", up);
121
+ addEventListener("pointercancel", up);
122
+ this.cleanup = () => {
123
+ element.removeEventListener("pointermove", aim);
124
+ element.removeEventListener("pointerleave", leave);
125
+ element.removeEventListener("pointerdown", down);
126
+ removeEventListener("pointerup", up);
127
+ removeEventListener("pointercancel", up);
128
+ removeEventListener("pointermove", aim);
129
+ };
130
+ }
131
+ detach() {
132
+ this.cleanup?.();
133
+ this.cleanup = null;
134
+ this.element.style.removeProperty("--glass-scale");
135
+ }
136
+ read(rect, scrolled, grow) {
137
+ const animate = this.motion.morph > 0;
138
+ const { x, y, w, h } = this;
139
+ const cx = rect.left + rect.width / 2;
140
+ const cy = rect.top + rect.height / 2;
141
+ if (!this.placed || !animate) {
142
+ x.snap(cx);
143
+ y.snap(cy);
144
+ if (this.placed || !grow || !animate) {
145
+ w.snap(rect.width);
146
+ h.snap(rect.height);
147
+ }
148
+ else {
149
+ w.target = rect.width;
150
+ h.target = rect.height;
151
+ }
152
+ this.placed = true;
153
+ return;
154
+ }
155
+ if (scrolled) {
156
+ x.shift(cx - x.target);
157
+ y.shift(cy - y.target);
158
+ }
159
+ x.target = cx;
160
+ y.target = cy;
161
+ w.target = rect.width;
162
+ h.target = rect.height;
163
+ }
164
+ step(dt) {
165
+ const stiffness = 420 - 300 * this.motion.morph;
166
+ for (const spring of [this.x, this.y, this.w, this.h])
167
+ spring.tune(stiffness);
168
+ for (const spring of this.springs)
169
+ spring.step(dt);
170
+ }
171
+ box(scale) {
172
+ const { press, jelly, merge } = this.motion;
173
+ const swell = 1 + this.held.value * press * 0.12;
174
+ const pull = this.held.value * jelly * 0.1 * (Math.abs(this.pointerX.value) - Math.abs(this.pointerY.value));
175
+ const speedX = clamp(this.x.velocity / 2400, 0.22) * jelly * 1.6;
176
+ const speedY = clamp(this.y.velocity / 2400, 0.22) * jelly * 1.6;
177
+ const squash = Math.abs(speedX) - Math.abs(speedY);
178
+ const halfW = Math.max(this.w.value, 0) / 2 * swell * (1 + pull + squash) * scale;
179
+ const halfH = Math.max(this.h.value, 0) / 2 * swell * (1 - pull - squash) * scale;
180
+ this.element.style.setProperty("--glass-scale", swell.toFixed(4));
181
+ return { x: this.x.value, y: this.y.value, halfW, halfH, radius: Math.min(this.radius, halfW, halfH), join: merge };
182
+ }
183
+ }
184
+ //# sourceMappingURL=interaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../src/interaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface Material {
2
+ bloom: number;
3
+ light: number;
4
+ glint: number;
5
+ rim: number;
6
+ lift: number;
7
+ lightAngle: number;
8
+ transparency: number;
9
+ refraction: number;
10
+ depth: number;
11
+ dispersion: number;
12
+ frost: number;
13
+ splay: number;
14
+ tint: string;
15
+ }
16
+ export type NumericKey = Exclude<keyof Material, "tint">;
17
+ export declare const ranges: Record<NumericKey, readonly [number, number]>;
18
+ export declare const numericKeys: NumericKey[];
19
+ export declare const defaults: Material;
20
+ export declare const clampValue: (key: NumericKey, value: number) => number;
21
+ export declare function resolveMaterial(...layers: (Partial<Material> | undefined)[]): Material;
22
+ export declare const kebab: (key: string) => string;
@@ -0,0 +1,37 @@
1
+ export const ranges = {
2
+ bloom: [0, 1],
3
+ light: [0, 1],
4
+ glint: [0, 1],
5
+ rim: [0, 1],
6
+ lift: [0, 1],
7
+ lightAngle: [-180, 180],
8
+ transparency: [0, 1],
9
+ refraction: [0, 1],
10
+ depth: [0, 96],
11
+ dispersion: [0, 1],
12
+ frost: [0, 1],
13
+ splay: [0, 1],
14
+ };
15
+ export const numericKeys = Object.keys(ranges);
16
+ export const defaults = { bloom: 0.2, light: 0.3, glint: 0, rim: 0.35, lift: 0.15, lightAngle: -45, transparency: 0.85, refraction: 0.3, depth: 6, dispersion: 0.2, frost: 0.7, splay: 0, tint: "auto" };
17
+ export const clampValue = (key, value) => {
18
+ const [min, max] = ranges[key];
19
+ return Math.min(max, Math.max(min, value));
20
+ };
21
+ export function resolveMaterial(...layers) {
22
+ const result = { ...defaults };
23
+ for (const layer of layers) {
24
+ if (!layer)
25
+ continue;
26
+ for (const key of numericKeys) {
27
+ const value = layer[key];
28
+ if (typeof value === "number" && Number.isFinite(value))
29
+ result[key] = clampValue(key, value);
30
+ }
31
+ if (layer.tint)
32
+ result.tint = layer.tint;
33
+ }
34
+ return result;
35
+ }
36
+ export const kebab = (key) => key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
37
+ //# sourceMappingURL=material.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"material.js","sourceRoot":"","sources":["../src/material.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,MAAM,GAAkD;IACnE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACZ,UAAU,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC;IACvB,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IACd,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;CACd,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAiB,CAAC;AAE/D,MAAM,CAAC,MAAM,QAAQ,GAAa,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAEnN,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAe,EAAE,KAAa,EAAE,EAAE;IAC3D,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,GAAG,MAAyC;IAC1E,MAAM,MAAM,GAAa,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChG,CAAC;QACD,IAAI,KAAK,CAAC,IAAI;YAAE,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { Backdrop } from "./backdrop.js";
2
+ import type { Material } from "./material.js";
3
+ export declare const maxShapes = 8;
4
+ export interface DrawJob {
5
+ target: CanvasRenderingContext2D;
6
+ backdrop: Backdrop;
7
+ width: number;
8
+ height: number;
9
+ pad: number;
10
+ boxes: Float32Array;
11
+ radii: Float32Array;
12
+ count: number;
13
+ joins: Float32Array;
14
+ peak: number;
15
+ appear: number;
16
+ touch: readonly [number, number, number];
17
+ dpr: number;
18
+ uvOrigin: readonly [number, number];
19
+ uvPerPixel: readonly [number, number];
20
+ lightDir: readonly [number, number];
21
+ tint: readonly [number, number, number];
22
+ material: Material;
23
+ }
24
+ export declare class Renderer {
25
+ private static instance;
26
+ static get shared(): Renderer | null;
27
+ onRestore: (() => void) | null;
28
+ private canvas;
29
+ private gl;
30
+ private uniforms;
31
+ private textures;
32
+ private frame;
33
+ private lens;
34
+ private blurProgram;
35
+ private frameBuffer;
36
+ private lost;
37
+ private setup;
38
+ private link;
39
+ private compile;
40
+ private target;
41
+ private pass;
42
+ /** Gaussian at a size where sigma is 3 texels, so any strength costs the same 17 taps per axis. */
43
+ private blurred;
44
+ nextFrame(): void;
45
+ private shrink;
46
+ private bind;
47
+ /** Throws on a tainted source: a cross-origin image without CORS headers can't reach the GPU. */
48
+ draw(job: DrawJob): void;
49
+ }
@@ -0,0 +1,237 @@
1
+ import { blurFragment, fragment, vertex } from "./shader.js";
2
+ export const maxShapes = 8;
3
+ const uniformNames = ["uBoxes", "uRadii", "uCount", "uCanvas", "uUvOrigin", "uUvPerPx", "uTexSize", "uLightDir", "uTint", "uTouch", "uViewY", "uPad", "uDpr", "uJoin", "uPeak", "uAppear", "uLight", "uTransparency", "uRefraction", "uDepth", "uDispersion", "uFrost", "uSplay", "uBloom", "uGlint", "uRim", "uLift"];
4
+ const maxTextures = 8;
5
+ const maxTextureSide = 4096;
6
+ export class Renderer {
7
+ static instance;
8
+ static get shared() {
9
+ if (Renderer.instance === undefined) {
10
+ const renderer = new Renderer();
11
+ Renderer.instance = renderer.setup() ? renderer : null;
12
+ }
13
+ return Renderer.instance;
14
+ }
15
+ onRestore = null;
16
+ canvas = document.createElement("canvas");
17
+ gl;
18
+ uniforms = {};
19
+ textures = new Map();
20
+ frame = 0;
21
+ lens;
22
+ blurProgram;
23
+ frameBuffer = null;
24
+ lost = false;
25
+ setup() {
26
+ const gl = this.canvas.getContext("webgl2", { alpha: true, premultipliedAlpha: true, antialias: false, depth: false, stencil: false });
27
+ if (!gl)
28
+ return false;
29
+ this.gl = gl;
30
+ this.canvas.addEventListener("webglcontextlost", (event) => {
31
+ event.preventDefault();
32
+ this.lost = true;
33
+ });
34
+ this.canvas.addEventListener("webglcontextrestored", () => {
35
+ this.lost = false;
36
+ this.textures.clear();
37
+ this.compile();
38
+ this.onRestore?.();
39
+ });
40
+ return this.compile();
41
+ }
42
+ link(source) {
43
+ const { gl } = this;
44
+ const program = gl.createProgram();
45
+ for (const [type, code] of [[gl.VERTEX_SHADER, vertex], [gl.FRAGMENT_SHADER, source]]) {
46
+ const shader = gl.createShader(type);
47
+ if (!shader || !program)
48
+ return null;
49
+ gl.shaderSource(shader, code);
50
+ gl.compileShader(shader);
51
+ gl.attachShader(program, shader);
52
+ }
53
+ gl.linkProgram(program);
54
+ if (gl.getProgramParameter(program, gl.LINK_STATUS))
55
+ return program;
56
+ console.warn("@edinstvo/glass:", gl.getProgramInfoLog(program));
57
+ return null;
58
+ }
59
+ compile() {
60
+ const { gl } = this;
61
+ const lens = this.link(fragment);
62
+ const blur = this.link(blurFragment);
63
+ if (!lens || !blur)
64
+ return false;
65
+ this.lens = lens;
66
+ this.blurProgram = blur;
67
+ this.frameBuffer = gl.createFramebuffer();
68
+ gl.bindVertexArray(gl.createVertexArray());
69
+ gl.enable(gl.SCISSOR_TEST);
70
+ gl.useProgram(blur);
71
+ gl.uniform1i(gl.getUniformLocation(blur, "uSource"), 0);
72
+ gl.useProgram(lens);
73
+ gl.uniform1i(gl.getUniformLocation(lens, "uBackdrop"), 0);
74
+ gl.uniform1i(gl.getUniformLocation(lens, "uFrosted"), 1);
75
+ for (const name of uniformNames)
76
+ this.uniforms[name] = gl.getUniformLocation(lens, name);
77
+ return true;
78
+ }
79
+ target(width, height) {
80
+ const { gl } = this;
81
+ const texture = gl.createTexture();
82
+ gl.bindTexture(gl.TEXTURE_2D, texture);
83
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
84
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
85
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
86
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
87
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
88
+ return texture;
89
+ }
90
+ pass(source, destination, width, height, step, lod) {
91
+ const { gl, blurProgram: program } = this;
92
+ const u = (name) => gl.getUniformLocation(program, name);
93
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
94
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destination, 0);
95
+ gl.viewport(0, 0, width, height);
96
+ gl.scissor(0, 0, width, height);
97
+ gl.activeTexture(gl.TEXTURE0);
98
+ gl.bindTexture(gl.TEXTURE_2D, source);
99
+ gl.uniform2f(u("uStep"), step[0], step[1]);
100
+ gl.uniform2f(u("uInvSize"), 1 / width, 1 / height);
101
+ gl.uniform1f(u("uLod"), lod);
102
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
103
+ }
104
+ /** Gaussian at a size where sigma is 3 texels, so any strength costs the same 17 taps per axis. */
105
+ blurred(entry, sigma) {
106
+ const { gl } = this;
107
+ const key = Math.round(sigma * 4) / 4;
108
+ const cached = entry.blurs.get(key);
109
+ if (cached && cached.frame === entry.frame)
110
+ return cached.texture;
111
+ if (cached)
112
+ gl.deleteTexture(cached.texture);
113
+ if (entry.blurs.size >= 4 && !cached) {
114
+ const [oldest, stale] = entry.blurs.entries().next().value;
115
+ gl.deleteTexture(stale.texture);
116
+ entry.blurs.delete(oldest);
117
+ }
118
+ const scale = Math.min(1, 3 / Math.max(key, 0.25));
119
+ const width = Math.max(4, Math.ceil(entry.width * scale));
120
+ const height = Math.max(4, Math.ceil(entry.height * scale));
121
+ const across = this.target(width, height);
122
+ const result = this.target(width, height);
123
+ gl.useProgram(this.blurProgram);
124
+ this.pass(entry.texture, across, width, height, [1 / width, 0], Math.log2(1 / scale));
125
+ this.pass(across, result, width, height, [0, 1 / height], 0);
126
+ gl.deleteTexture(across);
127
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
128
+ gl.useProgram(this.lens);
129
+ entry.blurs.set(key, { texture: result, frame: entry.frame });
130
+ return result;
131
+ }
132
+ nextFrame() {
133
+ this.frame++;
134
+ }
135
+ shrink(backdrop) {
136
+ const limit = Math.min(maxTextureSide, this.gl.getParameter(this.gl.MAX_TEXTURE_SIZE));
137
+ const scale = limit / Math.max(backdrop.width, backdrop.height);
138
+ if (scale >= 1)
139
+ return backdrop.image;
140
+ const canvas = document.createElement("canvas");
141
+ canvas.width = Math.round(backdrop.width * scale);
142
+ canvas.height = Math.round(backdrop.height * scale);
143
+ canvas.getContext("2d").drawImage(backdrop.image, 0, 0, canvas.width, canvas.height);
144
+ return canvas;
145
+ }
146
+ bind(backdrop) {
147
+ const { gl } = this;
148
+ let entry = this.textures.get(backdrop.image);
149
+ if (entry) {
150
+ gl.bindTexture(gl.TEXTURE_2D, entry.texture);
151
+ if (!backdrop.live || entry.frame === this.frame)
152
+ return entry;
153
+ }
154
+ else {
155
+ const texture = gl.createTexture();
156
+ gl.bindTexture(gl.TEXTURE_2D, texture);
157
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
158
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
159
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
160
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
161
+ entry = { texture, width: 0, height: 0, frame: -1, blurs: new Map() };
162
+ this.textures.set(backdrop.image, entry);
163
+ if (this.textures.size > maxTextures) {
164
+ const [oldest, stale] = this.textures.entries().next().value;
165
+ gl.deleteTexture(stale.texture);
166
+ for (const blur of stale.blurs.values())
167
+ gl.deleteTexture(blur.texture);
168
+ this.textures.delete(oldest);
169
+ }
170
+ }
171
+ const pixels = this.shrink(backdrop);
172
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
173
+ gl.generateMipmap(gl.TEXTURE_2D);
174
+ entry.width = backdrop.width;
175
+ entry.height = backdrop.height;
176
+ entry.frame = this.frame;
177
+ return entry;
178
+ }
179
+ /** Throws on a tainted source: a cross-origin image without CORS headers can't reach the GPU. */
180
+ draw(job) {
181
+ if (this.lost)
182
+ return;
183
+ const { gl, canvas, uniforms: u } = this;
184
+ const { width, height, material: m } = job;
185
+ if (canvas.width < width || canvas.height < height) {
186
+ canvas.width = Math.max(canvas.width, width);
187
+ canvas.height = Math.max(canvas.height, height);
188
+ }
189
+ gl.activeTexture(gl.TEXTURE0);
190
+ const texture = this.bind(job.backdrop);
191
+ if (m.frost > 0) {
192
+ const texels = Math.max(job.uvPerPixel[0] * texture.width, job.uvPerPixel[1] * texture.height);
193
+ const frosted = this.blurred(texture, m.frost * 20 * job.dpr * texels);
194
+ gl.activeTexture(gl.TEXTURE1);
195
+ gl.bindTexture(gl.TEXTURE_2D, frosted);
196
+ gl.activeTexture(gl.TEXTURE0);
197
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
198
+ }
199
+ const viewY = canvas.height - height;
200
+ gl.viewport(0, viewY, width, height);
201
+ gl.scissor(0, viewY, width, height);
202
+ gl.clearColor(0, 0, 0, 0);
203
+ gl.clear(gl.COLOR_BUFFER_BIT);
204
+ gl.uniform2f(u.uCanvas, width, height);
205
+ gl.uniform4fv(u.uBoxes, job.boxes);
206
+ gl.uniform1fv(u.uRadii, job.radii);
207
+ gl.uniform1i(u.uCount, job.count);
208
+ gl.uniform1fv(u.uJoin, job.joins);
209
+ gl.uniform1f(u.uPeak, job.peak);
210
+ gl.uniform1f(u.uAppear, job.appear);
211
+ gl.uniform3f(u.uTouch, job.touch[0], job.touch[1], job.touch[2]);
212
+ gl.uniform2f(u.uUvOrigin, job.uvOrigin[0], job.uvOrigin[1]);
213
+ gl.uniform2f(u.uUvPerPx, job.uvPerPixel[0], job.uvPerPixel[1]);
214
+ gl.uniform2f(u.uTexSize, texture.width, texture.height);
215
+ gl.uniform2f(u.uLightDir, job.lightDir[0], job.lightDir[1]);
216
+ gl.uniform3f(u.uTint, job.tint[0], job.tint[1], job.tint[2]);
217
+ gl.uniform1f(u.uViewY, viewY);
218
+ gl.uniform1f(u.uPad, job.pad);
219
+ gl.uniform1f(u.uDpr, job.dpr);
220
+ gl.uniform1f(u.uLight, m.light);
221
+ gl.uniform1f(u.uTransparency, m.transparency);
222
+ gl.uniform1f(u.uRefraction, m.refraction);
223
+ gl.uniform1f(u.uDepth, m.depth);
224
+ gl.uniform1f(u.uDispersion, m.dispersion);
225
+ gl.uniform1f(u.uFrost, m.frost);
226
+ gl.uniform1f(u.uSplay, m.splay);
227
+ gl.uniform1f(u.uBloom, m.bloom);
228
+ gl.uniform1f(u.uGlint, m.glint);
229
+ gl.uniform1f(u.uRim, m.rim);
230
+ gl.uniform1f(u.uLift, m.lift);
231
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
232
+ // ── the drawing buffer is only valid until this task ends, so copy out right here
233
+ job.target.clearRect(0, 0, width, height);
234
+ job.target.drawImage(canvas, 0, 0, width, height, 0, 0, width, height);
235
+ }
236
+ }
237
+ //# sourceMappingURL=renderer.js.map