@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.
Files changed (81) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +36 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/canvas-target.d.ts +83 -0
  6. package/dist/canvas-target.d.ts.map +1 -0
  7. package/dist/canvas-target.js +208 -0
  8. package/dist/canvas-target.js.map +1 -0
  9. package/dist/canvas-text-shaper.d.ts +11 -0
  10. package/dist/canvas-text-shaper.d.ts.map +1 -0
  11. package/dist/canvas-text-shaper.js +56 -0
  12. package/dist/canvas-text-shaper.js.map +1 -0
  13. package/dist/constants.d.ts +52 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +52 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/hi-dpi.d.ts +26 -0
  18. package/dist/hi-dpi.d.ts.map +1 -0
  19. package/dist/hi-dpi.js +62 -0
  20. package/dist/hi-dpi.js.map +1 -0
  21. package/dist/image-source.d.ts +3 -0
  22. package/dist/image-source.d.ts.map +1 -0
  23. package/dist/image-source.js +65 -0
  24. package/dist/image-source.js.map +1 -0
  25. package/dist/index.d.ts +15 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +24 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/layered-canvas.d.ts +46 -0
  30. package/dist/layered-canvas.d.ts.map +1 -0
  31. package/dist/layered-canvas.js +95 -0
  32. package/dist/layered-canvas.js.map +1 -0
  33. package/dist/layered-surface.d.ts +75 -0
  34. package/dist/layered-surface.d.ts.map +1 -0
  35. package/dist/layered-surface.js +262 -0
  36. package/dist/layered-surface.js.map +1 -0
  37. package/dist/offscreen.d.ts +30 -0
  38. package/dist/offscreen.d.ts.map +1 -0
  39. package/dist/offscreen.js +50 -0
  40. package/dist/offscreen.js.map +1 -0
  41. package/dist/recording-target.d.ts +203 -0
  42. package/dist/recording-target.d.ts.map +1 -0
  43. package/dist/recording-target.js +260 -0
  44. package/dist/recording-target.js.map +1 -0
  45. package/dist/render-worker.d.ts +2 -0
  46. package/dist/render-worker.d.ts.map +1 -0
  47. package/dist/render-worker.js +112 -0
  48. package/dist/render-worker.js.map +1 -0
  49. package/dist/tile-compositor.d.ts +44 -0
  50. package/dist/tile-compositor.d.ts.map +1 -0
  51. package/dist/tile-compositor.js +110 -0
  52. package/dist/tile-compositor.js.map +1 -0
  53. package/dist/webgl2-color.d.ts +13 -0
  54. package/dist/webgl2-color.d.ts.map +1 -0
  55. package/dist/webgl2-color.js +26 -0
  56. package/dist/webgl2-color.js.map +1 -0
  57. package/dist/webgl2-curve.d.ts +46 -0
  58. package/dist/webgl2-curve.d.ts.map +1 -0
  59. package/dist/webgl2-curve.js +162 -0
  60. package/dist/webgl2-curve.js.map +1 -0
  61. package/dist/webgl2-ellipse.d.ts +52 -0
  62. package/dist/webgl2-ellipse.d.ts.map +1 -0
  63. package/dist/webgl2-ellipse.js +171 -0
  64. package/dist/webgl2-ellipse.js.map +1 -0
  65. package/dist/webgl2-msdf-text.d.ts +84 -0
  66. package/dist/webgl2-msdf-text.d.ts.map +1 -0
  67. package/dist/webgl2-msdf-text.js +306 -0
  68. package/dist/webgl2-msdf-text.js.map +1 -0
  69. package/dist/webgl2-stroke.d.ts +52 -0
  70. package/dist/webgl2-stroke.d.ts.map +1 -0
  71. package/dist/webgl2-stroke.js +318 -0
  72. package/dist/webgl2-stroke.js.map +1 -0
  73. package/dist/webgl2-target.d.ts +311 -0
  74. package/dist/webgl2-target.d.ts.map +1 -0
  75. package/dist/webgl2-target.js +1368 -0
  76. package/dist/webgl2-target.js.map +1 -0
  77. package/dist/webgpu-detect.d.ts +30 -0
  78. package/dist/webgpu-detect.d.ts.map +1 -0
  79. package/dist/webgpu-detect.js +74 -0
  80. package/dist/webgpu-detect.js.map +1 -0
  81. package/package.json +67 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * `drawImage` is NOT serialisable through this command stream — image
3
+ * sources (HTMLImageElement, ImageBitmap, HTMLCanvasElement) either
4
+ * can't cross the postMessage boundary cleanly or require expensive
5
+ * transfers. The recording target silently skips `drawImage` calls and
6
+ * counts them in `skippedImageDraws` so the host UI can warn (or fall
7
+ * back to main-thread compositing for image-heavy scenes).
8
+ *
9
+ * `measureText` returns a synchronous heuristic — the worker has the
10
+ * authoritative measurement, but routing per-call is too chatty. Hosts
11
+ * that care about pixel-perfect text in offscreen mode should
12
+ * pre-measure on a sidecar text shaper.
13
+ */
14
+ export class RecordingTarget {
15
+ commands = [];
16
+ _width;
17
+ _height;
18
+ /** Counter so hosts can warn when images are silently skipped. */
19
+ skippedImageDraws = 0;
20
+ constructor(width, height) {
21
+ this._width = width;
22
+ this._height = height;
23
+ }
24
+ get size() {
25
+ return { width: this._width, height: this._height };
26
+ }
27
+ resize(width, height) {
28
+ this._width = width;
29
+ this._height = height;
30
+ this.commands.push({ k: "resize", w: width, h: height });
31
+ }
32
+ /** Pop the buffered commands and clear the internal log. */
33
+ flush() {
34
+ const out = this.commands;
35
+ this.commands = [];
36
+ return out;
37
+ }
38
+ /** Snapshot of the buffered commands without clearing. */
39
+ peek() {
40
+ return this.commands;
41
+ }
42
+ setFill(color) {
43
+ this.commands.push({ k: "setFill", color });
44
+ }
45
+ setStroke(color) {
46
+ this.commands.push({ k: "setStroke", color });
47
+ }
48
+ setStrokeWidth(w) {
49
+ this.commands.push({ k: "setStrokeWidth", w });
50
+ }
51
+ setOpacity(a) {
52
+ this.commands.push({ k: "setOpacity", a });
53
+ }
54
+ setLineCap(cap) {
55
+ this.commands.push({ k: "setLineCap", cap });
56
+ }
57
+ setLineJoin(join) {
58
+ this.commands.push({ k: "setLineJoin", join });
59
+ }
60
+ setDashArray(dash) {
61
+ this.commands.push({ k: "setDashArray", dash });
62
+ }
63
+ setFont(family, size, options) {
64
+ this.commands.push({ k: "setFont", family, size, ...(options ? { options } : {}) });
65
+ }
66
+ setTextAlign(align) {
67
+ this.commands.push({ k: "setTextAlign", align });
68
+ }
69
+ setTextBaseline(baseline) {
70
+ this.commands.push({ k: "setTextBaseline", baseline });
71
+ }
72
+ save() {
73
+ this.commands.push({ k: "save" });
74
+ }
75
+ restore() {
76
+ this.commands.push({ k: "restore" });
77
+ }
78
+ translate(x, y) {
79
+ this.commands.push({ k: "translate", x, y });
80
+ }
81
+ rotate(r) {
82
+ this.commands.push({ k: "rotate", r });
83
+ }
84
+ scale(sx, sy) {
85
+ this.commands.push({ k: "scale", sx, sy });
86
+ }
87
+ setTransform(t) {
88
+ this.commands.push({ k: "setTransform", t });
89
+ }
90
+ resetTransform() {
91
+ this.commands.push({ k: "resetTransform" });
92
+ }
93
+ beginPath() {
94
+ this.commands.push({ k: "beginPath" });
95
+ }
96
+ closePath() {
97
+ this.commands.push({ k: "closePath" });
98
+ }
99
+ moveTo(x, y) {
100
+ this.commands.push({ k: "moveTo", x, y });
101
+ }
102
+ lineTo(x, y) {
103
+ this.commands.push({ k: "lineTo", x, y });
104
+ }
105
+ quadraticCurveTo(cx, cy, x, y) {
106
+ this.commands.push({ k: "quadraticCurveTo", cx, cy, x, y });
107
+ }
108
+ bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
109
+ this.commands.push({ k: "bezierCurveTo", c1x, c1y, c2x, c2y, x, y });
110
+ }
111
+ rect(x, y, w, h) {
112
+ this.commands.push({ k: "rect", x, y, w, h });
113
+ }
114
+ ellipse(cx, cy, rx, ry) {
115
+ this.commands.push({ k: "ellipse", cx, cy, rx, ry });
116
+ }
117
+ fill(rule) {
118
+ this.commands.push(rule !== undefined ? { k: "fill", rule } : { k: "fill" });
119
+ }
120
+ stroke() {
121
+ this.commands.push({ k: "stroke" });
122
+ }
123
+ fillText(text, x, y, maxWidth) {
124
+ this.commands.push(maxWidth !== undefined
125
+ ? { k: "fillText", text, x, y, maxWidth }
126
+ : { k: "fillText", text, x, y });
127
+ }
128
+ measureText(text) {
129
+ // Heuristic — same factor as @text-wasm's fallback so layout stays
130
+ // roughly proportional. The Editor's text rendering path re-measures
131
+ // on the real Canvas2D in the main thread when it needs precise
132
+ // widths (label wrapping, hit-test).
133
+ return { width: text.length * 8 };
134
+ }
135
+ drawImage(_image, _dx, _dy, _dw, _dh) {
136
+ this.skippedImageDraws++;
137
+ }
138
+ clear(bounds) {
139
+ this.commands.push(bounds !== undefined ? { k: "clear", bounds } : { k: "clear" });
140
+ }
141
+ markDirty(bounds) {
142
+ this.commands.push({ k: "markDirty", bounds });
143
+ }
144
+ }
145
+ /**
146
+ * Replay a previously-flushed command buffer onto a real RenderTarget.
147
+ * Used by the worker entry point to apply commands shipped from the
148
+ * main thread.
149
+ */
150
+ export const replayCommands = (target, commands) => {
151
+ for (const cmd of commands) {
152
+ switch (cmd.k) {
153
+ case "setFill":
154
+ target.setFill(cmd.color);
155
+ break;
156
+ case "setStroke":
157
+ target.setStroke(cmd.color);
158
+ break;
159
+ case "setStrokeWidth":
160
+ target.setStrokeWidth(cmd.w);
161
+ break;
162
+ case "setOpacity":
163
+ target.setOpacity(cmd.a);
164
+ break;
165
+ case "setLineCap":
166
+ target.setLineCap(cmd.cap);
167
+ break;
168
+ case "setLineJoin":
169
+ target.setLineJoin(cmd.join);
170
+ break;
171
+ case "setDashArray":
172
+ target.setDashArray(cmd.dash);
173
+ break;
174
+ case "setFont":
175
+ target.setFont(cmd.family, cmd.size, cmd.options);
176
+ break;
177
+ case "setTextAlign":
178
+ target.setTextAlign(cmd.align);
179
+ break;
180
+ case "setTextBaseline":
181
+ target.setTextBaseline(cmd.baseline);
182
+ break;
183
+ case "save":
184
+ target.save();
185
+ break;
186
+ case "restore":
187
+ target.restore();
188
+ break;
189
+ case "translate":
190
+ target.translate(cmd.x, cmd.y);
191
+ break;
192
+ case "rotate":
193
+ target.rotate(cmd.r);
194
+ break;
195
+ case "scale":
196
+ target.scale(cmd.sx, cmd.sy);
197
+ break;
198
+ case "setTransform":
199
+ target.setTransform(cmd.t);
200
+ break;
201
+ case "resetTransform":
202
+ target.resetTransform();
203
+ break;
204
+ case "beginPath":
205
+ target.beginPath();
206
+ break;
207
+ case "closePath":
208
+ target.closePath();
209
+ break;
210
+ case "moveTo":
211
+ target.moveTo(cmd.x, cmd.y);
212
+ break;
213
+ case "lineTo":
214
+ target.lineTo(cmd.x, cmd.y);
215
+ break;
216
+ case "quadraticCurveTo":
217
+ target.quadraticCurveTo(cmd.cx, cmd.cy, cmd.x, cmd.y);
218
+ break;
219
+ case "bezierCurveTo":
220
+ target.bezierCurveTo(cmd.c1x, cmd.c1y, cmd.c2x, cmd.c2y, cmd.x, cmd.y);
221
+ break;
222
+ case "rect":
223
+ target.rect(cmd.x, cmd.y, cmd.w, cmd.h);
224
+ break;
225
+ case "ellipse":
226
+ target.ellipse(cmd.cx, cmd.cy, cmd.rx, cmd.ry);
227
+ break;
228
+ case "fill":
229
+ if (cmd.rule !== undefined)
230
+ target.fill(cmd.rule);
231
+ else
232
+ target.fill();
233
+ break;
234
+ case "stroke":
235
+ target.stroke();
236
+ break;
237
+ case "fillText":
238
+ if (cmd.maxWidth !== undefined)
239
+ target.fillText(cmd.text, cmd.x, cmd.y, cmd.maxWidth);
240
+ else
241
+ target.fillText(cmd.text, cmd.x, cmd.y);
242
+ break;
243
+ case "clear":
244
+ if (cmd.bounds !== undefined)
245
+ target.clear(cmd.bounds);
246
+ else
247
+ target.clear();
248
+ break;
249
+ case "markDirty":
250
+ target.markDirty?.(cmd.bounds);
251
+ break;
252
+ case "resize":
253
+ // No-op for replay — the worker owns the canvas size and
254
+ // resizes via its own `resize` message, not via the command
255
+ // stream.
256
+ break;
257
+ }
258
+ }
259
+ };
260
+ //# sourceMappingURL=recording-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recording-target.js","sourceRoot":"","sources":["../src/recording-target.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=render-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-worker.d.ts","sourceRoot":"","sources":["../src/render-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ /// <reference lib="webworker" />
2
+ import { installBuiltinRenderers, renderScene } from "@oh-just-another/renderer-core";
3
+ import { Canvas2DTarget } from "./canvas-target.js";
4
+ import { replayCommands } from "./recording-target.js";
5
+ const state = { canvas: null, target: null, dpr: 1 };
6
+ let renderersInstalled = false;
7
+ const ensureRenderers = () => {
8
+ if (renderersInstalled)
9
+ return;
10
+ installBuiltinRenderers();
11
+ renderersInstalled = true;
12
+ };
13
+ const post = (msg, transfer) => {
14
+ if (transfer && transfer.length > 0) {
15
+ self.postMessage(msg, transfer);
16
+ }
17
+ else {
18
+ self.postMessage(msg);
19
+ }
20
+ };
21
+ const init = (canvas, width, height, dpr) => {
22
+ state.canvas = canvas;
23
+ state.dpr = dpr;
24
+ // Resize the bitmap to dpr-scaled pixels — the host's CSS size is
25
+ // (width, height); render into the bigger buffer and let the
26
+ // composite step downsample as needed.
27
+ canvas.width = Math.max(1, Math.round(width * dpr));
28
+ canvas.height = Math.max(1, Math.round(height * dpr));
29
+ const ctx = canvas.getContext("2d");
30
+ if (!ctx)
31
+ throw new Error("OffscreenCanvas 2D context unavailable");
32
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
33
+ state.target = new Canvas2DTarget(ctx, width, height, dpr);
34
+ post({ type: "ready" });
35
+ };
36
+ const resize = (width, height) => {
37
+ if (!state.canvas || !state.target)
38
+ return;
39
+ state.canvas.width = Math.max(1, Math.round(width * state.dpr));
40
+ state.canvas.height = Math.max(1, Math.round(height * state.dpr));
41
+ const ctx = state.canvas.getContext("2d");
42
+ if (!ctx)
43
+ return;
44
+ ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
45
+ state.target.resize(width, height, state.dpr);
46
+ };
47
+ const snapshot = (scene) => {
48
+ if (!state.canvas || !state.target) {
49
+ post({ type: "error", message: "Worker not initialised" });
50
+ return;
51
+ }
52
+ ensureRenderers();
53
+ const ctx = state.canvas.getContext("2d");
54
+ if (ctx === null) {
55
+ post({ type: "error", message: "Worker not initialised" });
56
+ return;
57
+ }
58
+ ctx.save();
59
+ ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
60
+ ctx.clearRect(0, 0, state.target.size.width, state.target.size.height);
61
+ renderScene(scene, state.target);
62
+ ctx.restore();
63
+ const bitmap = state.canvas.transferToImageBitmap();
64
+ post({ type: "frame-done", bitmap }, [bitmap]);
65
+ };
66
+ /**
67
+ * Replay a serialised RecordingTarget command stream onto the owned
68
+ * OffscreenCanvas. Used by the LayeredSurface "offscreen" backend: the
69
+ * main thread captures every RenderTarget call into a buffer and ships
70
+ * it here per frame; the worker replays.
71
+ */
72
+ const replay = (commands) => {
73
+ if (!state.target) {
74
+ post({ type: "error", message: "Worker not initialised" });
75
+ return;
76
+ }
77
+ replayCommands(state.target, commands);
78
+ };
79
+ self.addEventListener("message", (ev) => {
80
+ const msg = ev.data;
81
+ try {
82
+ switch (msg.type) {
83
+ case "init":
84
+ init(msg.canvas, msg.width, msg.height, msg.dpr);
85
+ break;
86
+ case "resize":
87
+ resize(msg.width, msg.height);
88
+ break;
89
+ case "snapshot":
90
+ // A dpr update only takes effect on the next resize; snapshot
91
+ // honours whatever transform init established.
92
+ snapshot(msg.scene);
93
+ break;
94
+ case "replay":
95
+ replay(msg.commands);
96
+ break;
97
+ case "frame":
98
+ // Patch-stream frames are not implemented; the protocol is
99
+ // reserved. Reply with an error so callers don't hang on the
100
+ // awaited response.
101
+ post({ type: "error", message: "patch-stream frames not implemented" });
102
+ break;
103
+ }
104
+ }
105
+ catch (err) {
106
+ post({
107
+ type: "error",
108
+ message: err instanceof Error ? err.message : String(err),
109
+ });
110
+ }
111
+ });
112
+ //# sourceMappingURL=render-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-worker.js","sourceRoot":"","sources":["../src/render-worker.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAMtF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAsB,MAAM,uBAAuB,CAAC;AAqB3E,MAAM,KAAK,GAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAElE,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAE/B,MAAM,eAAe,GAAG,GAAS,EAAE;IACjC,IAAI,kBAAkB;QAAE,OAAO;IAC/B,uBAAuB,EAAE,CAAC;IAC1B,kBAAkB,GAAG,IAAI,CAAC;AAC5B,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG,CAAC,GAAyB,EAAE,QAAyB,EAAQ,EAAE;IAC1E,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,IAA8C,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC7E,CAAC;SAAM,CAAC;QACL,IAA8C,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnE,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG,CAAC,MAAuB,EAAE,KAAa,EAAE,MAAc,EAAE,GAAW,EAAQ,EAAE;IACzF,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;IAChB,kEAAkE;IAClE,6DAA6D;IAC7D,uCAAuC;IACvC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACpE,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,KAAK,CAAC,MAAM,GAAG,IAAI,cAAc,CAC/B,GAA0C,EAC1C,KAAK,EACL,MAAM,EACN,GAAG,CACJ,CAAC;IACF,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,MAAc,EAAQ,EAAE;IACrD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO;IAC3C,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,KAAY,EAAQ,EAAE;IACtC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACnC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,eAAe,EAAE,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,GAAG,CAAC,IAAI,EAAE,CAAC;IACX,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvE,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC;IACpD,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,QAAkC,EAAQ,EAAE;IAC1D,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC,CAAC;AAKD,IAA8C,CAAC,gBAAgB,CAC9D,SAAS,EACT,CAAC,EAAgC,EAAE,EAAE;IACnC,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,MAAM;gBACT,IAAI,CAAC,GAAG,CAAC,MAAyB,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpE,MAAM;YACR,KAAK,QAAQ;gBACX,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC9B,MAAM;YACR,KAAK,UAAU;gBACb,8DAA8D;gBAC9D,+CAA+C;gBAC/C,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM;YACR,KAAK,QAAQ;gBACX,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACrB,MAAM;YACR,KAAK,OAAO;gBACV,2DAA2D;gBAC3D,6DAA6D;gBAC7D,oBAAoB;gBACpB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC,CAAC;gBACxE,MAAM;QACV,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SAC1D,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CACF,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { type TileCache } from "@oh-just-another/renderer-core";
2
+ import type { Bounds, ElementId } from "@oh-just-another/types";
3
+ import { type Scene } from "@oh-just-another/scene";
4
+ import type { Canvas2DTarget } from "./canvas-target.js";
5
+ /**
6
+ * Main-thread Canvas2D tile compositor.
7
+ *
8
+ * Splits the visible viewport into a fixed-size world-space grid
9
+ * (`TILE_SIZE` per side; default 2048 world units). Each tile is
10
+ * rasterised once into its own OffscreenCanvas, stored in a
11
+ * `TileCache`, and composited onto the main canvas via `drawImage`.
12
+ * Pure pan re-composites from cache; zoom changes the bucket and may
13
+ * invalidate.
14
+ *
15
+ * Designed for very large scenes where the per-frame `renderScene`
16
+ * walks every visible shape — composite of N cached bitmaps is far
17
+ * cheaper than re-rasterising N shapes.
18
+ */
19
+ export interface ChangedElementRecord {
20
+ /** World bbox of the shape in the previous frame, or null if it was just added. */
21
+ readonly before: Bounds | null;
22
+ /** World bbox of the shape in the current frame, or null if it was just removed. */
23
+ readonly after: Bounds | null;
24
+ }
25
+ export interface RenderViaTilesOptions {
26
+ /** World-space rect currently visible. Tiles outside are skipped. */
27
+ readonly viewport: Bounds;
28
+ /** Persistent tile cache — same instance across frames. */
29
+ readonly cache: TileCache<OffscreenCanvas>;
30
+ /**
31
+ * Element ids whose scene-reference changed since the previous frame,
32
+ * each with the before/after world bbox. The compositor routes
33
+ * invalidation by case:
34
+ * • removed (after null) → invalidateForElement (id present in
35
+ * reverse index)
36
+ * • added (before null) → invalidateRect (no id yet)
37
+ * • mutated/moved (both) → both rects
38
+ */
39
+ readonly changedElements?: ReadonlyMap<ElementId, ChangedElementRecord>;
40
+ /** Current zoom (used to pick the cache bucket). */
41
+ readonly zoomBucket: number;
42
+ }
43
+ export declare const renderViaTiles: (scene: Scene, mainTarget: Canvas2DTarget, options: RenderViaTilesOptions) => void;
44
+ //# sourceMappingURL=tile-compositor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tile-compositor.d.ts","sourceRoot":"","sources":["../src/tile-compositor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAGf,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAIL,KAAK,KAAK,EAEX,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;IAC3C;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACxE,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,cAAc,GACzB,OAAO,KAAK,EACZ,YAAY,cAAc,EAC1B,SAAS,qBAAqB,KAC7B,IA0CF,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { TILE_SIZE, } from "@oh-just-another/renderer-core";
2
+ import { getElementsInLayer, getElementWorldBounds, getLayersInOrder, } from "@oh-just-another/scene";
3
+ import { createOffscreenCanvas2DTarget } from "./offscreen.js";
4
+ import { getElementRenderer } from "@oh-just-another/renderer-core";
5
+ export const renderViaTiles = (scene, mainTarget, options) => {
6
+ const { viewport, cache, changedElements, zoomBucket } = options;
7
+ // 1) Invalidate cached tiles per patch (covers add / remove / move).
8
+ if (changedElements) {
9
+ for (const [id, record] of changedElements) {
10
+ cache.invalidateForPatch({
11
+ ...(record.after === null ? { removedElementId: id } : {}),
12
+ ...(record.before ? { beforeBounds: record.before } : {}),
13
+ ...(record.after ? { afterBounds: record.after } : {}),
14
+ });
15
+ }
16
+ }
17
+ // 2) Visible tile range.
18
+ const colMin = Math.floor(viewport.x / TILE_SIZE);
19
+ const rowMin = Math.floor(viewport.y / TILE_SIZE);
20
+ const colMax = Math.floor((viewport.x + viewport.width) / TILE_SIZE);
21
+ const rowMax = Math.floor((viewport.y + viewport.height) / TILE_SIZE);
22
+ // 3) For each visible tile, hit cache or render fresh.
23
+ for (let row = rowMin; row <= rowMax; row++) {
24
+ for (let col = colMin; col <= colMax; col++) {
25
+ const key = { col, row, zoom: zoomBucket };
26
+ let entry = cache.get(key);
27
+ if (!entry) {
28
+ const fresh = rasteriseTile(scene, col, row, zoomBucket);
29
+ if (!fresh)
30
+ continue;
31
+ cache.set(fresh);
32
+ entry = fresh;
33
+ }
34
+ // Composite — draw the cached bitmap at its world position. The
35
+ // main target's transform already maps world→screen.
36
+ mainTarget.drawImage(entry.bitmap, entry.bounds.x, entry.bounds.y, entry.bounds.width, entry.bounds.height);
37
+ }
38
+ }
39
+ };
40
+ /**
41
+ * Render a single tile's worth of shapes into a fresh OffscreenCanvas
42
+ * sized at `TILE_SIZE * zoomBucket` device pixels per side. Returns
43
+ * `null` when the tile contains no shapes.
44
+ */
45
+ const rasteriseTile = (scene, col, row, zoomBucket) => {
46
+ if (typeof OffscreenCanvas === "undefined")
47
+ return null;
48
+ const worldX = col * TILE_SIZE;
49
+ const worldY = row * TILE_SIZE;
50
+ const worldBounds = {
51
+ x: worldX,
52
+ y: worldY,
53
+ width: TILE_SIZE,
54
+ height: TILE_SIZE,
55
+ };
56
+ const shapes = elementsIntersectingTile(scene, worldBounds);
57
+ if (shapes.length === 0)
58
+ return null;
59
+ // Bitmap size — `TILE_SIZE * zoomBucket` device pixels so the tile
60
+ // stays crisp at this bucket. Higher buckets allocate more memory;
61
+ // cache eviction keeps total bounded.
62
+ const bitmapSize = Math.max(1, Math.round(TILE_SIZE * zoomBucket));
63
+ const { canvas, target } = createOffscreenCanvas2DTarget(bitmapSize, bitmapSize);
64
+ // World → tile-local + zoom scaling.
65
+ target.save();
66
+ target.scale(zoomBucket, zoomBucket);
67
+ target.translate(-worldX, -worldY);
68
+ for (const shape of shapes) {
69
+ const renderer = getElementRenderer(shape.type);
70
+ if (!renderer)
71
+ continue;
72
+ target.save();
73
+ target.translate(shape.position.x, shape.position.y);
74
+ if (shape.rotation !== 0)
75
+ target.rotate(shape.rotation);
76
+ if (shape.scale.x !== 1 || shape.scale.y !== 1) {
77
+ target.scale(shape.scale.x, shape.scale.y);
78
+ }
79
+ renderer(shape, target, { zoom: zoomBucket });
80
+ target.restore();
81
+ }
82
+ target.restore();
83
+ // 4 bytes per pixel (RGBA) — byte accounting for LRU.
84
+ const bytes = bitmapSize * bitmapSize * 4;
85
+ return {
86
+ key: { col, row, zoom: zoomBucket },
87
+ bitmap: canvas,
88
+ bounds: worldBounds,
89
+ bytes,
90
+ elements: shapes.map((s) => s.id),
91
+ };
92
+ };
93
+ const elementsIntersectingTile = (scene, tileBounds) => {
94
+ const out = [];
95
+ for (const layer of getLayersInOrder(scene)) {
96
+ if (!layer.visible)
97
+ continue;
98
+ for (const shape of getElementsInLayer(scene, layer.id)) {
99
+ const b = getElementWorldBounds(shape);
100
+ if (intersects(b, tileBounds))
101
+ out.push(shape);
102
+ }
103
+ }
104
+ return out;
105
+ };
106
+ const intersects = (a, b) => a.x < b.x + b.width &&
107
+ a.x + a.width > b.x &&
108
+ a.y < b.y + b.height &&
109
+ a.y + a.height > b.y;
110
+ //# sourceMappingURL=tile-compositor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tile-compositor.js","sourceRoot":"","sources":["../src/tile-compositor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Color } from "@oh-just-another/types";
2
+ /**
3
+ * Parse a CSS color into normalised RGBA `[r, g, b, a]` where every
4
+ * component is in `[0, 1]`. Delegates to `@math/color` so the WebGL2
5
+ * backend understands the same syntaxes as Canvas2D — short hex
6
+ * (`#bbb`), `rgb()`/`rgba()`, named colors (`white`, `transparent`),
7
+ * and `#rrggbbaa` with explicit alpha.
8
+ *
9
+ * Anything unparseable falls back to opaque black plus a dev-time
10
+ * warning.
11
+ */
12
+ export declare const parseWebGL2Color: (color: Color | null) => [number, number, number, number];
13
+ //# sourceMappingURL=webgl2-color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl2-color.d.ts","sourceRoot":"","sources":["../src/webgl2-color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAGpD;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,KAAK,GAAG,IAAI,KAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAYrF,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { color as colorParser } from "@oh-just-another/math";
2
+ /**
3
+ * Parse a CSS color into normalised RGBA `[r, g, b, a]` where every
4
+ * component is in `[0, 1]`. Delegates to `@math/color` so the WebGL2
5
+ * backend understands the same syntaxes as Canvas2D — short hex
6
+ * (`#bbb`), `rgb()`/`rgba()`, named colors (`white`, `transparent`),
7
+ * and `#rrggbbaa` with explicit alpha.
8
+ *
9
+ * Anything unparseable falls back to opaque black plus a dev-time
10
+ * warning.
11
+ */
12
+ export const parseWebGL2Color = (color) => {
13
+ if (color === null)
14
+ return [0, 0, 0, 0];
15
+ try {
16
+ const { r, g, b, a } = colorParser.parse(color);
17
+ return [r / 255, g / 255, b / 255, a];
18
+ }
19
+ catch {
20
+ if (typeof console !== "undefined") {
21
+ console.warn(`[WebGL2Target] cannot parse color '${color}', falling back to black`);
22
+ }
23
+ return [0, 0, 0, 1];
24
+ }
25
+ };
26
+ //# sourceMappingURL=webgl2-color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl2-color.js","sourceRoot":"","sources":["../src/webgl2-color.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAmB,EAAoC,EAAE;IACxF,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE,CAAC;YAEnC,OAAO,CAAC,IAAI,CAAC,sCAAsC,KAAK,0BAA0B,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB,CAAC;AACH,CAAC,CAAC"}