interactive-bg 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +202 -2
- package/dist/core/canvas2d-renderer.cjs +119 -0
- package/dist/core/canvas2d-renderer.cjs.map +1 -0
- package/dist/core/canvas2d-renderer.d.cts +7 -0
- package/dist/core/canvas2d-renderer.d.ts +7 -0
- package/dist/core/canvas2d-renderer.js +119 -0
- package/dist/core/canvas2d-renderer.js.map +1 -0
- package/dist/core/color.cjs +46 -0
- package/dist/core/color.cjs.map +1 -0
- package/dist/core/color.d.cts +6 -0
- package/dist/core/color.d.ts +6 -0
- package/dist/core/color.js +46 -0
- package/dist/core/color.js.map +1 -0
- package/dist/core/engine.cjs +367 -0
- package/dist/core/engine.cjs.map +1 -0
- package/dist/core/engine.d.cts +69 -0
- package/dist/core/engine.d.ts +69 -0
- package/dist/core/engine.js +366 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/style.cjs +21 -0
- package/dist/core/style.cjs.map +1 -0
- package/dist/core/style.d.cts +85 -0
- package/dist/core/style.d.ts +85 -0
- package/dist/core/style.js +21 -0
- package/dist/core/style.js.map +1 -0
- package/dist/core/surface.cjs +25 -0
- package/dist/core/surface.cjs.map +1 -0
- package/dist/core/surface.d.cts +42 -0
- package/dist/core/surface.d.ts +42 -0
- package/dist/core/surface.js +24 -0
- package/dist/core/surface.js.map +1 -0
- package/dist/core/webgl-renderer.cjs +137 -0
- package/dist/core/webgl-renderer.cjs.map +1 -0
- package/dist/core/webgl-renderer.d.cts +8 -0
- package/dist/core/webgl-renderer.d.ts +8 -0
- package/dist/core/webgl-renderer.js +137 -0
- package/dist/core/webgl-renderer.js.map +1 -0
- package/dist/core/webgl.cjs +62 -0
- package/dist/core/webgl.cjs.map +1 -0
- package/dist/core/webgl.d.cts +15 -0
- package/dist/core/webgl.d.ts +15 -0
- package/dist/core/webgl.js +60 -0
- package/dist/core/webgl.js.map +1 -0
- package/dist/dots/canvas2d.cjs +14 -0
- package/dist/dots/canvas2d.cjs.map +1 -0
- package/dist/dots/canvas2d.d.cts +5 -0
- package/dist/dots/canvas2d.d.ts +5 -0
- package/dist/dots/canvas2d.js +13 -0
- package/dist/dots/canvas2d.js.map +1 -0
- package/dist/dots/index.cjs +16 -0
- package/dist/dots/index.cjs.map +1 -0
- package/dist/dots/index.d.cts +5 -0
- package/dist/dots/index.d.ts +5 -0
- package/dist/dots/index.js +15 -0
- package/dist/dots/index.js.map +1 -0
- package/dist/dots/webgl.cjs +14 -0
- package/dist/dots/webgl.cjs.map +1 -0
- package/dist/dots/webgl.d.cts +5 -0
- package/dist/dots/webgl.d.ts +5 -0
- package/dist/dots/webgl.js +13 -0
- package/dist/dots/webgl.js.map +1 -0
- package/dist/index.cjs +24 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +11 -0
- package/dist/index.umd.js +211 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/plus/canvas2d.cjs +14 -0
- package/dist/plus/canvas2d.cjs.map +1 -0
- package/dist/plus/canvas2d.d.cts +5 -0
- package/dist/plus/canvas2d.d.ts +5 -0
- package/dist/plus/canvas2d.js +13 -0
- package/dist/plus/canvas2d.js.map +1 -0
- package/dist/plus/index.cjs +16 -0
- package/dist/plus/index.cjs.map +1 -0
- package/dist/plus/index.d.cts +5 -0
- package/dist/plus/index.d.ts +5 -0
- package/dist/plus/index.js +15 -0
- package/dist/plus/index.js.map +1 -0
- package/dist/plus/webgl.cjs +14 -0
- package/dist/plus/webgl.cjs.map +1 -0
- package/dist/plus/webgl.d.cts +5 -0
- package/dist/plus/webgl.d.ts +5 -0
- package/dist/plus/webgl.js +13 -0
- package/dist/plus/webgl.js.map +1 -0
- package/dist/shared/buffer.cjs +20 -0
- package/dist/shared/buffer.cjs.map +1 -0
- package/dist/shared/buffer.d.cts +11 -0
- package/dist/shared/buffer.d.ts +11 -0
- package/dist/shared/buffer.js +19 -0
- package/dist/shared/buffer.js.map +1 -0
- package/dist/shared/grid-webgl.cjs +63 -0
- package/dist/shared/grid-webgl.cjs.map +1 -0
- package/dist/shared/grid-webgl.d.cts +10 -0
- package/dist/shared/grid-webgl.d.ts +10 -0
- package/dist/shared/grid-webgl.js +61 -0
- package/dist/shared/grid-webgl.js.map +1 -0
- package/dist/shared/grid.cjs +133 -0
- package/dist/shared/grid.cjs.map +1 -0
- package/dist/shared/grid.d.cts +72 -0
- package/dist/shared/grid.d.ts +72 -0
- package/dist/shared/grid.js +123 -0
- package/dist/shared/grid.js.map +1 -0
- package/dist/shared/noise.cjs +166 -0
- package/dist/shared/noise.cjs.map +1 -0
- package/dist/shared/noise.d.cts +4 -0
- package/dist/shared/noise.d.ts +4 -0
- package/dist/shared/noise.js +166 -0
- package/dist/shared/noise.js.map +1 -0
- package/dist/styles/dots/canvas2d.cjs +99 -0
- package/dist/styles/dots/canvas2d.cjs.map +1 -0
- package/dist/styles/dots/canvas2d.d.cts +7 -0
- package/dist/styles/dots/canvas2d.d.ts +7 -0
- package/dist/styles/dots/canvas2d.js +99 -0
- package/dist/styles/dots/canvas2d.js.map +1 -0
- package/dist/styles/dots/definition.cjs +15 -0
- package/dist/styles/dots/definition.cjs.map +1 -0
- package/dist/styles/dots/definition.d.cts +7 -0
- package/dist/styles/dots/definition.d.ts +7 -0
- package/dist/styles/dots/definition.js +16 -0
- package/dist/styles/dots/definition.js.map +1 -0
- package/dist/styles/dots/motion.cjs +6 -0
- package/dist/styles/dots/motion.cjs.map +1 -0
- package/dist/styles/dots/motion.d.cts +2 -0
- package/dist/styles/dots/motion.d.ts +2 -0
- package/dist/styles/dots/motion.js +6 -0
- package/dist/styles/dots/motion.js.map +1 -0
- package/dist/styles/dots/options.cjs +33 -0
- package/dist/styles/dots/options.cjs.map +1 -0
- package/dist/styles/dots/options.d.cts +33 -0
- package/dist/styles/dots/options.d.ts +33 -0
- package/dist/styles/dots/options.js +32 -0
- package/dist/styles/dots/options.js.map +1 -0
- package/dist/styles/dots/webgl.cjs +113 -0
- package/dist/styles/dots/webgl.cjs.map +1 -0
- package/dist/styles/dots/webgl.d.cts +4 -0
- package/dist/styles/dots/webgl.d.ts +4 -0
- package/dist/styles/dots/webgl.js +113 -0
- package/dist/styles/dots/webgl.js.map +1 -0
- package/dist/styles/plus/canvas2d.cjs +88 -0
- package/dist/styles/plus/canvas2d.cjs.map +1 -0
- package/dist/styles/plus/canvas2d.d.cts +8 -0
- package/dist/styles/plus/canvas2d.d.ts +8 -0
- package/dist/styles/plus/canvas2d.js +88 -0
- package/dist/styles/plus/canvas2d.js.map +1 -0
- package/dist/styles/plus/definition.cjs +17 -0
- package/dist/styles/plus/definition.cjs.map +1 -0
- package/dist/styles/plus/definition.d.cts +9 -0
- package/dist/styles/plus/definition.d.ts +9 -0
- package/dist/styles/plus/definition.js +18 -0
- package/dist/styles/plus/definition.js.map +1 -0
- package/dist/styles/plus/motion.cjs +18 -0
- package/dist/styles/plus/motion.cjs.map +1 -0
- package/dist/styles/plus/motion.d.cts +5 -0
- package/dist/styles/plus/motion.d.ts +5 -0
- package/dist/styles/plus/motion.js +18 -0
- package/dist/styles/plus/motion.js.map +1 -0
- package/dist/styles/plus/options.cjs +45 -0
- package/dist/styles/plus/options.cjs.map +1 -0
- package/dist/styles/plus/options.d.cts +45 -0
- package/dist/styles/plus/options.d.ts +45 -0
- package/dist/styles/plus/options.js +43 -0
- package/dist/styles/plus/options.js.map +1 -0
- package/dist/styles/plus/turns.cjs +102 -0
- package/dist/styles/plus/turns.cjs.map +1 -0
- package/dist/styles/plus/turns.d.cts +17 -0
- package/dist/styles/plus/turns.d.ts +17 -0
- package/dist/styles/plus/turns.js +102 -0
- package/dist/styles/plus/turns.js.map +1 -0
- package/dist/styles/plus/webgl.cjs +121 -0
- package/dist/styles/plus/webgl.cjs.map +1 -0
- package/dist/styles/plus/webgl.d.cts +8 -0
- package/dist/styles/plus/webgl.d.ts +8 -0
- package/dist/styles/plus/webgl.js +121 -0
- package/dist/styles/plus/webgl.js.map +1 -0
- package/dist/styles/topo/canvas2d.cjs +77 -0
- package/dist/styles/topo/canvas2d.cjs.map +1 -0
- package/dist/styles/topo/canvas2d.d.cts +4 -0
- package/dist/styles/topo/canvas2d.d.ts +4 -0
- package/dist/styles/topo/canvas2d.js +77 -0
- package/dist/styles/topo/canvas2d.js.map +1 -0
- package/dist/styles/topo/contours.cjs +83 -0
- package/dist/styles/topo/contours.cjs.map +1 -0
- package/dist/styles/topo/contours.d.cts +9 -0
- package/dist/styles/topo/contours.d.ts +9 -0
- package/dist/styles/topo/contours.js +83 -0
- package/dist/styles/topo/contours.js.map +1 -0
- package/dist/styles/topo/definition.cjs +13 -0
- package/dist/styles/topo/definition.cjs.map +1 -0
- package/dist/styles/topo/definition.d.cts +7 -0
- package/dist/styles/topo/definition.d.ts +7 -0
- package/dist/styles/topo/definition.js +14 -0
- package/dist/styles/topo/definition.js.map +1 -0
- package/dist/styles/topo/field.cjs +118 -0
- package/dist/styles/topo/field.cjs.map +1 -0
- package/dist/styles/topo/field.d.cts +26 -0
- package/dist/styles/topo/field.d.ts +26 -0
- package/dist/styles/topo/field.js +118 -0
- package/dist/styles/topo/field.js.map +1 -0
- package/dist/styles/topo/options.cjs +39 -0
- package/dist/styles/topo/options.cjs.map +1 -0
- package/dist/styles/topo/options.d.cts +52 -0
- package/dist/styles/topo/options.d.ts +52 -0
- package/dist/styles/topo/options.js +38 -0
- package/dist/styles/topo/options.js.map +1 -0
- package/dist/styles/topo/terrain.cjs +22 -0
- package/dist/styles/topo/terrain.cjs.map +1 -0
- package/dist/styles/topo/terrain.d.cts +11 -0
- package/dist/styles/topo/terrain.d.ts +11 -0
- package/dist/styles/topo/terrain.js +22 -0
- package/dist/styles/topo/terrain.js.map +1 -0
- package/dist/styles/topo/webgl.cjs +150 -0
- package/dist/styles/topo/webgl.cjs.map +1 -0
- package/dist/styles/topo/webgl.d.cts +7 -0
- package/dist/styles/topo/webgl.d.ts +7 -0
- package/dist/styles/topo/webgl.js +150 -0
- package/dist/styles/topo/webgl.js.map +1 -0
- package/dist/topo/canvas2d.cjs +14 -0
- package/dist/topo/canvas2d.cjs.map +1 -0
- package/dist/topo/canvas2d.d.cts +5 -0
- package/dist/topo/canvas2d.d.ts +5 -0
- package/dist/topo/canvas2d.js +13 -0
- package/dist/topo/canvas2d.js.map +1 -0
- package/dist/topo/index.cjs +16 -0
- package/dist/topo/index.cjs.map +1 -0
- package/dist/topo/index.d.cts +5 -0
- package/dist/topo/index.d.ts +5 -0
- package/dist/topo/index.js +15 -0
- package/dist/topo/index.js.map +1 -0
- package/dist/topo/webgl.cjs +14 -0
- package/dist/topo/webgl.cjs.map +1 -0
- package/dist/topo/webgl.d.cts +5 -0
- package/dist/topo/webgl.d.ts +5 -0
- package/dist/topo/webgl.js +13 -0
- package/dist/topo/webgl.js.map +1 -0
- package/package.json +192 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/core/surface.ts
|
|
2
|
+
/** Whether any side fades. */
|
|
3
|
+
function fades(fade) {
|
|
4
|
+
return fade.top > 0 || fade.right > 0 || fade.bottom > 0 || fade.left > 0;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Set up the first renderer that works here, trying them in the style's order,
|
|
8
|
+
* with `preference` (if the style has it) first. `null` if none works.
|
|
9
|
+
*
|
|
10
|
+
* A canvas keeps the first kind of context it hands out, so after a renderer
|
|
11
|
+
* fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.
|
|
12
|
+
*/
|
|
13
|
+
function createSurface(canvas, renderers, options, preference, invalidate, replaceCanvas) {
|
|
14
|
+
const ordered = preference === "auto" ? renderers : [...renderers.filter((r) => r.name === preference), ...renderers.filter((r) => r.name !== preference)];
|
|
15
|
+
for (const [k, renderer] of ordered.entries()) {
|
|
16
|
+
if (k > 0) replaceCanvas?.();
|
|
17
|
+
const surface = renderer.create(canvas(), options, invalidate);
|
|
18
|
+
if (surface) return surface;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
exports.createSurface = createSurface;
|
|
23
|
+
exports.fades = fades;
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=surface.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.cjs","names":[],"sources":["../../src/core/surface.ts"],"sourcesContent":["import type { Frame, RendererName } from './style';\n\n/** How far in from each edge the style's drawing fades out, in CSS pixels. `0` = no fade on that side. */\nexport interface Fade {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** Whether any side fades. */\nexport function fades(fade: Fade): boolean {\n return fade.top > 0 || fade.right > 0 || fade.bottom > 0 || fade.left > 0;\n}\n\n/** A canvas context plus the style renderer drawing into it. */\nexport interface Surface<O> {\n readonly name: RendererName;\n update(options: O): void;\n /**\n * Clear to `backgroundColor` (`null` = transparent) and let the style draw, then fade the\n * style's drawing out towards the edges as `fade` says, keeping the background colour solid.\n * Fades on several sides multiply. Returns `true` while the style has a short-lived effect playing.\n */\n render(frame: Frame, backgroundColor: string | null, fade: Fade): boolean;\n /** Leave the canvas blank. */\n clear(): void;\n destroy(): void;\n}\n\n/**\n * One way to draw a style: a kind of canvas context plus the style's renderer\n * for it. Made with `webglRenderer()` or `canvas2dRenderer()`, each in its own\n * module, so a style only bundles the backends it lists.\n */\nexport interface Renderer<O> {\n readonly name: RendererName;\n /** Set up on `canvas`, or return `null` if this kind of context isn't available. */\n create(canvas: HTMLCanvasElement, options: O, invalidate: () => void): Surface<O> | null;\n}\n\n/**\n * Set up the first renderer that works here, trying them in the style's order,\n * with `preference` (if the style has it) first. `null` if none works.\n *\n * A canvas keeps the first kind of context it hands out, so after a renderer\n * fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.\n */\nexport function createSurface<O>(\n canvas: () => HTMLCanvasElement,\n renderers: readonly Renderer<O>[],\n options: O,\n preference: 'auto' | RendererName,\n invalidate: () => void,\n replaceCanvas?: () => void,\n): Surface<O> | null {\n const ordered =\n preference === 'auto'\n ? renderers\n : [...renderers.filter((r) => r.name === preference), ...renderers.filter((r) => r.name !== preference)];\n for (const [k, renderer] of ordered.entries()) {\n if (k > 0) replaceCanvas?.();\n const surface = renderer.create(canvas(), options, invalidate);\n if (surface) return surface;\n }\n return null;\n}\n"],"mappings":";;AAWA,SAAgB,MAAM,MAAqB;CACzC,OAAO,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO;AAC1E;;;;;;;;AAmCA,SAAgB,cACd,QACA,WACA,SACA,YACA,YACA,eACmB;CACnB,MAAM,UACJ,eAAe,SACX,YACA,CAAC,GAAG,UAAU,QAAQ,MAAM,EAAE,SAAS,UAAU,GAAG,GAAG,UAAU,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC;CAC3G,KAAK,MAAM,CAAC,GAAG,aAAa,QAAQ,QAAQ,GAAG;EAC7C,IAAI,IAAI,GAAG,gBAAgB;EAC3B,MAAM,UAAU,SAAS,OAAO,OAAO,GAAG,SAAS,UAAU;EAC7D,IAAI,SAAS,OAAO;CACtB;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Frame, RendererName } from './style.cjs';
|
|
2
|
+
/** How far in from each edge the style's drawing fades out, in CSS pixels. `0` = no fade on that side. */
|
|
3
|
+
export interface Fade {
|
|
4
|
+
top: number;
|
|
5
|
+
right: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
left: number;
|
|
8
|
+
}
|
|
9
|
+
/** Whether any side fades. */
|
|
10
|
+
export declare function fades(fade: Fade): boolean;
|
|
11
|
+
/** A canvas context plus the style renderer drawing into it. */
|
|
12
|
+
export interface Surface<O> {
|
|
13
|
+
readonly name: RendererName;
|
|
14
|
+
update(options: O): void;
|
|
15
|
+
/**
|
|
16
|
+
* Clear to `backgroundColor` (`null` = transparent) and let the style draw, then fade the
|
|
17
|
+
* style's drawing out towards the edges as `fade` says, keeping the background colour solid.
|
|
18
|
+
* Fades on several sides multiply. Returns `true` while the style has a short-lived effect playing.
|
|
19
|
+
*/
|
|
20
|
+
render(frame: Frame, backgroundColor: string | null, fade: Fade): boolean;
|
|
21
|
+
/** Leave the canvas blank. */
|
|
22
|
+
clear(): void;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One way to draw a style: a kind of canvas context plus the style's renderer
|
|
27
|
+
* for it. Made with `webglRenderer()` or `canvas2dRenderer()`, each in its own
|
|
28
|
+
* module, so a style only bundles the backends it lists.
|
|
29
|
+
*/
|
|
30
|
+
export interface Renderer<O> {
|
|
31
|
+
readonly name: RendererName;
|
|
32
|
+
/** Set up on `canvas`, or return `null` if this kind of context isn't available. */
|
|
33
|
+
create(canvas: HTMLCanvasElement, options: O, invalidate: () => void): Surface<O> | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set up the first renderer that works here, trying them in the style's order,
|
|
37
|
+
* with `preference` (if the style has it) first. `null` if none works.
|
|
38
|
+
*
|
|
39
|
+
* A canvas keeps the first kind of context it hands out, so after a renderer
|
|
40
|
+
* fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createSurface<O>(canvas: () => HTMLCanvasElement, renderers: readonly Renderer<O>[], options: O, preference: 'auto' | RendererName, invalidate: () => void, replaceCanvas?: () => void): Surface<O> | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Frame, RendererName } from './style.js';
|
|
2
|
+
/** How far in from each edge the style's drawing fades out, in CSS pixels. `0` = no fade on that side. */
|
|
3
|
+
export interface Fade {
|
|
4
|
+
top: number;
|
|
5
|
+
right: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
left: number;
|
|
8
|
+
}
|
|
9
|
+
/** Whether any side fades. */
|
|
10
|
+
export declare function fades(fade: Fade): boolean;
|
|
11
|
+
/** A canvas context plus the style renderer drawing into it. */
|
|
12
|
+
export interface Surface<O> {
|
|
13
|
+
readonly name: RendererName;
|
|
14
|
+
update(options: O): void;
|
|
15
|
+
/**
|
|
16
|
+
* Clear to `backgroundColor` (`null` = transparent) and let the style draw, then fade the
|
|
17
|
+
* style's drawing out towards the edges as `fade` says, keeping the background colour solid.
|
|
18
|
+
* Fades on several sides multiply. Returns `true` while the style has a short-lived effect playing.
|
|
19
|
+
*/
|
|
20
|
+
render(frame: Frame, backgroundColor: string | null, fade: Fade): boolean;
|
|
21
|
+
/** Leave the canvas blank. */
|
|
22
|
+
clear(): void;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One way to draw a style: a kind of canvas context plus the style's renderer
|
|
27
|
+
* for it. Made with `webglRenderer()` or `canvas2dRenderer()`, each in its own
|
|
28
|
+
* module, so a style only bundles the backends it lists.
|
|
29
|
+
*/
|
|
30
|
+
export interface Renderer<O> {
|
|
31
|
+
readonly name: RendererName;
|
|
32
|
+
/** Set up on `canvas`, or return `null` if this kind of context isn't available. */
|
|
33
|
+
create(canvas: HTMLCanvasElement, options: O, invalidate: () => void): Surface<O> | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set up the first renderer that works here, trying them in the style's order,
|
|
37
|
+
* with `preference` (if the style has it) first. `null` if none works.
|
|
38
|
+
*
|
|
39
|
+
* A canvas keeps the first kind of context it hands out, so after a renderer
|
|
40
|
+
* fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createSurface<O>(canvas: () => HTMLCanvasElement, renderers: readonly Renderer<O>[], options: O, preference: 'auto' | RendererName, invalidate: () => void, replaceCanvas?: () => void): Surface<O> | null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/core/surface.ts
|
|
2
|
+
/** Whether any side fades. */
|
|
3
|
+
function fades(fade) {
|
|
4
|
+
return fade.top > 0 || fade.right > 0 || fade.bottom > 0 || fade.left > 0;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Set up the first renderer that works here, trying them in the style's order,
|
|
8
|
+
* with `preference` (if the style has it) first. `null` if none works.
|
|
9
|
+
*
|
|
10
|
+
* A canvas keeps the first kind of context it hands out, so after a renderer
|
|
11
|
+
* fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.
|
|
12
|
+
*/
|
|
13
|
+
function createSurface(canvas, renderers, options, preference, invalidate, replaceCanvas) {
|
|
14
|
+
const ordered = preference === "auto" ? renderers : [...renderers.filter((r) => r.name === preference), ...renderers.filter((r) => r.name !== preference)];
|
|
15
|
+
for (const [k, renderer] of ordered.entries()) {
|
|
16
|
+
if (k > 0) replaceCanvas?.();
|
|
17
|
+
const surface = renderer.create(canvas(), options, invalidate);
|
|
18
|
+
if (surface) return surface;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export { createSurface, fades };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","names":[],"sources":["../../src/core/surface.ts"],"sourcesContent":["import type { Frame, RendererName } from './style';\n\n/** How far in from each edge the style's drawing fades out, in CSS pixels. `0` = no fade on that side. */\nexport interface Fade {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** Whether any side fades. */\nexport function fades(fade: Fade): boolean {\n return fade.top > 0 || fade.right > 0 || fade.bottom > 0 || fade.left > 0;\n}\n\n/** A canvas context plus the style renderer drawing into it. */\nexport interface Surface<O> {\n readonly name: RendererName;\n update(options: O): void;\n /**\n * Clear to `backgroundColor` (`null` = transparent) and let the style draw, then fade the\n * style's drawing out towards the edges as `fade` says, keeping the background colour solid.\n * Fades on several sides multiply. Returns `true` while the style has a short-lived effect playing.\n */\n render(frame: Frame, backgroundColor: string | null, fade: Fade): boolean;\n /** Leave the canvas blank. */\n clear(): void;\n destroy(): void;\n}\n\n/**\n * One way to draw a style: a kind of canvas context plus the style's renderer\n * for it. Made with `webglRenderer()` or `canvas2dRenderer()`, each in its own\n * module, so a style only bundles the backends it lists.\n */\nexport interface Renderer<O> {\n readonly name: RendererName;\n /** Set up on `canvas`, or return `null` if this kind of context isn't available. */\n create(canvas: HTMLCanvasElement, options: O, invalidate: () => void): Surface<O> | null;\n}\n\n/**\n * Set up the first renderer that works here, trying them in the style's order,\n * with `preference` (if the style has it) first. `null` if none works.\n *\n * A canvas keeps the first kind of context it hands out, so after a renderer\n * fails, `replaceCanvas` (if given) swaps in a fresh canvas for the next one.\n */\nexport function createSurface<O>(\n canvas: () => HTMLCanvasElement,\n renderers: readonly Renderer<O>[],\n options: O,\n preference: 'auto' | RendererName,\n invalidate: () => void,\n replaceCanvas?: () => void,\n): Surface<O> | null {\n const ordered =\n preference === 'auto'\n ? renderers\n : [...renderers.filter((r) => r.name === preference), ...renderers.filter((r) => r.name !== preference)];\n for (const [k, renderer] of ordered.entries()) {\n if (k > 0) replaceCanvas?.();\n const surface = renderer.create(canvas(), options, invalidate);\n if (surface) return surface;\n }\n return null;\n}\n"],"mappings":";;AAWA,SAAgB,MAAM,MAAqB;CACzC,OAAO,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO;AAC1E;;;;;;;;AAmCA,SAAgB,cACd,QACA,WACA,SACA,YACA,YACA,eACmB;CACnB,MAAM,UACJ,eAAe,SACX,YACA,CAAC,GAAG,UAAU,QAAQ,MAAM,EAAE,SAAS,UAAU,GAAG,GAAG,UAAU,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC;CAC3G,KAAK,MAAM,CAAC,GAAG,aAAa,QAAQ,QAAQ,GAAG;EAC7C,IAAI,IAAI,GAAG,gBAAgB;EAC3B,MAAM,UAAU,SAAS,OAAO,OAAO,GAAG,SAAS,UAAU;EAC7D,IAAI,SAAS,OAAO;CACtB;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const require_surface = require("./surface.cjs");
|
|
2
|
+
const require_color = require("./color.cjs");
|
|
3
|
+
const require_webgl = require("./webgl.cjs");
|
|
4
|
+
// src/core/webgl-renderer.ts
|
|
5
|
+
var WEBGL_ATTRIBUTES = {
|
|
6
|
+
alpha: true,
|
|
7
|
+
premultipliedAlpha: true,
|
|
8
|
+
antialias: false,
|
|
9
|
+
depth: false,
|
|
10
|
+
stencil: false,
|
|
11
|
+
preserveDrawingBuffer: false,
|
|
12
|
+
powerPreference: "low-power"
|
|
13
|
+
};
|
|
14
|
+
var FADE_FRAGMENT = `#version 300 es
|
|
15
|
+
precision highp float;
|
|
16
|
+
uniform float uMode;
|
|
17
|
+
uniform vec4 uFade; // top, right, bottom, left, device px (0 = no fade)
|
|
18
|
+
uniform vec2 uSize; // drawing buffer size, device px
|
|
19
|
+
uniform vec4 uColor; // premultiplied background colour
|
|
20
|
+
out vec4 outColor;
|
|
21
|
+
|
|
22
|
+
float keep(float distance, float length) {
|
|
23
|
+
return length > 0.0 ? clamp(distance / length, 0.0, 1.0) : 1.0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void main() {
|
|
27
|
+
if (uMode > 0.5) {
|
|
28
|
+
outColor = uColor;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
vec2 p = gl_FragCoord.xy; // origin at the bottom left
|
|
32
|
+
float kept = keep(uSize.y - p.y, uFade.x) * keep(uSize.x - p.x, uFade.y) * keep(p.y, uFade.z) * keep(p.x, uFade.w);
|
|
33
|
+
outColor = vec4(0.0, 0.0, 0.0, 1.0 - kept);
|
|
34
|
+
}`;
|
|
35
|
+
/**
|
|
36
|
+
* Draw a style with WebGL2. The engine clears to the background colour, sets
|
|
37
|
+
* the viewport and premultiplied-alpha blending, and recreates the renderer
|
|
38
|
+
* after a lost context is restored.
|
|
39
|
+
*/
|
|
40
|
+
function webglRenderer(createRenderer) {
|
|
41
|
+
return {
|
|
42
|
+
name: "webgl",
|
|
43
|
+
create(canvas, initial, invalidate) {
|
|
44
|
+
const gl = canvas.getContext("webgl2", WEBGL_ATTRIBUTES);
|
|
45
|
+
if (!gl) return null;
|
|
46
|
+
let options = initial;
|
|
47
|
+
let renderer;
|
|
48
|
+
try {
|
|
49
|
+
renderer = createRenderer(gl, options);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn("interactive-bg: WebGL setup failed, trying the next renderer", error);
|
|
52
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
let fadePass = null;
|
|
56
|
+
const onLost = (event) => {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
renderer = null;
|
|
59
|
+
fadePass = null;
|
|
60
|
+
};
|
|
61
|
+
/** Fade the drawing out towards the edges (`fade` in CSS px), then put `background` (RGBA) behind it. */
|
|
62
|
+
const fadeEdges = (fade, dpr, background) => {
|
|
63
|
+
if (!fadePass) {
|
|
64
|
+
const program = require_webgl.createProgram(gl, require_webgl.FULLSCREEN_VERTEX_SHADER, FADE_FRAGMENT);
|
|
65
|
+
if (!program) return;
|
|
66
|
+
fadePass = {
|
|
67
|
+
program,
|
|
68
|
+
uniforms: require_webgl.getUniforms(gl, program, [
|
|
69
|
+
"uMode",
|
|
70
|
+
"uFade",
|
|
71
|
+
"uSize",
|
|
72
|
+
"uColor"
|
|
73
|
+
])
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const { uniforms } = fadePass;
|
|
77
|
+
gl.useProgram(fadePass.program);
|
|
78
|
+
gl.uniform1f(uniforms.uMode, 0);
|
|
79
|
+
gl.uniform4f(uniforms.uFade, fade.top * dpr, fade.right * dpr, fade.bottom * dpr, fade.left * dpr);
|
|
80
|
+
gl.uniform2f(uniforms.uSize, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
81
|
+
gl.blendFunc(gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);
|
|
82
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
83
|
+
const [r, g, b, a] = background;
|
|
84
|
+
if (a > 0) {
|
|
85
|
+
gl.uniform1f(uniforms.uMode, 1);
|
|
86
|
+
gl.uniform4f(uniforms.uColor, r * a, g * a, b * a, a);
|
|
87
|
+
gl.blendFunc(gl.ONE_MINUS_DST_ALPHA, gl.ONE);
|
|
88
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const onRestored = () => {
|
|
92
|
+
renderer = createRenderer(gl, options);
|
|
93
|
+
invalidate();
|
|
94
|
+
};
|
|
95
|
+
canvas.addEventListener("webglcontextlost", onLost);
|
|
96
|
+
canvas.addEventListener("webglcontextrestored", onRestored);
|
|
97
|
+
return {
|
|
98
|
+
name: "webgl",
|
|
99
|
+
update(next) {
|
|
100
|
+
options = next;
|
|
101
|
+
renderer?.update(next);
|
|
102
|
+
},
|
|
103
|
+
render(frame, backgroundColor, fade) {
|
|
104
|
+
if (!renderer) return false;
|
|
105
|
+
const background = require_color.parseColor(backgroundColor);
|
|
106
|
+
const [r, g, b, a] = background;
|
|
107
|
+
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
108
|
+
const fading = require_surface.fades(fade);
|
|
109
|
+
if (fading) gl.clearColor(0, 0, 0, 0);
|
|
110
|
+
else gl.clearColor(r * a, g * a, b * a, a);
|
|
111
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
112
|
+
gl.enable(gl.BLEND);
|
|
113
|
+
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
114
|
+
const busy = renderer.render(frame) === true;
|
|
115
|
+
if (fading) fadeEdges(fade, frame.dpr, background);
|
|
116
|
+
return busy;
|
|
117
|
+
},
|
|
118
|
+
clear() {
|
|
119
|
+
if (!renderer) return;
|
|
120
|
+
gl.clearColor(0, 0, 0, 0);
|
|
121
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
122
|
+
},
|
|
123
|
+
destroy() {
|
|
124
|
+
canvas.removeEventListener("webglcontextlost", onLost);
|
|
125
|
+
canvas.removeEventListener("webglcontextrestored", onRestored);
|
|
126
|
+
renderer?.destroy?.();
|
|
127
|
+
renderer = null;
|
|
128
|
+
if (fadePass) gl.deleteProgram(fadePass.program);
|
|
129
|
+
fadePass = null;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
exports.webglRenderer = webglRenderer;
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=webgl-renderer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webgl-renderer.cjs","names":["createProgram","FULLSCREEN_VERTEX_SHADER","getUniforms","parseColor","fades"],"sources":["../../src/core/webgl-renderer.ts"],"sourcesContent":["import { parseColor, type RGBA } from './color';\nimport type { StyleRenderer } from './style';\nimport { fades, type Fade, type Renderer } from './surface';\nimport { createProgram, FULLSCREEN_VERTEX_SHADER, getUniforms } from './webgl';\n\nconst WEBGL_ATTRIBUTES: WebGLContextAttributes = {\n alpha: true,\n premultipliedAlpha: true,\n antialias: false, // styles antialias in their shaders; MSAA would only cost memory\n depth: false,\n stencil: false,\n preserveDrawingBuffer: false,\n powerPreference: 'low-power',\n};\n\n// The edge fades, drawn after the style. Mode 0 scales what's there by how much of it each side\n// keeps (1 inside, down to 0 at a fading edge, multiplied over the sides) with blend\n// ZERO, ONE_MINUS_SRC_ALPHA. Mode 1 paints the premultiplied background colour behind it, where\n// the drawing is transparent (blend ONE_MINUS_DST_ALPHA, ONE).\nconst FADE_FRAGMENT = `#version 300 es\nprecision highp float;\nuniform float uMode;\nuniform vec4 uFade; // top, right, bottom, left, device px (0 = no fade)\nuniform vec2 uSize; // drawing buffer size, device px\nuniform vec4 uColor; // premultiplied background colour\nout vec4 outColor;\n\nfloat keep(float distance, float length) {\n return length > 0.0 ? clamp(distance / length, 0.0, 1.0) : 1.0;\n}\n\nvoid main() {\n if (uMode > 0.5) {\n outColor = uColor;\n return;\n }\n vec2 p = gl_FragCoord.xy; // origin at the bottom left\n float kept = keep(uSize.y - p.y, uFade.x) * keep(uSize.x - p.x, uFade.y) * keep(p.y, uFade.z) * keep(p.x, uFade.w);\n outColor = vec4(0.0, 0.0, 0.0, 1.0 - kept);\n}`;\n\n/**\n * Draw a style with WebGL2. The engine clears to the background colour, sets\n * the viewport and premultiplied-alpha blending, and recreates the renderer\n * after a lost context is restored.\n */\nexport function webglRenderer<O>(createRenderer: (gl: WebGL2RenderingContext, options: O) => StyleRenderer<O>): Renderer<O> {\n return {\n name: 'webgl',\n create(canvas, initial, invalidate) {\n const gl = canvas.getContext('webgl2', WEBGL_ATTRIBUTES);\n if (!gl) return null;\n\n let options = initial;\n // `null` while the context is lost: its GPU resources are gone.\n let renderer: StyleRenderer<O> | null;\n try {\n renderer = createRenderer(gl, options);\n } catch (error) {\n // E.g. a shader this GPU's driver rejects: let the engine try the next renderer.\n console.warn('interactive-bg: WebGL setup failed, trying the next renderer', error);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n return null;\n }\n // Compiled the first time a fade is drawn, and again after a lost context.\n let fadePass: {\n program: WebGLProgram;\n uniforms: Record<'uMode' | 'uFade' | 'uSize' | 'uColor', WebGLUniformLocation | null>;\n } | null = null;\n\n const onLost = (event: Event): void => {\n event.preventDefault(); // allow the context to be restored\n renderer = null;\n fadePass = null;\n };\n\n /** Fade the drawing out towards the edges (`fade` in CSS px), then put `background` (RGBA) behind it. */\n const fadeEdges = (fade: Fade, dpr: number, background: RGBA): void => {\n if (!fadePass) {\n const program = createProgram(gl, FULLSCREEN_VERTEX_SHADER, FADE_FRAGMENT);\n if (!program) return; // context lost\n fadePass = { program, uniforms: getUniforms(gl, program, ['uMode', 'uFade', 'uSize', 'uColor'] as const) };\n }\n const { uniforms } = fadePass;\n gl.useProgram(fadePass.program);\n gl.uniform1f(uniforms.uMode, 0);\n gl.uniform4f(uniforms.uFade, fade.top * dpr, fade.right * dpr, fade.bottom * dpr, fade.left * dpr);\n gl.uniform2f(uniforms.uSize, gl.drawingBufferWidth, gl.drawingBufferHeight);\n gl.blendFunc(gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n const [r, g, b, a] = background;\n if (a > 0) {\n gl.uniform1f(uniforms.uMode, 1);\n gl.uniform4f(uniforms.uColor, r * a, g * a, b * a, a);\n gl.blendFunc(gl.ONE_MINUS_DST_ALPHA, gl.ONE);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n }\n };\n\n const onRestored = (): void => {\n renderer = createRenderer(gl, options);\n invalidate();\n };\n\n canvas.addEventListener('webglcontextlost', onLost);\n canvas.addEventListener('webglcontextrestored', onRestored);\n\n return {\n name: 'webgl',\n update(next) {\n options = next;\n renderer?.update(next);\n },\n render(frame, backgroundColor, fade) {\n if (!renderer) return false;\n const background = parseColor(backgroundColor);\n const [r, g, b, a] = background;\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n // With a fade, the background goes in last, behind the faded drawing, so it stays solid.\n const fading = fades(fade);\n if (fading) gl.clearColor(0, 0, 0, 0);\n else gl.clearColor(r * a, g * a, b * a, a);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n const busy = renderer.render(frame) === true;\n if (fading) fadeEdges(fade, frame.dpr, background);\n return busy;\n },\n clear() {\n if (!renderer) return;\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n },\n destroy() {\n canvas.removeEventListener('webglcontextlost', onLost);\n canvas.removeEventListener('webglcontextrestored', onRestored);\n renderer?.destroy?.();\n renderer = null;\n if (fadePass) gl.deleteProgram(fadePass.program);\n fadePass = null;\n },\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StyleRenderer } from './style.cjs';
|
|
2
|
+
import { type Renderer } from './surface.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* Draw a style with WebGL2. The engine clears to the background colour, sets
|
|
5
|
+
* the viewport and premultiplied-alpha blending, and recreates the renderer
|
|
6
|
+
* after a lost context is restored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function webglRenderer<O>(createRenderer: (gl: WebGL2RenderingContext, options: O) => StyleRenderer<O>): Renderer<O>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StyleRenderer } from './style.js';
|
|
2
|
+
import { type Renderer } from './surface.js';
|
|
3
|
+
/**
|
|
4
|
+
* Draw a style with WebGL2. The engine clears to the background colour, sets
|
|
5
|
+
* the viewport and premultiplied-alpha blending, and recreates the renderer
|
|
6
|
+
* after a lost context is restored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function webglRenderer<O>(createRenderer: (gl: WebGL2RenderingContext, options: O) => StyleRenderer<O>): Renderer<O>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { fades } from "./surface.js";
|
|
2
|
+
import { parseColor } from "./color.js";
|
|
3
|
+
import { FULLSCREEN_VERTEX_SHADER, createProgram, getUniforms } from "./webgl.js";
|
|
4
|
+
// src/core/webgl-renderer.ts
|
|
5
|
+
var WEBGL_ATTRIBUTES = {
|
|
6
|
+
alpha: true,
|
|
7
|
+
premultipliedAlpha: true,
|
|
8
|
+
antialias: false,
|
|
9
|
+
depth: false,
|
|
10
|
+
stencil: false,
|
|
11
|
+
preserveDrawingBuffer: false,
|
|
12
|
+
powerPreference: "low-power"
|
|
13
|
+
};
|
|
14
|
+
var FADE_FRAGMENT = `#version 300 es
|
|
15
|
+
precision highp float;
|
|
16
|
+
uniform float uMode;
|
|
17
|
+
uniform vec4 uFade; // top, right, bottom, left, device px (0 = no fade)
|
|
18
|
+
uniform vec2 uSize; // drawing buffer size, device px
|
|
19
|
+
uniform vec4 uColor; // premultiplied background colour
|
|
20
|
+
out vec4 outColor;
|
|
21
|
+
|
|
22
|
+
float keep(float distance, float length) {
|
|
23
|
+
return length > 0.0 ? clamp(distance / length, 0.0, 1.0) : 1.0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void main() {
|
|
27
|
+
if (uMode > 0.5) {
|
|
28
|
+
outColor = uColor;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
vec2 p = gl_FragCoord.xy; // origin at the bottom left
|
|
32
|
+
float kept = keep(uSize.y - p.y, uFade.x) * keep(uSize.x - p.x, uFade.y) * keep(p.y, uFade.z) * keep(p.x, uFade.w);
|
|
33
|
+
outColor = vec4(0.0, 0.0, 0.0, 1.0 - kept);
|
|
34
|
+
}`;
|
|
35
|
+
/**
|
|
36
|
+
* Draw a style with WebGL2. The engine clears to the background colour, sets
|
|
37
|
+
* the viewport and premultiplied-alpha blending, and recreates the renderer
|
|
38
|
+
* after a lost context is restored.
|
|
39
|
+
*/
|
|
40
|
+
function webglRenderer(createRenderer) {
|
|
41
|
+
return {
|
|
42
|
+
name: "webgl",
|
|
43
|
+
create(canvas, initial, invalidate) {
|
|
44
|
+
const gl = canvas.getContext("webgl2", WEBGL_ATTRIBUTES);
|
|
45
|
+
if (!gl) return null;
|
|
46
|
+
let options = initial;
|
|
47
|
+
let renderer;
|
|
48
|
+
try {
|
|
49
|
+
renderer = createRenderer(gl, options);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn("interactive-bg: WebGL setup failed, trying the next renderer", error);
|
|
52
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
let fadePass = null;
|
|
56
|
+
const onLost = (event) => {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
renderer = null;
|
|
59
|
+
fadePass = null;
|
|
60
|
+
};
|
|
61
|
+
/** Fade the drawing out towards the edges (`fade` in CSS px), then put `background` (RGBA) behind it. */
|
|
62
|
+
const fadeEdges = (fade, dpr, background) => {
|
|
63
|
+
if (!fadePass) {
|
|
64
|
+
const program = createProgram(gl, FULLSCREEN_VERTEX_SHADER, FADE_FRAGMENT);
|
|
65
|
+
if (!program) return;
|
|
66
|
+
fadePass = {
|
|
67
|
+
program,
|
|
68
|
+
uniforms: getUniforms(gl, program, [
|
|
69
|
+
"uMode",
|
|
70
|
+
"uFade",
|
|
71
|
+
"uSize",
|
|
72
|
+
"uColor"
|
|
73
|
+
])
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const { uniforms } = fadePass;
|
|
77
|
+
gl.useProgram(fadePass.program);
|
|
78
|
+
gl.uniform1f(uniforms.uMode, 0);
|
|
79
|
+
gl.uniform4f(uniforms.uFade, fade.top * dpr, fade.right * dpr, fade.bottom * dpr, fade.left * dpr);
|
|
80
|
+
gl.uniform2f(uniforms.uSize, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
81
|
+
gl.blendFunc(gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);
|
|
82
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
83
|
+
const [r, g, b, a] = background;
|
|
84
|
+
if (a > 0) {
|
|
85
|
+
gl.uniform1f(uniforms.uMode, 1);
|
|
86
|
+
gl.uniform4f(uniforms.uColor, r * a, g * a, b * a, a);
|
|
87
|
+
gl.blendFunc(gl.ONE_MINUS_DST_ALPHA, gl.ONE);
|
|
88
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const onRestored = () => {
|
|
92
|
+
renderer = createRenderer(gl, options);
|
|
93
|
+
invalidate();
|
|
94
|
+
};
|
|
95
|
+
canvas.addEventListener("webglcontextlost", onLost);
|
|
96
|
+
canvas.addEventListener("webglcontextrestored", onRestored);
|
|
97
|
+
return {
|
|
98
|
+
name: "webgl",
|
|
99
|
+
update(next) {
|
|
100
|
+
options = next;
|
|
101
|
+
renderer?.update(next);
|
|
102
|
+
},
|
|
103
|
+
render(frame, backgroundColor, fade) {
|
|
104
|
+
if (!renderer) return false;
|
|
105
|
+
const background = parseColor(backgroundColor);
|
|
106
|
+
const [r, g, b, a] = background;
|
|
107
|
+
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
108
|
+
const fading = fades(fade);
|
|
109
|
+
if (fading) gl.clearColor(0, 0, 0, 0);
|
|
110
|
+
else gl.clearColor(r * a, g * a, b * a, a);
|
|
111
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
112
|
+
gl.enable(gl.BLEND);
|
|
113
|
+
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
114
|
+
const busy = renderer.render(frame) === true;
|
|
115
|
+
if (fading) fadeEdges(fade, frame.dpr, background);
|
|
116
|
+
return busy;
|
|
117
|
+
},
|
|
118
|
+
clear() {
|
|
119
|
+
if (!renderer) return;
|
|
120
|
+
gl.clearColor(0, 0, 0, 0);
|
|
121
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
122
|
+
},
|
|
123
|
+
destroy() {
|
|
124
|
+
canvas.removeEventListener("webglcontextlost", onLost);
|
|
125
|
+
canvas.removeEventListener("webglcontextrestored", onRestored);
|
|
126
|
+
renderer?.destroy?.();
|
|
127
|
+
renderer = null;
|
|
128
|
+
if (fadePass) gl.deleteProgram(fadePass.program);
|
|
129
|
+
fadePass = null;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export { webglRenderer };
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=webgl-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webgl-renderer.js","names":[],"sources":["../../src/core/webgl-renderer.ts"],"sourcesContent":["import { parseColor, type RGBA } from './color';\nimport type { StyleRenderer } from './style';\nimport { fades, type Fade, type Renderer } from './surface';\nimport { createProgram, FULLSCREEN_VERTEX_SHADER, getUniforms } from './webgl';\n\nconst WEBGL_ATTRIBUTES: WebGLContextAttributes = {\n alpha: true,\n premultipliedAlpha: true,\n antialias: false, // styles antialias in their shaders; MSAA would only cost memory\n depth: false,\n stencil: false,\n preserveDrawingBuffer: false,\n powerPreference: 'low-power',\n};\n\n// The edge fades, drawn after the style. Mode 0 scales what's there by how much of it each side\n// keeps (1 inside, down to 0 at a fading edge, multiplied over the sides) with blend\n// ZERO, ONE_MINUS_SRC_ALPHA. Mode 1 paints the premultiplied background colour behind it, where\n// the drawing is transparent (blend ONE_MINUS_DST_ALPHA, ONE).\nconst FADE_FRAGMENT = `#version 300 es\nprecision highp float;\nuniform float uMode;\nuniform vec4 uFade; // top, right, bottom, left, device px (0 = no fade)\nuniform vec2 uSize; // drawing buffer size, device px\nuniform vec4 uColor; // premultiplied background colour\nout vec4 outColor;\n\nfloat keep(float distance, float length) {\n return length > 0.0 ? clamp(distance / length, 0.0, 1.0) : 1.0;\n}\n\nvoid main() {\n if (uMode > 0.5) {\n outColor = uColor;\n return;\n }\n vec2 p = gl_FragCoord.xy; // origin at the bottom left\n float kept = keep(uSize.y - p.y, uFade.x) * keep(uSize.x - p.x, uFade.y) * keep(p.y, uFade.z) * keep(p.x, uFade.w);\n outColor = vec4(0.0, 0.0, 0.0, 1.0 - kept);\n}`;\n\n/**\n * Draw a style with WebGL2. The engine clears to the background colour, sets\n * the viewport and premultiplied-alpha blending, and recreates the renderer\n * after a lost context is restored.\n */\nexport function webglRenderer<O>(createRenderer: (gl: WebGL2RenderingContext, options: O) => StyleRenderer<O>): Renderer<O> {\n return {\n name: 'webgl',\n create(canvas, initial, invalidate) {\n const gl = canvas.getContext('webgl2', WEBGL_ATTRIBUTES);\n if (!gl) return null;\n\n let options = initial;\n // `null` while the context is lost: its GPU resources are gone.\n let renderer: StyleRenderer<O> | null;\n try {\n renderer = createRenderer(gl, options);\n } catch (error) {\n // E.g. a shader this GPU's driver rejects: let the engine try the next renderer.\n console.warn('interactive-bg: WebGL setup failed, trying the next renderer', error);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n return null;\n }\n // Compiled the first time a fade is drawn, and again after a lost context.\n let fadePass: {\n program: WebGLProgram;\n uniforms: Record<'uMode' | 'uFade' | 'uSize' | 'uColor', WebGLUniformLocation | null>;\n } | null = null;\n\n const onLost = (event: Event): void => {\n event.preventDefault(); // allow the context to be restored\n renderer = null;\n fadePass = null;\n };\n\n /** Fade the drawing out towards the edges (`fade` in CSS px), then put `background` (RGBA) behind it. */\n const fadeEdges = (fade: Fade, dpr: number, background: RGBA): void => {\n if (!fadePass) {\n const program = createProgram(gl, FULLSCREEN_VERTEX_SHADER, FADE_FRAGMENT);\n if (!program) return; // context lost\n fadePass = { program, uniforms: getUniforms(gl, program, ['uMode', 'uFade', 'uSize', 'uColor'] as const) };\n }\n const { uniforms } = fadePass;\n gl.useProgram(fadePass.program);\n gl.uniform1f(uniforms.uMode, 0);\n gl.uniform4f(uniforms.uFade, fade.top * dpr, fade.right * dpr, fade.bottom * dpr, fade.left * dpr);\n gl.uniform2f(uniforms.uSize, gl.drawingBufferWidth, gl.drawingBufferHeight);\n gl.blendFunc(gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n const [r, g, b, a] = background;\n if (a > 0) {\n gl.uniform1f(uniforms.uMode, 1);\n gl.uniform4f(uniforms.uColor, r * a, g * a, b * a, a);\n gl.blendFunc(gl.ONE_MINUS_DST_ALPHA, gl.ONE);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n }\n };\n\n const onRestored = (): void => {\n renderer = createRenderer(gl, options);\n invalidate();\n };\n\n canvas.addEventListener('webglcontextlost', onLost);\n canvas.addEventListener('webglcontextrestored', onRestored);\n\n return {\n name: 'webgl',\n update(next) {\n options = next;\n renderer?.update(next);\n },\n render(frame, backgroundColor, fade) {\n if (!renderer) return false;\n const background = parseColor(backgroundColor);\n const [r, g, b, a] = background;\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n // With a fade, the background goes in last, behind the faded drawing, so it stays solid.\n const fading = fades(fade);\n if (fading) gl.clearColor(0, 0, 0, 0);\n else gl.clearColor(r * a, g * a, b * a, a);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n const busy = renderer.render(frame) === true;\n if (fading) fadeEdges(fade, frame.dpr, background);\n return busy;\n },\n clear() {\n if (!renderer) return;\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n },\n destroy() {\n canvas.removeEventListener('webglcontextlost', onLost);\n canvas.removeEventListener('webglcontextrestored', onRestored);\n renderer?.destroy?.();\n renderer = null;\n if (fadePass) gl.deleteProgram(fadePass.program);\n fadePass = null;\n },\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/core/webgl.ts
|
|
2
|
+
/**
|
|
3
|
+
* Vertex shader for one triangle that covers the whole viewport, drawn with
|
|
4
|
+
* `gl.drawArrays(gl.TRIANGLES, 0, 3)` and no vertex buffers. Pair it with a
|
|
5
|
+
* fragment shader that works per pixel from `gl_FragCoord`.
|
|
6
|
+
*/
|
|
7
|
+
var FULLSCREEN_VERTEX_SHADER = `#version 300 es
|
|
8
|
+
void main() {
|
|
9
|
+
vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
10
|
+
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
|
11
|
+
}`;
|
|
12
|
+
/**
|
|
13
|
+
* Compile and link a shader program. Returns `null` if the context is lost, and
|
|
14
|
+
* throws with the driver's message if a shader doesn't compile or link (the
|
|
15
|
+
* engine then falls back to the style's next renderer). Nothing is left behind
|
|
16
|
+
* on failure.
|
|
17
|
+
*/
|
|
18
|
+
function createProgram(gl, vertexSource, fragmentSource) {
|
|
19
|
+
const program = gl.createProgram();
|
|
20
|
+
let vertex = null;
|
|
21
|
+
let fragment = null;
|
|
22
|
+
try {
|
|
23
|
+
vertex = compile(gl, gl.VERTEX_SHADER, vertexSource);
|
|
24
|
+
fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
25
|
+
if (!program || !vertex || !fragment) {
|
|
26
|
+
gl.deleteProgram(program);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
gl.attachShader(program, vertex);
|
|
30
|
+
gl.attachShader(program, fragment);
|
|
31
|
+
gl.linkProgram(program);
|
|
32
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS) && !gl.isContextLost()) throw new Error(`interactive-bg: shader link error: ${gl.getProgramInfoLog(program)}`);
|
|
33
|
+
return program;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
gl.deleteProgram(program);
|
|
36
|
+
throw error;
|
|
37
|
+
} finally {
|
|
38
|
+
gl.deleteShader(vertex);
|
|
39
|
+
gl.deleteShader(fragment);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Look up uniform locations by name. */
|
|
43
|
+
function getUniforms(gl, program, names) {
|
|
44
|
+
return Object.fromEntries(names.map((name) => [name, gl.getUniformLocation(program, name)]));
|
|
45
|
+
}
|
|
46
|
+
function compile(gl, type, source) {
|
|
47
|
+
const shader = gl.createShader(type);
|
|
48
|
+
if (!shader) return null;
|
|
49
|
+
gl.shaderSource(shader, source);
|
|
50
|
+
gl.compileShader(shader);
|
|
51
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {
|
|
52
|
+
const log = gl.getShaderInfoLog(shader);
|
|
53
|
+
gl.deleteShader(shader);
|
|
54
|
+
throw new Error(`interactive-bg: shader error: ${log}`);
|
|
55
|
+
}
|
|
56
|
+
return shader;
|
|
57
|
+
}
|
|
58
|
+
exports.FULLSCREEN_VERTEX_SHADER = FULLSCREEN_VERTEX_SHADER;
|
|
59
|
+
exports.createProgram = createProgram;
|
|
60
|
+
exports.getUniforms = getUniforms;
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=webgl.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webgl.cjs","names":[],"sources":["../../src/core/webgl.ts"],"sourcesContent":["/**\n * Vertex shader for one triangle that covers the whole viewport, drawn with\n * `gl.drawArrays(gl.TRIANGLES, 0, 3)` and no vertex buffers. Pair it with a\n * fragment shader that works per pixel from `gl_FragCoord`.\n */\nexport const FULLSCREEN_VERTEX_SHADER = `#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}`;\n\n/**\n * Compile and link a shader program. Returns `null` if the context is lost, and\n * throws with the driver's message if a shader doesn't compile or link (the\n * engine then falls back to the style's next renderer). Nothing is left behind\n * on failure.\n */\nexport function createProgram(\n gl: WebGL2RenderingContext,\n vertexSource: string,\n fragmentSource: string,\n): WebGLProgram | null {\n const program = gl.createProgram();\n let vertex: WebGLShader | null = null;\n let fragment: WebGLShader | null = null;\n try {\n vertex = compile(gl, gl.VERTEX_SHADER, vertexSource);\n fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);\n if (!program || !vertex || !fragment) {\n gl.deleteProgram(program);\n return null;\n }\n gl.attachShader(program, vertex);\n gl.attachShader(program, fragment);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS) && !gl.isContextLost()) {\n throw new Error(`interactive-bg: shader link error: ${gl.getProgramInfoLog(program)}`);\n }\n return program;\n } catch (error) {\n gl.deleteProgram(program);\n throw error;\n } finally {\n // Linked programs keep their own copy; the shader objects aren't needed either way.\n gl.deleteShader(vertex);\n gl.deleteShader(fragment);\n }\n}\n\n/** Look up uniform locations by name. */\nexport function getUniforms<N extends string>(\n gl: WebGL2RenderingContext,\n program: WebGLProgram,\n names: readonly N[],\n): Record<N, WebGLUniformLocation | null> {\n return Object.fromEntries(names.map((name) => [name, gl.getUniformLocation(program, name)])) as Record<\n N,\n WebGLUniformLocation | null\n >;\n}\n\nfunction compile(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {\n const log = gl.getShaderInfoLog(shader);\n gl.deleteShader(shader);\n throw new Error(`interactive-bg: shader error: ${log}`);\n }\n return shader;\n}\n"],"mappings":";;;;;;AAKA,IAAa,2BAA2B;;;;;;;;;;;AAYxC,SAAgB,cACd,IACA,cACA,gBACqB;CACrB,MAAM,UAAU,GAAG,cAAc;CACjC,IAAI,SAA6B;CACjC,IAAI,WAA+B;CACnC,IAAI;EACF,SAAS,QAAQ,IAAI,GAAG,eAAe,YAAY;EACnD,WAAW,QAAQ,IAAI,GAAG,iBAAiB,cAAc;EACzD,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,UAAU;GACpC,GAAG,cAAc,OAAO;GACxB,OAAO;EACT;EACA,GAAG,aAAa,SAAS,MAAM;EAC/B,GAAG,aAAa,SAAS,QAAQ;EACjC,GAAG,YAAY,OAAO;EACtB,IAAI,CAAC,GAAG,oBAAoB,SAAS,GAAG,WAAW,KAAK,CAAC,GAAG,cAAc,GACxE,MAAM,IAAI,MAAM,sCAAsC,GAAG,kBAAkB,OAAO,GAAG;EAEvF,OAAO;CACT,SAAS,OAAO;EACd,GAAG,cAAc,OAAO;EACxB,MAAM;CACR,UAAU;EAER,GAAG,aAAa,MAAM;EACtB,GAAG,aAAa,QAAQ;CAC1B;AACF;;AAGA,SAAgB,YACd,IACA,SACA,OACwC;CACxC,OAAO,OAAO,YAAY,MAAM,KAAK,SAAS,CAAC,MAAM,GAAG,mBAAmB,SAAS,IAAI,CAAC,CAAC,CAAC;AAI7F;AAEA,SAAS,QAAQ,IAA4B,MAAc,QAAoC;CAC7F,MAAM,SAAS,GAAG,aAAa,IAAI;CACnC,IAAI,CAAC,QAAQ,OAAO;CACpB,GAAG,aAAa,QAAQ,MAAM;CAC9B,GAAG,cAAc,MAAM;CACvB,IAAI,CAAC,GAAG,mBAAmB,QAAQ,GAAG,cAAc,KAAK,CAAC,GAAG,cAAc,GAAG;EAC5E,MAAM,MAAM,GAAG,iBAAiB,MAAM;EACtC,GAAG,aAAa,MAAM;EACtB,MAAM,IAAI,MAAM,iCAAiC,KAAK;CACxD;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vertex shader for one triangle that covers the whole viewport, drawn with
|
|
3
|
+
* `gl.drawArrays(gl.TRIANGLES, 0, 3)` and no vertex buffers. Pair it with a
|
|
4
|
+
* fragment shader that works per pixel from `gl_FragCoord`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FULLSCREEN_VERTEX_SHADER = "#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}";
|
|
7
|
+
/**
|
|
8
|
+
* Compile and link a shader program. Returns `null` if the context is lost, and
|
|
9
|
+
* throws with the driver's message if a shader doesn't compile or link (the
|
|
10
|
+
* engine then falls back to the style's next renderer). Nothing is left behind
|
|
11
|
+
* on failure.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | null;
|
|
14
|
+
/** Look up uniform locations by name. */
|
|
15
|
+
export declare function getUniforms<N extends string>(gl: WebGL2RenderingContext, program: WebGLProgram, names: readonly N[]): Record<N, WebGLUniformLocation | null>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vertex shader for one triangle that covers the whole viewport, drawn with
|
|
3
|
+
* `gl.drawArrays(gl.TRIANGLES, 0, 3)` and no vertex buffers. Pair it with a
|
|
4
|
+
* fragment shader that works per pixel from `gl_FragCoord`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FULLSCREEN_VERTEX_SHADER = "#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);\n}";
|
|
7
|
+
/**
|
|
8
|
+
* Compile and link a shader program. Returns `null` if the context is lost, and
|
|
9
|
+
* throws with the driver's message if a shader doesn't compile or link (the
|
|
10
|
+
* engine then falls back to the style's next renderer). Nothing is left behind
|
|
11
|
+
* on failure.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | null;
|
|
14
|
+
/** Look up uniform locations by name. */
|
|
15
|
+
export declare function getUniforms<N extends string>(gl: WebGL2RenderingContext, program: WebGLProgram, names: readonly N[]): Record<N, WebGLUniformLocation | null>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/core/webgl.ts
|
|
2
|
+
/**
|
|
3
|
+
* Vertex shader for one triangle that covers the whole viewport, drawn with
|
|
4
|
+
* `gl.drawArrays(gl.TRIANGLES, 0, 3)` and no vertex buffers. Pair it with a
|
|
5
|
+
* fragment shader that works per pixel from `gl_FragCoord`.
|
|
6
|
+
*/
|
|
7
|
+
var FULLSCREEN_VERTEX_SHADER = `#version 300 es
|
|
8
|
+
void main() {
|
|
9
|
+
vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
10
|
+
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
|
11
|
+
}`;
|
|
12
|
+
/**
|
|
13
|
+
* Compile and link a shader program. Returns `null` if the context is lost, and
|
|
14
|
+
* throws with the driver's message if a shader doesn't compile or link (the
|
|
15
|
+
* engine then falls back to the style's next renderer). Nothing is left behind
|
|
16
|
+
* on failure.
|
|
17
|
+
*/
|
|
18
|
+
function createProgram(gl, vertexSource, fragmentSource) {
|
|
19
|
+
const program = gl.createProgram();
|
|
20
|
+
let vertex = null;
|
|
21
|
+
let fragment = null;
|
|
22
|
+
try {
|
|
23
|
+
vertex = compile(gl, gl.VERTEX_SHADER, vertexSource);
|
|
24
|
+
fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
25
|
+
if (!program || !vertex || !fragment) {
|
|
26
|
+
gl.deleteProgram(program);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
gl.attachShader(program, vertex);
|
|
30
|
+
gl.attachShader(program, fragment);
|
|
31
|
+
gl.linkProgram(program);
|
|
32
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS) && !gl.isContextLost()) throw new Error(`interactive-bg: shader link error: ${gl.getProgramInfoLog(program)}`);
|
|
33
|
+
return program;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
gl.deleteProgram(program);
|
|
36
|
+
throw error;
|
|
37
|
+
} finally {
|
|
38
|
+
gl.deleteShader(vertex);
|
|
39
|
+
gl.deleteShader(fragment);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Look up uniform locations by name. */
|
|
43
|
+
function getUniforms(gl, program, names) {
|
|
44
|
+
return Object.fromEntries(names.map((name) => [name, gl.getUniformLocation(program, name)]));
|
|
45
|
+
}
|
|
46
|
+
function compile(gl, type, source) {
|
|
47
|
+
const shader = gl.createShader(type);
|
|
48
|
+
if (!shader) return null;
|
|
49
|
+
gl.shaderSource(shader, source);
|
|
50
|
+
gl.compileShader(shader);
|
|
51
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS) && !gl.isContextLost()) {
|
|
52
|
+
const log = gl.getShaderInfoLog(shader);
|
|
53
|
+
gl.deleteShader(shader);
|
|
54
|
+
throw new Error(`interactive-bg: shader error: ${log}`);
|
|
55
|
+
}
|
|
56
|
+
return shader;
|
|
57
|
+
}
|
|
58
|
+
export { FULLSCREEN_VERTEX_SHADER, createProgram, getUniforms };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=webgl.js.map
|