@oh-just-another/renderer-canvas 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +36 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/canvas-target.d.ts +83 -0
- package/dist/canvas-target.d.ts.map +1 -0
- package/dist/canvas-target.js +208 -0
- package/dist/canvas-target.js.map +1 -0
- package/dist/canvas-text-shaper.d.ts +11 -0
- package/dist/canvas-text-shaper.d.ts.map +1 -0
- package/dist/canvas-text-shaper.js +56 -0
- package/dist/canvas-text-shaper.js.map +1 -0
- package/dist/constants.d.ts +52 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +52 -0
- package/dist/constants.js.map +1 -0
- package/dist/hi-dpi.d.ts +26 -0
- package/dist/hi-dpi.d.ts.map +1 -0
- package/dist/hi-dpi.js +62 -0
- package/dist/hi-dpi.js.map +1 -0
- package/dist/image-source.d.ts +3 -0
- package/dist/image-source.d.ts.map +1 -0
- package/dist/image-source.js +65 -0
- package/dist/image-source.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/layered-canvas.d.ts +46 -0
- package/dist/layered-canvas.d.ts.map +1 -0
- package/dist/layered-canvas.js +95 -0
- package/dist/layered-canvas.js.map +1 -0
- package/dist/layered-surface.d.ts +75 -0
- package/dist/layered-surface.d.ts.map +1 -0
- package/dist/layered-surface.js +262 -0
- package/dist/layered-surface.js.map +1 -0
- package/dist/offscreen.d.ts +30 -0
- package/dist/offscreen.d.ts.map +1 -0
- package/dist/offscreen.js +50 -0
- package/dist/offscreen.js.map +1 -0
- package/dist/recording-target.d.ts +203 -0
- package/dist/recording-target.d.ts.map +1 -0
- package/dist/recording-target.js +260 -0
- package/dist/recording-target.js.map +1 -0
- package/dist/render-worker.d.ts +2 -0
- package/dist/render-worker.d.ts.map +1 -0
- package/dist/render-worker.js +112 -0
- package/dist/render-worker.js.map +1 -0
- package/dist/tile-compositor.d.ts +44 -0
- package/dist/tile-compositor.d.ts.map +1 -0
- package/dist/tile-compositor.js +110 -0
- package/dist/tile-compositor.js.map +1 -0
- package/dist/webgl2-color.d.ts +13 -0
- package/dist/webgl2-color.d.ts.map +1 -0
- package/dist/webgl2-color.js +26 -0
- package/dist/webgl2-color.js.map +1 -0
- package/dist/webgl2-curve.d.ts +46 -0
- package/dist/webgl2-curve.d.ts.map +1 -0
- package/dist/webgl2-curve.js +162 -0
- package/dist/webgl2-curve.js.map +1 -0
- package/dist/webgl2-ellipse.d.ts +52 -0
- package/dist/webgl2-ellipse.d.ts.map +1 -0
- package/dist/webgl2-ellipse.js +171 -0
- package/dist/webgl2-ellipse.js.map +1 -0
- package/dist/webgl2-msdf-text.d.ts +84 -0
- package/dist/webgl2-msdf-text.d.ts.map +1 -0
- package/dist/webgl2-msdf-text.js +306 -0
- package/dist/webgl2-msdf-text.js.map +1 -0
- package/dist/webgl2-stroke.d.ts +52 -0
- package/dist/webgl2-stroke.d.ts.map +1 -0
- package/dist/webgl2-stroke.js +318 -0
- package/dist/webgl2-stroke.js.map +1 -0
- package/dist/webgl2-target.d.ts +311 -0
- package/dist/webgl2-target.d.ts.map +1 -0
- package/dist/webgl2-target.js +1368 -0
- package/dist/webgl2-target.js.map +1 -0
- package/dist/webgpu-detect.d.ts +30 -0
- package/dist/webgpu-detect.d.ts.map +1 -0
- package/dist/webgpu-detect.js +74 -0
- package/dist/webgpu-detect.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunable thresholds for the canvas backend.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Element count above which a host that has wired up `WorkerPool` and
|
|
6
|
+
* `transferCanvasToWorker` should prefer the worker-rendering path.
|
|
7
|
+
* Below this number the per-postMessage overhead dominates and a
|
|
8
|
+
* main-thread render is faster.
|
|
9
|
+
*
|
|
10
|
+
* At 50k shapes a full-scene main-thread render at low zoom starts to
|
|
11
|
+
* drop frames; below 50k the main-thread path stays within 16 ms via
|
|
12
|
+
* viewport culling and ElementCache.
|
|
13
|
+
*/
|
|
14
|
+
export declare const LARGE_SCENE_WORKER_THRESHOLD = 50000;
|
|
15
|
+
/**
|
|
16
|
+
* LRU cap on `WebGL2Target.textBitmaps` — the OffscreenCanvas cache
|
|
17
|
+
* used by the fallback text path (when no MSDF shaper is available:
|
|
18
|
+
* Safari without WASM, older modules). Key is `text|font|color`; each
|
|
19
|
+
* combination maps to one OffscreenCanvas plus one GPU texture after
|
|
20
|
+
* the first drawImage.
|
|
21
|
+
*
|
|
22
|
+
* Without a cap the Map grows unbounded: 1000 text mutations (renaming
|
|
23
|
+
* labels, comments, counter numbers) produce 1000 live GPU textures
|
|
24
|
+
* plus 1000 OffscreenCanvases on the JS heap, leading to VRAM pressure
|
|
25
|
+
* over a long session.
|
|
26
|
+
*
|
|
27
|
+
* 256 entries — a typical 200-shape scene uses 50-150 unique texts; a
|
|
28
|
+
* 1.5-3x margin covers editing without thrashing. On overflow the LRU
|
|
29
|
+
* evicts the least-recently-used entry: `Map.delete` plus
|
|
30
|
+
* `gl.deleteTexture` for the associated WebGL texture (deterministic
|
|
31
|
+
* VRAM release).
|
|
32
|
+
*
|
|
33
|
+
* Hosts with an MSDF shaper (WasmTextShaper) never use this path —
|
|
34
|
+
* text renders through an atlas, without an OffscreenCanvas round-trip.
|
|
35
|
+
*/
|
|
36
|
+
export declare const WEBGL2_TEXT_BITMAP_CACHE_CAP = 256;
|
|
37
|
+
/**
|
|
38
|
+
* LRU cap on `WebGL2Target.textures` — image-source to WebGLTexture
|
|
39
|
+
* cache used by `drawImage`. Each entry holds a GPU texture
|
|
40
|
+
* (`width × height × 4` bytes VRAM).
|
|
41
|
+
*
|
|
42
|
+
* A cap plus an explicit `gl.deleteTexture` on evict makes the release
|
|
43
|
+
* deterministic.
|
|
44
|
+
*
|
|
45
|
+
* 64 — a typical scene rarely has more than 10-20 unique images at
|
|
46
|
+
* once; a 3-6x margin covers intensive editing without thrashing. On
|
|
47
|
+
* overflow the coldest image textures are unloaded; the next drawImage
|
|
48
|
+
* of the same image source re-uploads via `gl.texImage2D` (extra frame
|
|
49
|
+
* cost).
|
|
50
|
+
*/
|
|
51
|
+
export declare const WEBGL2_IMAGE_TEXTURE_CACHE_CAP = 64;
|
|
52
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,KAAK,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunable thresholds for the canvas backend.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Element count above which a host that has wired up `WorkerPool` and
|
|
6
|
+
* `transferCanvasToWorker` should prefer the worker-rendering path.
|
|
7
|
+
* Below this number the per-postMessage overhead dominates and a
|
|
8
|
+
* main-thread render is faster.
|
|
9
|
+
*
|
|
10
|
+
* At 50k shapes a full-scene main-thread render at low zoom starts to
|
|
11
|
+
* drop frames; below 50k the main-thread path stays within 16 ms via
|
|
12
|
+
* viewport culling and ElementCache.
|
|
13
|
+
*/
|
|
14
|
+
export const LARGE_SCENE_WORKER_THRESHOLD = 50_000;
|
|
15
|
+
/**
|
|
16
|
+
* LRU cap on `WebGL2Target.textBitmaps` — the OffscreenCanvas cache
|
|
17
|
+
* used by the fallback text path (when no MSDF shaper is available:
|
|
18
|
+
* Safari without WASM, older modules). Key is `text|font|color`; each
|
|
19
|
+
* combination maps to one OffscreenCanvas plus one GPU texture after
|
|
20
|
+
* the first drawImage.
|
|
21
|
+
*
|
|
22
|
+
* Without a cap the Map grows unbounded: 1000 text mutations (renaming
|
|
23
|
+
* labels, comments, counter numbers) produce 1000 live GPU textures
|
|
24
|
+
* plus 1000 OffscreenCanvases on the JS heap, leading to VRAM pressure
|
|
25
|
+
* over a long session.
|
|
26
|
+
*
|
|
27
|
+
* 256 entries — a typical 200-shape scene uses 50-150 unique texts; a
|
|
28
|
+
* 1.5-3x margin covers editing without thrashing. On overflow the LRU
|
|
29
|
+
* evicts the least-recently-used entry: `Map.delete` plus
|
|
30
|
+
* `gl.deleteTexture` for the associated WebGL texture (deterministic
|
|
31
|
+
* VRAM release).
|
|
32
|
+
*
|
|
33
|
+
* Hosts with an MSDF shaper (WasmTextShaper) never use this path —
|
|
34
|
+
* text renders through an atlas, without an OffscreenCanvas round-trip.
|
|
35
|
+
*/
|
|
36
|
+
export const WEBGL2_TEXT_BITMAP_CACHE_CAP = 256;
|
|
37
|
+
/**
|
|
38
|
+
* LRU cap on `WebGL2Target.textures` — image-source to WebGLTexture
|
|
39
|
+
* cache used by `drawImage`. Each entry holds a GPU texture
|
|
40
|
+
* (`width × height × 4` bytes VRAM).
|
|
41
|
+
*
|
|
42
|
+
* A cap plus an explicit `gl.deleteTexture` on evict makes the release
|
|
43
|
+
* deterministic.
|
|
44
|
+
*
|
|
45
|
+
* 64 — a typical scene rarely has more than 10-20 unique images at
|
|
46
|
+
* once; a 3-6x margin covers intensive editing without thrashing. On
|
|
47
|
+
* overflow the coldest image textures are unloaded; the next drawImage
|
|
48
|
+
* of the same image source re-uploads via `gl.texImage2D` (extra frame
|
|
49
|
+
* cost).
|
|
50
|
+
*/
|
|
51
|
+
export const WEBGL2_IMAGE_TEXTURE_CACHE_CAP = 64;
|
|
52
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC"}
|
package/dist/hi-dpi.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configures a `<canvas>` for hi-DPI rendering. Sets the bitmap size to
|
|
3
|
+
* `width * dpr` × `height * dpr`, the CSS size to `width` × `height`, and the
|
|
4
|
+
* context transform to `scale(dpr, dpr)` so subsequent draw calls operate in
|
|
5
|
+
* CSS pixels. Returns the DPR used so callers can recompute on monitor changes.
|
|
6
|
+
*
|
|
7
|
+
* IDEMPOTENT: assigning `canvas.width` / `canvas.height` resets both
|
|
8
|
+
* the bitmap AND the context transform — even when the new value equals
|
|
9
|
+
* the old. Hosts call this from a `ResizeObserver` that may fire with
|
|
10
|
+
* the same size on attach; without the guard each tick would wipe the
|
|
11
|
+
* existing canvas content. We early-return when bitmap + CSS size +
|
|
12
|
+
* DPR are already exactly right.
|
|
13
|
+
*/
|
|
14
|
+
export declare const setupHiDpi: (canvas: HTMLCanvasElement, width: number, height: number, dpr?: number) => number;
|
|
15
|
+
/**
|
|
16
|
+
* Hi-DPI sizing for canvases that will host a non-2D context (WebGL2,
|
|
17
|
+
* WebGPU). Same bitmap + CSS dimensions as `setupHiDpi`, but skips
|
|
18
|
+
* the `getContext("2d")` call — `HTMLCanvasElement` only ever yields
|
|
19
|
+
* one *kind* of context, so touching `2d` on a canvas destined for
|
|
20
|
+
* `webgl2` poisons the slot and every subsequent `getContext("webgl2")`
|
|
21
|
+
* returns null. Use this for the WebGL2 main canvas in
|
|
22
|
+
* `WebGL2LayeredSurface`; the WebGL2 viewport is set inside
|
|
23
|
+
* `WebGL2Target` after the GL context is obtained.
|
|
24
|
+
*/
|
|
25
|
+
export declare const setupHiDpiNoContext: (canvas: HTMLCanvasElement, width: number, height: number, dpr?: number) => number;
|
|
26
|
+
//# sourceMappingURL=hi-dpi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hi-dpi.d.ts","sourceRoot":"","sources":["../src/hi-dpi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GACrB,QAAQ,iBAAiB,EACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAK,MAAqC,KACzC,MAqBF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,iBAAiB,EACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,MAAK,MAAqC,KACzC,MAkBF,CAAC"}
|
package/dist/hi-dpi.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configures a `<canvas>` for hi-DPI rendering. Sets the bitmap size to
|
|
3
|
+
* `width * dpr` × `height * dpr`, the CSS size to `width` × `height`, and the
|
|
4
|
+
* context transform to `scale(dpr, dpr)` so subsequent draw calls operate in
|
|
5
|
+
* CSS pixels. Returns the DPR used so callers can recompute on monitor changes.
|
|
6
|
+
*
|
|
7
|
+
* IDEMPOTENT: assigning `canvas.width` / `canvas.height` resets both
|
|
8
|
+
* the bitmap AND the context transform — even when the new value equals
|
|
9
|
+
* the old. Hosts call this from a `ResizeObserver` that may fire with
|
|
10
|
+
* the same size on attach; without the guard each tick would wipe the
|
|
11
|
+
* existing canvas content. We early-return when bitmap + CSS size +
|
|
12
|
+
* DPR are already exactly right.
|
|
13
|
+
*/
|
|
14
|
+
export const setupHiDpi = (canvas, width, height, dpr = window.devicePixelRatio || 1) => {
|
|
15
|
+
const targetW = Math.round(width * dpr);
|
|
16
|
+
const targetH = Math.round(height * dpr);
|
|
17
|
+
const cssW = `${width}px`;
|
|
18
|
+
const cssH = `${height}px`;
|
|
19
|
+
if (canvas.width === targetW &&
|
|
20
|
+
canvas.height === targetH &&
|
|
21
|
+
canvas.style.width === cssW &&
|
|
22
|
+
canvas.style.height === cssH) {
|
|
23
|
+
return dpr;
|
|
24
|
+
}
|
|
25
|
+
canvas.width = targetW;
|
|
26
|
+
canvas.height = targetH;
|
|
27
|
+
canvas.style.width = cssW;
|
|
28
|
+
canvas.style.height = cssH;
|
|
29
|
+
const ctx = canvas.getContext("2d");
|
|
30
|
+
if (!ctx)
|
|
31
|
+
throw new Error("Failed to obtain 2D context");
|
|
32
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
33
|
+
return dpr;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Hi-DPI sizing for canvases that will host a non-2D context (WebGL2,
|
|
37
|
+
* WebGPU). Same bitmap + CSS dimensions as `setupHiDpi`, but skips
|
|
38
|
+
* the `getContext("2d")` call — `HTMLCanvasElement` only ever yields
|
|
39
|
+
* one *kind* of context, so touching `2d` on a canvas destined for
|
|
40
|
+
* `webgl2` poisons the slot and every subsequent `getContext("webgl2")`
|
|
41
|
+
* returns null. Use this for the WebGL2 main canvas in
|
|
42
|
+
* `WebGL2LayeredSurface`; the WebGL2 viewport is set inside
|
|
43
|
+
* `WebGL2Target` after the GL context is obtained.
|
|
44
|
+
*/
|
|
45
|
+
export const setupHiDpiNoContext = (canvas, width, height, dpr = window.devicePixelRatio || 1) => {
|
|
46
|
+
const targetW = Math.round(width * dpr);
|
|
47
|
+
const targetH = Math.round(height * dpr);
|
|
48
|
+
const cssW = `${width}px`;
|
|
49
|
+
const cssH = `${height}px`;
|
|
50
|
+
if (canvas.width === targetW &&
|
|
51
|
+
canvas.height === targetH &&
|
|
52
|
+
canvas.style.width === cssW &&
|
|
53
|
+
canvas.style.height === cssH) {
|
|
54
|
+
return dpr;
|
|
55
|
+
}
|
|
56
|
+
canvas.width = targetW;
|
|
57
|
+
canvas.height = targetH;
|
|
58
|
+
canvas.style.width = cssW;
|
|
59
|
+
canvas.style.height = cssH;
|
|
60
|
+
return dpr;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=hi-dpi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hi-dpi.js","sourceRoot":"","sources":["../src/hi-dpi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,MAAyB,EACzB,KAAa,EACb,MAAc,EACd,MAAc,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAClC,EAAE;IACV,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC;IAC3B,IACE,MAAM,CAAC,KAAK,KAAK,OAAO;QACxB,MAAM,CAAC,MAAM,KAAK,OAAO;QACzB,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,EAC5B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAyB,EACzB,KAAa,EACb,MAAc,EACd,MAAc,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAClC,EAAE;IACV,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC;IAC3B,IACE,MAAM,CAAC,KAAK,KAAK,OAAO;QACxB,MAAM,CAAC,MAAM,KAAK,OAAO;QACzB,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,IAAI,EAC5B,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,GAAG,CAAC;AACb,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-source.d.ts","sourceRoot":"","sources":["../src/image-source.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,iBAU/D,CAAC;AAUF,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,IAoBjD,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime guard: is `value` an actual drawable image source that
|
|
3
|
+
* `ctx.drawImage` / `gl.texImage2D` will accept?
|
|
4
|
+
*
|
|
5
|
+
* Needed because a deserialized scene can carry a **garbage**
|
|
6
|
+
* `metadata.image`: a live `<img>` DOM element serialises to `{}`
|
|
7
|
+
* via `JSON.stringify`, so a scene restored from localStorage has
|
|
8
|
+
* `metadata.image === {}` — a truthy object that passes a naive
|
|
9
|
+
* `typeof === "object"` check but throws inside `drawImage`
|
|
10
|
+
* ("provided value is not of type …") / `texImage2D` ("overload
|
|
11
|
+
* resolution failed").
|
|
12
|
+
*
|
|
13
|
+
* The check is environment-safe: each constructor is probed for
|
|
14
|
+
* existence first (workers / SSR / older browsers may lack some),
|
|
15
|
+
* so it never throws on a missing global. A bare `{}` matches none
|
|
16
|
+
* of them and is rejected.
|
|
17
|
+
*/
|
|
18
|
+
const DRAWABLE_CTOR_NAMES = [
|
|
19
|
+
"HTMLImageElement",
|
|
20
|
+
"HTMLCanvasElement",
|
|
21
|
+
"HTMLVideoElement",
|
|
22
|
+
"ImageBitmap",
|
|
23
|
+
"OffscreenCanvas",
|
|
24
|
+
"SVGImageElement",
|
|
25
|
+
"VideoFrame",
|
|
26
|
+
];
|
|
27
|
+
export const isDrawableImageSource = (value) => {
|
|
28
|
+
if (typeof value !== "object" || value === null)
|
|
29
|
+
return false;
|
|
30
|
+
const g = globalThis;
|
|
31
|
+
for (const name of DRAWABLE_CTOR_NAMES) {
|
|
32
|
+
const ctor = g[name];
|
|
33
|
+
if (typeof ctor === "function" && value instanceof ctor) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Warn (once per distinct kind) when an image draw is skipped because
|
|
41
|
+
* the handle isn't drawable. Throttled by a module-level `Set` so a
|
|
42
|
+
* per-frame render loop doesn't spam the console — but the host still
|
|
43
|
+
* sees that an image failed to render and the likely cause.
|
|
44
|
+
*/
|
|
45
|
+
const warnedImageKinds = new Set();
|
|
46
|
+
export const warnSkippedImage = (value) => {
|
|
47
|
+
if (typeof console === "undefined")
|
|
48
|
+
return;
|
|
49
|
+
const kind = typeof value === "string"
|
|
50
|
+
? value.startsWith("blob:")
|
|
51
|
+
? "dead-blob-url"
|
|
52
|
+
: "string-src"
|
|
53
|
+
: value === null || value === undefined
|
|
54
|
+
? "empty"
|
|
55
|
+
: "stale-object"; // e.g. a {} from a serialised <img>
|
|
56
|
+
if (warnedImageKinds.has(kind))
|
|
57
|
+
return;
|
|
58
|
+
warnedImageKinds.add(kind);
|
|
59
|
+
console.warn(`[renderer] skipped a non-drawable image source (kind: ${kind}). ` +
|
|
60
|
+
"The shape's image handle isn't a live HTMLImageElement / canvas / " +
|
|
61
|
+
"bitmap — likely a scene restored from storage where the <img> was " +
|
|
62
|
+
"lost (metadata.image) and src is a dead blob: URL. The image won't " +
|
|
63
|
+
"render until rehydrated from Scene.files.");
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=image-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-source.js","sourceRoot":"","sources":["../src/image-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,mBAAmB,GAAG;IAC1B,kBAAkB;IAClB,mBAAmB;IACnB,kBAAkB;IAClB,aAAa;IACb,iBAAiB;IACjB,iBAAiB;IACjB,YAAY;CACJ,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAA8B,EAAE;IAClF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,UAAqC,CAAC;IAChD,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,KAAK,YAAa,IAA0C,EAAE,CAAC;YAC/F,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAQ,EAAE;IACvD,IAAI,OAAO,OAAO,KAAK,WAAW;QAAE,OAAO;IAC3C,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,QAAQ;QACvB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC;YACzB,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,YAAY;QAChB,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YACrC,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,cAAc,CAAC,CAAC,oCAAoC;IAC5D,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IACvC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE3B,OAAO,CAAC,IAAI,CACV,yDAAyD,IAAI,KAAK;QAChE,oEAAoE;QACpE,oEAAoE;QACpE,qEAAqE;QACrE,2CAA2C,CAC9C,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { Canvas2DTarget } from "./canvas-target.js";
|
|
2
|
+
export { Canvas2DTextShaper } from "./canvas-text-shaper.js";
|
|
3
|
+
export { WebGL2Target } from "./webgl2-target.js";
|
|
4
|
+
export { renderViaTiles, type RenderViaTilesOptions } from "./tile-compositor.js";
|
|
5
|
+
export { setupHiDpi } from "./hi-dpi.js";
|
|
6
|
+
export { LayeredCanvas, type LayeredCanvasOptions } from "./layered-canvas.js";
|
|
7
|
+
export { supportsOffscreenCanvas, createOffscreenCanvas2DTarget, transferCanvasToWorker, } from "./offscreen.js";
|
|
8
|
+
export { WorkerPool, LayerWorkerPool } from "@oh-just-another/renderer-workers";
|
|
9
|
+
export { RecordingTarget, replayCommands, type RenderCommand, } from "./recording-target.js";
|
|
10
|
+
export { createLayeredSurface, createLayeredSurfaceWithFallback, type LayeredSurface, type RendererBackend, type CreateLayeredSurfaceOptions, } from "./layered-surface.js";
|
|
11
|
+
export { isWebGPUAvailable, isWebGL2Available, pickAvailableBackend, } from "./webgpu-detect.js";
|
|
12
|
+
export { LARGE_SCENE_WORKER_THRESHOLD } from "./constants.js";
|
|
13
|
+
export { installBuiltinRenderers, wrapText } from "@oh-just-another/renderer-core";
|
|
14
|
+
export type { WrapOptions, WrappedLine } from "@oh-just-another/renderer-core";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,gCAAgC,EAChC,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,2BAA2B,GACjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAK9D,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACnF,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { Canvas2DTarget } from "./canvas-target.js";
|
|
2
|
+
export { Canvas2DTextShaper } from "./canvas-text-shaper.js";
|
|
3
|
+
export { WebGL2Target } from "./webgl2-target.js";
|
|
4
|
+
export { renderViaTiles } from "./tile-compositor.js";
|
|
5
|
+
export { setupHiDpi } from "./hi-dpi.js";
|
|
6
|
+
export { LayeredCanvas } from "./layered-canvas.js";
|
|
7
|
+
export { supportsOffscreenCanvas, createOffscreenCanvas2DTarget, transferCanvasToWorker, } from "./offscreen.js";
|
|
8
|
+
// WorkerPool / LayerWorkerPool live in @oh-just-another/renderer-workers
|
|
9
|
+
// (backend-neutral primitives don't belong in the Canvas-specific package).
|
|
10
|
+
// Re-export to keep existing imports working.
|
|
11
|
+
export { WorkerPool, LayerWorkerPool } from "@oh-just-another/renderer-workers";
|
|
12
|
+
export { RecordingTarget, replayCommands, } from "./recording-target.js";
|
|
13
|
+
export { createLayeredSurface, createLayeredSurfaceWithFallback, } from "./layered-surface.js";
|
|
14
|
+
export { isWebGPUAvailable, isWebGL2Available, pickAvailableBackend, } from "./webgpu-detect.js";
|
|
15
|
+
export { LARGE_SCENE_WORKER_THRESHOLD } from "./constants.js";
|
|
16
|
+
// `installBuiltinRenderers` and `wrapText` live in
|
|
17
|
+
// `@oh-just-another/renderer-core` so the SVG / headless backends can share them.
|
|
18
|
+
// These re-exports keep existing imports working.
|
|
19
|
+
export { installBuiltinRenderers, wrapText } from "@oh-just-another/renderer-core";
|
|
20
|
+
// `installBuiltinRenderers()` must be called once before `renderScene`
|
|
21
|
+
// from `@oh-just-another/renderer-core` knows how to draw built-in shapes. It is
|
|
22
|
+
// not auto-invoked so that this package stays `sideEffects: false` and tree-
|
|
23
|
+
// shakeable. Hosts typically call it in their entry file.
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAA8B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,yEAAyE;AACzE,4EAA4E;AAC5E,8CAA8C;AAC9C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,gCAAgC,GAIjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAE9D,mDAAmD;AACnD,kFAAkF;AAClF,kDAAkD;AAClD,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAGnF,uEAAuE;AACvE,iFAAiF;AACjF,6EAA6E;AAC7E,0DAA0D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type LayerName } from "@oh-just-another/renderer-core";
|
|
2
|
+
import { Canvas2DTarget } from "./canvas-target.js";
|
|
3
|
+
export interface LayeredCanvasOptions {
|
|
4
|
+
/** Subset of layers to create. Defaults to all three. */
|
|
5
|
+
readonly layers?: readonly LayerName[];
|
|
6
|
+
/** Override `window.devicePixelRatio`. */
|
|
7
|
+
readonly dpr?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Manages one stacked `<canvas>` per layer. The background canvas sits at the
|
|
11
|
+
* bottom of the DOM stack and the overlay at the top, so drawing onto `main`
|
|
12
|
+
* never invalidates the static `background`. Each layer is a `Canvas2DTarget`
|
|
13
|
+
* with its own CSS-pixel coordinate space.
|
|
14
|
+
*
|
|
15
|
+
* The host element gets `position: relative` so absolute children stack on top
|
|
16
|
+
* of each other; each canvas is `position: absolute, inset: 0`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LayeredCanvas {
|
|
19
|
+
private readonly host;
|
|
20
|
+
private readonly layers;
|
|
21
|
+
private readonly canvases;
|
|
22
|
+
private width;
|
|
23
|
+
private height;
|
|
24
|
+
private dprOverride;
|
|
25
|
+
constructor(host: HTMLElement, width: number, height: number, options?: LayeredCanvasOptions);
|
|
26
|
+
/** Get the target for a layer. Throws if the layer was not created. */
|
|
27
|
+
get(name: LayerName): Canvas2DTarget;
|
|
28
|
+
/** Get the underlying `<canvas>` element for a layer. */
|
|
29
|
+
getCanvas(name: LayerName): HTMLCanvasElement;
|
|
30
|
+
/**
|
|
31
|
+
* Resize every canvas in the stack to the new CSS-pixel size.
|
|
32
|
+
*
|
|
33
|
+
* No-op when neither `width` nor `height` changed. Setting `canvas.width`
|
|
34
|
+
* resets the bitmap even when the new value equals the old, so this guard
|
|
35
|
+
* prevents the content getting wiped when a `ResizeObserver` fires with
|
|
36
|
+
* the current box size on its initial attach tick.
|
|
37
|
+
*/
|
|
38
|
+
resize(width: number, height: number): void;
|
|
39
|
+
get size(): {
|
|
40
|
+
readonly width: number;
|
|
41
|
+
readonly height: number;
|
|
42
|
+
};
|
|
43
|
+
/** Detach all canvases from the host. After this the instance is unusable. */
|
|
44
|
+
dispose(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=layered-canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layered-canvas.d.ts","sourceRoot":"","sources":["../src/layered-canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,oBAAoB;IACnC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4C;IACrE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAqB;gBAGtC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB;IAkCpC,uEAAuE;IACvE,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc;IAMpC,yDAAyD;IACzD,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB;IAM7C;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAY3C,IAAI,IAAI,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAE9D;IAED,8EAA8E;IAC9E,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { LAYER_ORDER } from "@oh-just-another/renderer-core";
|
|
2
|
+
import { Canvas2DTarget } from "./canvas-target.js";
|
|
3
|
+
import { setupHiDpi } from "./hi-dpi.js";
|
|
4
|
+
/**
|
|
5
|
+
* Manages one stacked `<canvas>` per layer. The background canvas sits at the
|
|
6
|
+
* bottom of the DOM stack and the overlay at the top, so drawing onto `main`
|
|
7
|
+
* never invalidates the static `background`. Each layer is a `Canvas2DTarget`
|
|
8
|
+
* with its own CSS-pixel coordinate space.
|
|
9
|
+
*
|
|
10
|
+
* The host element gets `position: relative` so absolute children stack on top
|
|
11
|
+
* of each other; each canvas is `position: absolute, inset: 0`.
|
|
12
|
+
*/
|
|
13
|
+
export class LayeredCanvas {
|
|
14
|
+
host;
|
|
15
|
+
layers;
|
|
16
|
+
canvases;
|
|
17
|
+
width;
|
|
18
|
+
height;
|
|
19
|
+
dprOverride;
|
|
20
|
+
constructor(host, width, height, options = {}) {
|
|
21
|
+
this.host = host;
|
|
22
|
+
this.width = width;
|
|
23
|
+
this.height = height;
|
|
24
|
+
this.dprOverride = options.dpr;
|
|
25
|
+
if (getComputedStyle(host).position === "static") {
|
|
26
|
+
host.style.position = "relative";
|
|
27
|
+
}
|
|
28
|
+
const requested = options.layers ?? LAYER_ORDER;
|
|
29
|
+
const layers = new Map();
|
|
30
|
+
const canvases = new Map();
|
|
31
|
+
for (const name of LAYER_ORDER) {
|
|
32
|
+
if (!requested.includes(name))
|
|
33
|
+
continue;
|
|
34
|
+
const canvas = host.ownerDocument.createElement("canvas");
|
|
35
|
+
canvas.dataset.layer = name;
|
|
36
|
+
canvas.style.position = "absolute";
|
|
37
|
+
canvas.style.inset = "0";
|
|
38
|
+
canvas.style.pointerEvents = name === "overlay" ? "auto" : "none";
|
|
39
|
+
host.appendChild(canvas);
|
|
40
|
+
const dpr = setupHiDpi(canvas, width, height, this.dprOverride);
|
|
41
|
+
const ctx = canvas.getContext("2d");
|
|
42
|
+
if (!ctx)
|
|
43
|
+
throw new Error("Failed to obtain 2D context");
|
|
44
|
+
layers.set(name, new Canvas2DTarget(ctx, width, height, dpr));
|
|
45
|
+
canvases.set(name, canvas);
|
|
46
|
+
}
|
|
47
|
+
this.layers = layers;
|
|
48
|
+
this.canvases = canvases;
|
|
49
|
+
}
|
|
50
|
+
/** Get the target for a layer. Throws if the layer was not created. */
|
|
51
|
+
get(name) {
|
|
52
|
+
const target = this.layers.get(name);
|
|
53
|
+
if (!target)
|
|
54
|
+
throw new Error(`Layer not created: ${name}`);
|
|
55
|
+
return target;
|
|
56
|
+
}
|
|
57
|
+
/** Get the underlying `<canvas>` element for a layer. */
|
|
58
|
+
getCanvas(name) {
|
|
59
|
+
const canvas = this.canvases.get(name);
|
|
60
|
+
if (!canvas)
|
|
61
|
+
throw new Error(`Layer not created: ${name}`);
|
|
62
|
+
return canvas;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resize every canvas in the stack to the new CSS-pixel size.
|
|
66
|
+
*
|
|
67
|
+
* No-op when neither `width` nor `height` changed. Setting `canvas.width`
|
|
68
|
+
* resets the bitmap even when the new value equals the old, so this guard
|
|
69
|
+
* prevents the content getting wiped when a `ResizeObserver` fires with
|
|
70
|
+
* the current box size on its initial attach tick.
|
|
71
|
+
*/
|
|
72
|
+
resize(width, height) {
|
|
73
|
+
if (this.width === width && this.height === height)
|
|
74
|
+
return;
|
|
75
|
+
this.width = width;
|
|
76
|
+
this.height = height;
|
|
77
|
+
for (const [name, canvas] of this.canvases) {
|
|
78
|
+
const dpr = setupHiDpi(canvas, width, height, this.dprOverride);
|
|
79
|
+
const layer = this.layers.get(name);
|
|
80
|
+
if (layer === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
layer.resize(width, height, dpr);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
get size() {
|
|
86
|
+
return { width: this.width, height: this.height };
|
|
87
|
+
}
|
|
88
|
+
/** Detach all canvases from the host. After this the instance is unusable. */
|
|
89
|
+
dispose() {
|
|
90
|
+
for (const canvas of this.canvases.values()) {
|
|
91
|
+
canvas.remove();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=layered-canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layered-canvas.js","sourceRoot":"","sources":["../src/layered-canvas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type LayerName, type RenderTarget } from "@oh-just-another/renderer-core";
|
|
2
|
+
/**
|
|
3
|
+
* Backend selector for `createLayeredSurface`.
|
|
4
|
+
*
|
|
5
|
+
* - `canvas2d` per-layer `Canvas2DTarget` over stacked DOM canvases.
|
|
6
|
+
* - `webgl2` per-layer `WebGL2Target` over stacked DOM canvases
|
|
7
|
+
* (each with its own WebGL2 context).
|
|
8
|
+
* - `offscreen` per-layer `RecordingTarget` on the main thread,
|
|
9
|
+
* forwarded into a dedicated worker per layer that
|
|
10
|
+
* owns the transferred OffscreenCanvas and replays
|
|
11
|
+
* the buffered command stream.
|
|
12
|
+
*/
|
|
13
|
+
export type RendererBackend = "canvas2d" | "webgl2" | "offscreen";
|
|
14
|
+
/**
|
|
15
|
+
* Common interface every backend's surface exposes. The Editor reaches
|
|
16
|
+
* each layer's `RenderTarget` via `get(name)`; the host calls
|
|
17
|
+
* `present()` after each frame so backends with deferred submission
|
|
18
|
+
* (offscreen) can ship their buffered commands to workers. Synchronous
|
|
19
|
+
* backends (`canvas2d`, `webgl2`) implement `present` as a no-op.
|
|
20
|
+
*/
|
|
21
|
+
export interface LayeredSurface {
|
|
22
|
+
readonly backend: RendererBackend;
|
|
23
|
+
get(name: LayerName): RenderTarget;
|
|
24
|
+
getCanvas(name: LayerName): HTMLCanvasElement;
|
|
25
|
+
resize(width: number, height: number): void;
|
|
26
|
+
readonly size: {
|
|
27
|
+
readonly width: number;
|
|
28
|
+
readonly height: number;
|
|
29
|
+
};
|
|
30
|
+
/** Hook called once per frame after the Editor finishes drawing. */
|
|
31
|
+
present(): void;
|
|
32
|
+
dispose(): void;
|
|
33
|
+
}
|
|
34
|
+
export interface CreateLayeredSurfaceOptions {
|
|
35
|
+
readonly backend?: RendererBackend;
|
|
36
|
+
/**
|
|
37
|
+
* Override the worker factory used by the `offscreen` backend.
|
|
38
|
+
* Hosts pass a Vite-aware factory:
|
|
39
|
+
*
|
|
40
|
+
* new Worker(new URL("@oh-just-another/renderer-canvas/render-worker",
|
|
41
|
+
* import.meta.url), { type: "module" })
|
|
42
|
+
*
|
|
43
|
+
* because `import.meta.url` resolution depends on the bundler.
|
|
44
|
+
* When missing, the offscreen backend throws on construction (there
|
|
45
|
+
* is no sensible cross-bundler default).
|
|
46
|
+
*/
|
|
47
|
+
readonly workerFactory?: () => Worker;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Factory entry point. Picks the right backend implementation, builds
|
|
51
|
+
* it, and returns the common `LayeredSurface` interface. Canvas2D is
|
|
52
|
+
* always safe; WebGL2 throws if the browser doesn't expose
|
|
53
|
+
* `getContext("webgl2")`; Offscreen throws if either `OffscreenCanvas`
|
|
54
|
+
* or `transferControlToOffscreen` is missing.
|
|
55
|
+
*
|
|
56
|
+
* The host (DiagramRoot) catches errors and falls back to `canvas2d`
|
|
57
|
+
* automatically when an opt-in backend isn't supported.
|
|
58
|
+
*/
|
|
59
|
+
export declare const createLayeredSurface: (host: HTMLElement, width: number, height: number, options?: CreateLayeredSurfaceOptions) => LayeredSurface;
|
|
60
|
+
/**
|
|
61
|
+
* Build the requested surface and fall back to `canvas2d` if the
|
|
62
|
+
* opt-in backend throws (no WebGL2 / no OffscreenCanvas / per-page
|
|
63
|
+
* WebGL context limit hit). On fallback, `onFallback` is invoked
|
|
64
|
+
* with the original error so the host can surface a toast / log.
|
|
65
|
+
*
|
|
66
|
+
* Use this when the backend is user-controlled (demo dropdown,
|
|
67
|
+
* URL param) so a misclick doesn't crash the React tree. Hosts
|
|
68
|
+
* that want hard failures should call `createLayeredSurface`
|
|
69
|
+
* directly and handle the throw themselves.
|
|
70
|
+
*/
|
|
71
|
+
export declare const createLayeredSurfaceWithFallback: (host: HTMLElement, width: number, height: number, options?: CreateLayeredSurfaceOptions, onFallback?: (backend: RendererBackend, error: unknown) => void) => {
|
|
72
|
+
surface: LayeredSurface;
|
|
73
|
+
effectiveBackend: RendererBackend;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=layered-surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layered-surface.d.ts","sourceRoot":"","sources":["../src/layered-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMhG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CAAC;IACnC,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,oEAAoE;IACpE,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACnC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,WAAW,EACjB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,UAAS,2BAAgC,KACxC,cAeF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC,GAC3C,MAAM,WAAW,EACjB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,UAAS,2BAAgC,EACzC,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,KAC9D;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,gBAAgB,EAAE,eAAe,CAAA;CAY9D,CAAC"}
|