canvas-ultrafast 1.0.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.
@@ -0,0 +1,963 @@
1
+ class v {
2
+ d = [];
3
+ b = {};
4
+ a(t, ...e) {
5
+ this.d.push({ type: "method", name: t, args: e });
6
+ }
7
+ c(t, e) {
8
+ this.b[t] = e, this.d.push({ type: "property", name: t, value: e });
9
+ }
10
+ /**
11
+ * Drain and return all buffered commands.
12
+ * No _ prefix: called cross-file from pipeline, must survive mangleProps.
13
+ */
14
+ takeCommands() {
15
+ if (this.d.length === 0) return [];
16
+ const t = this.d;
17
+ return this.d = [], t;
18
+ }
19
+ // Canvas 2D API methods
20
+ // State
21
+ save() {
22
+ this.a("save");
23
+ }
24
+ restore() {
25
+ this.a("restore");
26
+ }
27
+ // Transform
28
+ scale(t, e) {
29
+ this.a("scale", t, e);
30
+ }
31
+ rotate(t) {
32
+ this.a("rotate", t);
33
+ }
34
+ translate(t, e) {
35
+ this.a("translate", t, e);
36
+ }
37
+ transform(t, e, s, i, r, a) {
38
+ this.a("transform", t, e, s, i, r, a);
39
+ }
40
+ setTransform(t, e, s, i, r, a) {
41
+ this.a("setTransform", t, e, s, i, r, a);
42
+ }
43
+ resetTransform() {
44
+ this.a("resetTransform");
45
+ }
46
+ // Rectangles
47
+ clearRect(t, e, s, i) {
48
+ this.a("clearRect", t, e, s, i);
49
+ }
50
+ fillRect(t, e, s, i) {
51
+ this.a("fillRect", t, e, s, i);
52
+ }
53
+ strokeRect(t, e, s, i) {
54
+ this.a("strokeRect", t, e, s, i);
55
+ }
56
+ // Text
57
+ fillText(t, e, s, i) {
58
+ this.a("fillText", ...i !== void 0 ? [t, e, s, i] : [t, e, s]);
59
+ }
60
+ strokeText(t, e, s, i) {
61
+ this.a("strokeText", ...i !== void 0 ? [t, e, s, i] : [t, e, s]);
62
+ }
63
+ // Line drawing
64
+ beginPath() {
65
+ this.a("beginPath");
66
+ }
67
+ closePath() {
68
+ this.a("closePath");
69
+ }
70
+ moveTo(t, e) {
71
+ this.a("moveTo", t, e);
72
+ }
73
+ lineTo(t, e) {
74
+ this.a("lineTo", t, e);
75
+ }
76
+ bezierCurveTo(t, e, s, i, r, a) {
77
+ this.a("bezierCurveTo", t, e, s, i, r, a);
78
+ }
79
+ quadraticCurveTo(t, e, s, i) {
80
+ this.a("quadraticCurveTo", t, e, s, i);
81
+ }
82
+ arc(t, e, s, i, r, a) {
83
+ this.a("arc", ...a !== void 0 ? [t, e, s, i, r, a] : [t, e, s, i, r]);
84
+ }
85
+ arcTo(t, e, s, i, r) {
86
+ this.a("arcTo", t, e, s, i, r);
87
+ }
88
+ ellipse(t, e, s, i, r, a, h, o) {
89
+ this.a("ellipse", ...o !== void 0 ? [t, e, s, i, r, a, h, o] : [t, e, s, i, r, a, h]);
90
+ }
91
+ rect(t, e, s, i) {
92
+ this.a("rect", t, e, s, i);
93
+ }
94
+ // Fill and stroke
95
+ fill() {
96
+ this.a("fill");
97
+ }
98
+ stroke() {
99
+ this.a("stroke");
100
+ }
101
+ clip() {
102
+ this.a("clip");
103
+ }
104
+ // Properties (setters)
105
+ set fillStyle(t) {
106
+ this.c("fillStyle", t);
107
+ }
108
+ set strokeStyle(t) {
109
+ this.c("strokeStyle", t);
110
+ }
111
+ set lineWidth(t) {
112
+ this.c("lineWidth", t);
113
+ }
114
+ set lineCap(t) {
115
+ this.c("lineCap", t);
116
+ }
117
+ set lineJoin(t) {
118
+ this.c("lineJoin", t);
119
+ }
120
+ set miterLimit(t) {
121
+ this.c("miterLimit", t);
122
+ }
123
+ set lineDashOffset(t) {
124
+ this.c("lineDashOffset", t);
125
+ }
126
+ set font(t) {
127
+ this.c("font", t);
128
+ }
129
+ set textAlign(t) {
130
+ this.c("textAlign", t);
131
+ }
132
+ set textBaseline(t) {
133
+ this.c("textBaseline", t);
134
+ }
135
+ set globalAlpha(t) {
136
+ this.c("globalAlpha", t);
137
+ }
138
+ set globalCompositeOperation(t) {
139
+ this.c("globalCompositeOperation", t);
140
+ }
141
+ set shadowBlur(t) {
142
+ this.c("shadowBlur", t);
143
+ }
144
+ set shadowColor(t) {
145
+ this.c("shadowColor", t);
146
+ }
147
+ set shadowOffsetX(t) {
148
+ this.c("shadowOffsetX", t);
149
+ }
150
+ set shadowOffsetY(t) {
151
+ this.c("shadowOffsetY", t);
152
+ }
153
+ // Property getters (return local cached values)
154
+ get fillStyle() {
155
+ return this.b.fillStyle ?? "#000";
156
+ }
157
+ get strokeStyle() {
158
+ return this.b.strokeStyle ?? "#000";
159
+ }
160
+ get lineWidth() {
161
+ return this.b.lineWidth ?? 1;
162
+ }
163
+ get lineCap() {
164
+ return this.b.lineCap ?? "butt";
165
+ }
166
+ get lineJoin() {
167
+ return this.b.lineJoin ?? "miter";
168
+ }
169
+ get miterLimit() {
170
+ return this.b.miterLimit ?? 10;
171
+ }
172
+ get lineDashOffset() {
173
+ return this.b.lineDashOffset ?? 0;
174
+ }
175
+ get font() {
176
+ return this.b.font ?? "10px sans-serif";
177
+ }
178
+ get textAlign() {
179
+ return this.b.textAlign ?? "start";
180
+ }
181
+ get textBaseline() {
182
+ return this.b.textBaseline ?? "alphabetic";
183
+ }
184
+ get globalAlpha() {
185
+ return this.b.globalAlpha ?? 1;
186
+ }
187
+ get globalCompositeOperation() {
188
+ return this.b.globalCompositeOperation ?? "source-over";
189
+ }
190
+ get shadowBlur() {
191
+ return this.b.shadowBlur ?? 0;
192
+ }
193
+ get shadowColor() {
194
+ return this.b.shadowColor ?? "rgba(0, 0, 0, 0)";
195
+ }
196
+ get shadowOffsetX() {
197
+ return this.b.shadowOffsetX ?? 0;
198
+ }
199
+ get shadowOffsetY() {
200
+ return this.b.shadowOffsetY ?? 0;
201
+ }
202
+ }
203
+ class L {
204
+ c = [];
205
+ a;
206
+ b;
207
+ /**
208
+ * @param width Canvas width in pixels
209
+ * @param height Canvas height in pixels
210
+ */
211
+ constructor(t, e) {
212
+ this.b = F(t, e), this.a = new Float32Array(this.b);
213
+ }
214
+ /** Push current matrix onto the stack. No _ prefix: cross-file. */
215
+ save() {
216
+ this.c.push(new Float32Array(this.a));
217
+ }
218
+ /** Pop and restore the top matrix. No _ prefix: cross-file. */
219
+ restore() {
220
+ this.c.length > 0 && (this.a = this.c.pop());
221
+ }
222
+ /** Translate the current matrix. No _ prefix: cross-file. */
223
+ translate(t, e) {
224
+ const s = this.a;
225
+ s[6] += s[0] * t + s[3] * e, s[7] += s[1] * t + s[4] * e;
226
+ }
227
+ /** Rotate the current matrix by angle (radians). No _ prefix: cross-file. */
228
+ rotate(t) {
229
+ const e = Math.cos(t), s = Math.sin(t), i = this.a, r = i[0], a = i[1], h = i[3], o = i[4];
230
+ i[0] = r * e + h * s, i[1] = a * e + o * s, i[3] = r * -s + h * e, i[4] = a * -s + o * e;
231
+ }
232
+ /** Scale the current matrix. No _ prefix: cross-file. */
233
+ scale(t, e) {
234
+ const s = this.a;
235
+ s[0] *= t, s[1] *= t, s[3] *= e, s[4] *= e;
236
+ }
237
+ /**
238
+ * Multiply current matrix by an arbitrary 2D affine transform.
239
+ * Canvas 2D transform(a, b, c, d, e, f) matrix:
240
+ * a c e
241
+ * b d f
242
+ * 0 0 1
243
+ * No _ prefix: cross-file.
244
+ */
245
+ transform(t, e, s, i, r, a) {
246
+ const h = this.a, o = h[0], c = h[1], l = h[3], f = h[4], b = h[6], u = h[7];
247
+ h[0] = o * t + l * e, h[1] = c * t + f * e, h[3] = o * s + l * i, h[4] = c * s + f * i, h[6] = o * r + l * a + b, h[7] = c * r + f * a + u;
248
+ }
249
+ /**
250
+ * Reset to projection then apply the given affine transform.
251
+ * Canvas 2D setTransform(a, b, c, d, e, f).
252
+ * No _ prefix: cross-file.
253
+ */
254
+ setTransform(t, e, s, i, r, a) {
255
+ this.a.set(this.b), this.transform(t, e, s, i, r, a);
256
+ }
257
+ /** Reset to the base orthographic projection. No _ prefix: cross-file. */
258
+ resetTransform() {
259
+ this.a.set(this.b);
260
+ }
261
+ /**
262
+ * Update canvas dimensions (e.g., on resize).
263
+ * Recomputes the projection and resets the current matrix.
264
+ * No _ prefix: cross-file.
265
+ */
266
+ resize(t, e) {
267
+ this.b = F(t, e), this.a = new Float32Array(this.b), this.c = [];
268
+ }
269
+ /**
270
+ * Returns the current 3×3 matrix for gl.uniformMatrix3fv.
271
+ * No _ prefix: cross-file.
272
+ */
273
+ getMatrix() {
274
+ return this.a;
275
+ }
276
+ }
277
+ function F(n, t) {
278
+ return new Float32Array([
279
+ 2 / n,
280
+ 0,
281
+ 0,
282
+ // column 0
283
+ 0,
284
+ -2 / t,
285
+ 0,
286
+ // column 1
287
+ -1,
288
+ 1,
289
+ 1
290
+ // column 2
291
+ ]);
292
+ }
293
+ const B = {
294
+ black: 255,
295
+ white: 4294967295,
296
+ red: 4278190335,
297
+ green: 8388863,
298
+ blue: 65535,
299
+ yellow: 4294902015,
300
+ cyan: 16777215,
301
+ magenta: 4278255615,
302
+ orange: 4289003775,
303
+ transparent: 0
304
+ }, x = /* @__PURE__ */ new Map();
305
+ function g(n) {
306
+ const t = x.get(n);
307
+ if (t) return t;
308
+ const e = U(n);
309
+ return x.set(n, e), e;
310
+ }
311
+ function U(n) {
312
+ const t = n.trim();
313
+ if (t.charCodeAt(0) === 35)
314
+ return k(t);
315
+ if (t.charCodeAt(0) === 114)
316
+ return y(t);
317
+ const e = B[t.toLowerCase()];
318
+ return e !== void 0 ? new Float32Array([
319
+ (e >>> 24 & 255) / 255,
320
+ (e >>> 16 & 255) / 255,
321
+ (e >>> 8 & 255) / 255,
322
+ (e & 255) / 255
323
+ ]) : new Float32Array([0, 0, 0, 1]);
324
+ }
325
+ function k(n) {
326
+ const t = n.length;
327
+ if (t === 4) {
328
+ const e = parseInt(n[1], 16), s = parseInt(n[2], 16), i = parseInt(n[3], 16);
329
+ return new Float32Array([
330
+ e * 17 / 255,
331
+ s * 17 / 255,
332
+ i * 17 / 255,
333
+ 1
334
+ ]);
335
+ }
336
+ if (t === 7) {
337
+ const e = parseInt(n.slice(1, 3), 16), s = parseInt(n.slice(3, 5), 16), i = parseInt(n.slice(5, 7), 16);
338
+ return new Float32Array([e / 255, s / 255, i / 255, 1]);
339
+ }
340
+ if (t === 9) {
341
+ const e = parseInt(n.slice(1, 3), 16), s = parseInt(n.slice(3, 5), 16), i = parseInt(n.slice(5, 7), 16), r = parseInt(n.slice(7, 9), 16);
342
+ return new Float32Array([e / 255, s / 255, i / 255, r / 255]);
343
+ }
344
+ if (t === 5) {
345
+ const e = parseInt(n[1], 16), s = parseInt(n[2], 16), i = parseInt(n[3], 16), r = parseInt(n[4], 16);
346
+ return new Float32Array([
347
+ e * 17 / 255,
348
+ s * 17 / 255,
349
+ i * 17 / 255,
350
+ r * 17 / 255
351
+ ]);
352
+ }
353
+ return new Float32Array([0, 0, 0, 1]);
354
+ }
355
+ function y(n) {
356
+ const t = n.indexOf("("), e = n.lastIndexOf(")");
357
+ if (t === -1 || e === -1) return new Float32Array([0, 0, 0, 1]);
358
+ const s = n.slice(t + 1, e).split(","), i = parseFloat(s[0]) / 255, r = parseFloat(s[1]) / 255, a = parseFloat(s[2]) / 255, h = s.length >= 4 ? parseFloat(s[3]) : 1;
359
+ return new Float32Array([i, r, a, h]);
360
+ }
361
+ const D = `
362
+ attribute vec2 a_position;
363
+ uniform mat3 u_matrix;
364
+ void main() {
365
+ vec3 pos = u_matrix * vec3(a_position, 1.0);
366
+ gl_Position = vec4(pos.xy, 0.0, 1.0);
367
+ }
368
+ `, I = `
369
+ precision mediump float;
370
+ uniform vec4 u_color;
371
+ void main() {
372
+ gl_FragColor = u_color;
373
+ }
374
+ `, M = `
375
+ attribute vec2 a_position;
376
+ attribute vec2 a_texCoord;
377
+ uniform mat3 u_matrix;
378
+ varying vec2 v_texCoord;
379
+ void main() {
380
+ vec3 pos = u_matrix * vec3(a_position, 1.0);
381
+ gl_Position = vec4(pos.xy, 0.0, 1.0);
382
+ v_texCoord = a_texCoord;
383
+ }
384
+ `, O = `
385
+ precision mediump float;
386
+ varying vec2 v_texCoord;
387
+ uniform sampler2D u_texture;
388
+ uniform vec4 u_color;
389
+ void main() {
390
+ vec4 texel = texture2D(u_texture, v_texCoord);
391
+ gl_FragColor = texel * u_color;
392
+ }
393
+ `, N = `
394
+ attribute vec2 a_position;
395
+ varying vec2 v_texCoord;
396
+ void main() {
397
+ v_texCoord = a_position;
398
+ gl_Position = vec4(a_position * 2.0 - 1.0, 0, 1);
399
+ }
400
+ `, X = `
401
+ precision mediump float;
402
+ varying vec2 v_texCoord;
403
+ uniform sampler2D u_texture;
404
+ void main() {
405
+ gl_FragColor = texture2D(u_texture, v_texCoord);
406
+ }
407
+ `;
408
+ class G {
409
+ c;
410
+ F;
411
+ A;
412
+ // Transform
413
+ a;
414
+ // State
415
+ i = new Float32Array([0, 0, 0, 1]);
416
+ d = new Float32Array([0, 0, 0, 1]);
417
+ f = 1;
418
+ e = 1;
419
+ g = "10px sans-serif";
420
+ j = "start";
421
+ k = "alphabetic";
422
+ o = "butt";
423
+ p = "miter";
424
+ G = [];
425
+ // Path state
426
+ t = [];
427
+ // flat: x0,y0,x1,y1,...
428
+ l = 0;
429
+ m = 0;
430
+ u = 0;
431
+ v = 0;
432
+ // WebGL resources
433
+ q;
434
+ B;
435
+ r;
436
+ C;
437
+ D;
438
+ s;
439
+ // Text rendering surface
440
+ b;
441
+ H;
442
+ // Temp arrays to avoid allocation in hot path
443
+ X = new Float32Array(4);
444
+ constructor(t, e, s) {
445
+ this.c = t, this.F = e, this.A = s, this.a = new L(e, s), this.q = this.I(
446
+ D,
447
+ I,
448
+ !1
449
+ ), this.B = this.I(
450
+ M,
451
+ O,
452
+ !0
453
+ ), this.r = t.createBuffer(), t.bindBuffer(t.ARRAY_BUFFER, this.r), t.bufferData(t.ARRAY_BUFFER, new Float32Array([
454
+ 0,
455
+ 0,
456
+ 1,
457
+ 0,
458
+ 0,
459
+ 1,
460
+ 0,
461
+ 1,
462
+ 1,
463
+ 0,
464
+ 1,
465
+ 1
466
+ ]), t.STATIC_DRAW), this.C = t.createBuffer(), this.D = t.createBuffer(), this.s = t.createTexture(), t.bindTexture(t.TEXTURE_2D, this.s), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), this.b = new OffscreenCanvas(512, 128), this.H = this.b.getContext("2d"), t.enable(t.BLEND), t.blendFunc(t.SRC_ALPHA, t.ONE_MINUS_SRC_ALPHA);
467
+ }
468
+ /**
469
+ * Execute a batch of Canvas 2D commands as WebGL draw calls.
470
+ * No _ prefix: called cross-file from webgl-renderer.
471
+ */
472
+ executeBatch(t) {
473
+ for (const e of t)
474
+ e.type === "property" ? this.L(e.name, e.value) : this.M(e.name, e.args);
475
+ }
476
+ /**
477
+ * Update canvas dimensions on resize.
478
+ * No _ prefix: called cross-file.
479
+ */
480
+ resize(t, e) {
481
+ this.F = t, this.A = e, this.a.resize(t, e);
482
+ }
483
+ /**
484
+ * Clean up WebGL resources.
485
+ * No _ prefix: called cross-file.
486
+ */
487
+ destroy() {
488
+ const t = this.c;
489
+ t.deleteBuffer(this.r), t.deleteBuffer(this.C), t.deleteBuffer(this.D), t.deleteTexture(this.s), t.deleteProgram(this.q.n), t.deleteProgram(this.B.n);
490
+ }
491
+ // -------------------------------------------------------------------------
492
+ // Private: property handling
493
+ // -------------------------------------------------------------------------
494
+ L(t, e) {
495
+ switch (t) {
496
+ case "fillStyle":
497
+ this.i = g(e);
498
+ break;
499
+ case "strokeStyle":
500
+ this.d = g(e);
501
+ break;
502
+ case "lineWidth":
503
+ this.f = e;
504
+ break;
505
+ case "globalAlpha":
506
+ this.e = e;
507
+ break;
508
+ case "font":
509
+ this.g = e;
510
+ break;
511
+ case "textAlign":
512
+ this.j = e;
513
+ break;
514
+ case "textBaseline":
515
+ this.k = e;
516
+ break;
517
+ case "lineCap":
518
+ this.o = e;
519
+ break;
520
+ case "lineJoin":
521
+ this.p = e;
522
+ break;
523
+ }
524
+ }
525
+ // -------------------------------------------------------------------------
526
+ // Private: method dispatch
527
+ // -------------------------------------------------------------------------
528
+ M(t, e) {
529
+ switch (t) {
530
+ // Rectangles
531
+ case "clearRect":
532
+ this.N(e[0], e[1], e[2], e[3]);
533
+ break;
534
+ case "fillRect":
535
+ this.O(e[0], e[1], e[2], e[3]);
536
+ break;
537
+ case "strokeRect":
538
+ this.P(e[0], e[1], e[2], e[3]);
539
+ break;
540
+ // Text
541
+ case "fillText":
542
+ this.Q(e[0], e[1], e[2]);
543
+ break;
544
+ case "strokeText":
545
+ this.R(e[0], e[1], e[2]);
546
+ break;
547
+ // Path
548
+ case "beginPath":
549
+ this.t = [];
550
+ break;
551
+ case "closePath":
552
+ (this.l !== this.u || this.m !== this.v) && (this.t.push(
553
+ this.l,
554
+ this.m,
555
+ this.u,
556
+ this.v
557
+ ), this.l = this.u, this.m = this.v);
558
+ break;
559
+ case "moveTo":
560
+ this.l = this.u = e[0], this.m = this.v = e[1];
561
+ break;
562
+ case "lineTo": {
563
+ const s = e[0], i = e[1];
564
+ this.t.push(this.l, this.m, s, i), this.l = s, this.m = i;
565
+ break;
566
+ }
567
+ case "stroke":
568
+ this.S();
569
+ break;
570
+ case "fill":
571
+ break;
572
+ // State
573
+ case "save":
574
+ this.T();
575
+ break;
576
+ case "restore":
577
+ this.U();
578
+ break;
579
+ // Transforms
580
+ case "translate":
581
+ this.a.translate(e[0], e[1]);
582
+ break;
583
+ case "rotate":
584
+ this.a.rotate(e[0]);
585
+ break;
586
+ case "scale":
587
+ this.a.scale(e[0], e[1]);
588
+ break;
589
+ case "transform":
590
+ this.a.transform(
591
+ e[0],
592
+ e[1],
593
+ e[2],
594
+ e[3],
595
+ e[4],
596
+ e[5]
597
+ );
598
+ break;
599
+ case "setTransform":
600
+ this.a.setTransform(
601
+ e[0],
602
+ e[1],
603
+ e[2],
604
+ e[3],
605
+ e[4],
606
+ e[5]
607
+ );
608
+ break;
609
+ case "resetTransform":
610
+ this.a.resetTransform();
611
+ break;
612
+ }
613
+ }
614
+ // -------------------------------------------------------------------------
615
+ // Private: drawing operations
616
+ // -------------------------------------------------------------------------
617
+ N(t, e, s, i) {
618
+ const r = this.c;
619
+ r.enable(r.SCISSOR_TEST), r.scissor(t, this.A - e - i, s, i), r.clearColor(0, 0, 0, 0), r.clear(r.COLOR_BUFFER_BIT), r.disable(r.SCISSOR_TEST);
620
+ }
621
+ O(t, e, s, i) {
622
+ const r = this.c, a = this.q;
623
+ r.useProgram(a.n), this.a.save(), this.a.translate(t, e), this.a.scale(s, i), r.uniformMatrix3fv(a.w, !1, this.a.getMatrix()), this.x(a.y, this.i, this.e), this.a.restore(), r.bindBuffer(r.ARRAY_BUFFER, this.r), r.enableVertexAttribArray(a.h), r.vertexAttribPointer(a.h, 2, r.FLOAT, !1, 0, 0), r.drawArrays(r.TRIANGLES, 0, 6);
624
+ }
625
+ P(t, e, s, i) {
626
+ const r = this.f, a = r / 2;
627
+ this.z(t - a, e - a, s + r, r, this.d), this.z(t - a, e + i - a, s + r, r, this.d), this.z(t - a, e + a, r, i - r, this.d), this.z(t + s - a, e + a, r, i - r, this.d);
628
+ }
629
+ /** Internal: draw a filled rect with a specific color (used by strokeRect). */
630
+ z(t, e, s, i, r) {
631
+ const a = this.c, h = this.q;
632
+ a.useProgram(h.n), this.a.save(), this.a.translate(t, e), this.a.scale(s, i), a.uniformMatrix3fv(h.w, !1, this.a.getMatrix()), this.x(h.y, r, this.e), this.a.restore(), a.bindBuffer(a.ARRAY_BUFFER, this.r), a.enableVertexAttribArray(h.h), a.vertexAttribPointer(h.h, 2, a.FLOAT, !1, 0, 0), a.drawArrays(a.TRIANGLES, 0, 6);
633
+ }
634
+ S() {
635
+ const t = this.t;
636
+ if (t.length === 0) return;
637
+ const e = this.c, s = this.q, i = Math.max(this.f / 2, 0.5), r = t.length / 4, a = new Float32Array(r * 12);
638
+ for (let h = 0, o = 0; h < t.length; h += 4) {
639
+ const c = t[h], l = t[h + 1], f = t[h + 2], b = t[h + 3], u = f - c, m = b - l, E = Math.sqrt(u * u + m * m);
640
+ if (E < 1e-3) continue;
641
+ const d = -m / E * i, A = u / E * i, S = c + d, w = l + A, T = c - d, R = l - A, _ = f + d, p = b + A, C = f - d, P = b - A;
642
+ a[o++] = S, a[o++] = w, a[o++] = T, a[o++] = R, a[o++] = _, a[o++] = p, a[o++] = T, a[o++] = R, a[o++] = C, a[o++] = P, a[o++] = _, a[o++] = p;
643
+ }
644
+ e.bindBuffer(e.ARRAY_BUFFER, this.C), e.bufferData(e.ARRAY_BUFFER, a, e.DYNAMIC_DRAW), e.useProgram(s.n), e.uniformMatrix3fv(s.w, !1, this.a.getMatrix()), this.x(s.y, this.d, this.e), e.enableVertexAttribArray(s.h), e.vertexAttribPointer(s.h, 2, e.FLOAT, !1, 0, 0), e.drawArrays(e.TRIANGLES, 0, r * 6);
645
+ }
646
+ Q(t, e, s) {
647
+ this.J(t, e, s, "fill");
648
+ }
649
+ R(t, e, s) {
650
+ this.J(t, e, s, "stroke");
651
+ }
652
+ J(t, e, s, i) {
653
+ const r = this.c, a = this.H;
654
+ a.font = this.g, a.textAlign = "left", a.textBaseline = "top";
655
+ const h = a.measureText(t), o = Math.ceil(h.width) + 4, c = H(this.g), l = Math.ceil(c * 1.5) + 4;
656
+ if (o <= 0 || l <= 0) return;
657
+ (this.b.width < o || this.b.height < l) && (this.b.width = Math.max(this.b.width, o), this.b.height = Math.max(this.b.height, l), a.font = this.g, a.textAlign = "left", a.textBaseline = "top"), a.clearRect(0, 0, this.b.width, this.b.height), i === "fill" ? (a.fillStyle = "white", a.fillText(t, 2, 2)) : (a.strokeStyle = "white", a.lineWidth = this.f, a.strokeText(t, 2, 2)), r.bindTexture(r.TEXTURE_2D, this.s), r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1), r.texImage2D(
658
+ r.TEXTURE_2D,
659
+ 0,
660
+ r.RGBA,
661
+ r.RGBA,
662
+ r.UNSIGNED_BYTE,
663
+ this.b
664
+ ), r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
665
+ const f = i === "fill" ? this.i : this.d;
666
+ let b = e - 2, u = s - 2;
667
+ switch (this.j) {
668
+ case "center":
669
+ b -= o / 2;
670
+ break;
671
+ case "right":
672
+ case "end":
673
+ b -= o;
674
+ break;
675
+ }
676
+ switch (this.k) {
677
+ case "top":
678
+ break;
679
+ case "middle":
680
+ u -= l / 2;
681
+ break;
682
+ case "alphabetic":
683
+ case "ideographic":
684
+ u -= c;
685
+ break;
686
+ case "bottom":
687
+ case "hanging":
688
+ u -= l;
689
+ break;
690
+ }
691
+ this.V(
692
+ b,
693
+ u,
694
+ o,
695
+ l,
696
+ o / this.b.width,
697
+ l / this.b.height,
698
+ f
699
+ );
700
+ }
701
+ V(t, e, s, i, r, a, h) {
702
+ const o = this.c, c = this.B, l = new Float32Array([
703
+ t,
704
+ e,
705
+ 0,
706
+ 0,
707
+ t + s,
708
+ e,
709
+ r,
710
+ 0,
711
+ t,
712
+ e + i,
713
+ 0,
714
+ a,
715
+ t,
716
+ e + i,
717
+ 0,
718
+ a,
719
+ t + s,
720
+ e,
721
+ r,
722
+ 0,
723
+ t + s,
724
+ e + i,
725
+ r,
726
+ a
727
+ ]);
728
+ o.bindBuffer(o.ARRAY_BUFFER, this.D), o.bufferData(o.ARRAY_BUFFER, l, o.DYNAMIC_DRAW), o.useProgram(c.n), o.uniformMatrix3fv(c.w, !1, this.a.getMatrix()), this.x(c.y, h, this.e), o.blendFunc(o.ONE, o.ONE_MINUS_SRC_ALPHA);
729
+ const f = 16;
730
+ o.enableVertexAttribArray(c.h), o.vertexAttribPointer(c.h, 2, o.FLOAT, !1, f, 0), o.enableVertexAttribArray(c.E), o.vertexAttribPointer(c.E, 2, o.FLOAT, !1, f, 8), o.activeTexture(o.TEXTURE0), o.bindTexture(o.TEXTURE_2D, this.s), o.uniform1i(c.W, 0), o.drawArrays(o.TRIANGLES, 0, 6), o.blendFunc(o.SRC_ALPHA, o.ONE_MINUS_SRC_ALPHA), o.disableVertexAttribArray(c.E);
731
+ }
732
+ // -------------------------------------------------------------------------
733
+ // Private: state management
734
+ // -------------------------------------------------------------------------
735
+ T() {
736
+ this.a.save(), this.G.push({
737
+ i: new Float32Array(this.i),
738
+ d: new Float32Array(this.d),
739
+ f: this.f,
740
+ e: this.e,
741
+ g: this.g,
742
+ j: this.j,
743
+ k: this.k,
744
+ o: this.o,
745
+ p: this.p
746
+ });
747
+ }
748
+ U() {
749
+ this.a.restore();
750
+ const t = this.G.pop();
751
+ t && (this.i = t.i, this.d = t.d, this.f = t.f, this.e = t.e, this.g = t.g, this.j = t.j, this.k = t.k, this.o = t.o, this.p = t.p);
752
+ }
753
+ // -------------------------------------------------------------------------
754
+ // Private: WebGL helpers
755
+ // -------------------------------------------------------------------------
756
+ x(t, e, s) {
757
+ const i = e[3] * s;
758
+ this.c.uniform4f(t, e[0], e[1], e[2], i);
759
+ }
760
+ I(t, e, s) {
761
+ const i = this.c, r = this.K(i.VERTEX_SHADER, t), a = this.K(i.FRAGMENT_SHADER, e), h = i.createProgram();
762
+ if (i.attachShader(h, r), i.attachShader(h, a), i.linkProgram(h), !i.getProgramParameter(h, i.LINK_STATUS))
763
+ throw new Error("Shader link failed: " + i.getProgramInfoLog(h));
764
+ return i.deleteShader(r), i.deleteShader(a), {
765
+ n: h,
766
+ w: i.getUniformLocation(h, "u_matrix"),
767
+ y: i.getUniformLocation(h, "u_color"),
768
+ W: s ? i.getUniformLocation(h, "u_texture") : null,
769
+ h: i.getAttribLocation(h, "a_position"),
770
+ E: s ? i.getAttribLocation(h, "a_texCoord") : -1
771
+ };
772
+ }
773
+ K(t, e) {
774
+ const s = this.c, i = s.createShader(t);
775
+ if (s.shaderSource(i, e), s.compileShader(i), !s.getShaderParameter(i, s.COMPILE_STATUS)) {
776
+ const r = s.getShaderInfoLog(i);
777
+ throw s.deleteShader(i), new Error("Shader compile failed: " + r);
778
+ }
779
+ return i;
780
+ }
781
+ }
782
+ function H(n) {
783
+ const t = n.match(/(\d+(?:\.\d+)?)\s*px/);
784
+ return t ? parseFloat(t[1]) : 10;
785
+ }
786
+ class W {
787
+ b;
788
+ a;
789
+ h;
790
+ i;
791
+ d = null;
792
+ // Triple buffer: three FBOs with color texture attachments
793
+ c;
794
+ j = 0;
795
+ e = 1;
796
+ w = 2;
797
+ o = !1;
798
+ // Display program
799
+ f;
800
+ // Fullscreen quad VBO
801
+ g;
802
+ k = -1;
803
+ constructor(t) {
804
+ this.a = t, this.i = new v();
805
+ const e = t.getContext("webgl2", {
806
+ alpha: !1,
807
+ antialias: !1,
808
+ desynchronized: !0,
809
+ preserveDrawingBuffer: !0,
810
+ powerPreference: "high-performance"
811
+ });
812
+ if (!e)
813
+ throw new Error("WebGL2 not supported");
814
+ this.b = e, e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), this.s(), this.h = new G(e, t.width, t.height), this.f = this.t(
815
+ N,
816
+ X
817
+ ), this.u(), this.v(), this.startDisplay();
818
+ }
819
+ // -------------------------------------------------------------------------
820
+ // Public API (no _ prefix: cross-file access safe from mangleProps)
821
+ // -------------------------------------------------------------------------
822
+ /** Get the CanvasAPI for recording draw commands. */
823
+ getCanvasAPI() {
824
+ return this.i;
825
+ }
826
+ /**
827
+ * Submit a batch of Canvas 2D commands to be rendered into the write FBO.
828
+ * After rendering, the write and ready FBOs are swapped so the display
829
+ * loop picks up the latest frame.
830
+ */
831
+ submitBatch(t) {
832
+ if (t.length === 0) return;
833
+ const e = this.b;
834
+ e.bindFramebuffer(e.FRAMEBUFFER, this.c[this.j].l), e.viewport(0, 0, this.a.width, this.a.height), this.h.executeBatch(t);
835
+ const s = this.j;
836
+ this.j = this.e, this.e = s, this.o = !0;
837
+ }
838
+ /** Start the passthrough RAF display loop with auto-flush. */
839
+ startDisplay() {
840
+ this.d === null && this.p();
841
+ }
842
+ /** Stop the passthrough RAF display loop. Last frame persists (preserveDrawingBuffer). */
843
+ stopDisplay() {
844
+ this.d !== null && (cancelAnimationFrame(this.d), this.d = null);
845
+ }
846
+ /** Returns the display canvas element. */
847
+ getCanvas() {
848
+ return this.a;
849
+ }
850
+ /** Get canvas dimensions. */
851
+ getCanvasSize() {
852
+ return { width: this.a.width, height: this.a.height };
853
+ }
854
+ /** Capture the current displayed frame as an ImageBitmap. */
855
+ screenshot() {
856
+ return this.q(), createImageBitmap(this.a);
857
+ }
858
+ /** Clean up all WebGL resources. */
859
+ destroy() {
860
+ this.stopDisplay();
861
+ const t = this.b;
862
+ this.h.destroy();
863
+ for (const s of this.c)
864
+ t.deleteFramebuffer(s.l), t.deleteTexture(s.m);
865
+ t.deleteProgram(this.f), t.deleteBuffer(this.g);
866
+ const e = t.getExtension("WEBGL_lose_context");
867
+ e && e.loseContext();
868
+ }
869
+ // -------------------------------------------------------------------------
870
+ // Extension points (used by maalata CRT display)
871
+ // -------------------------------------------------------------------------
872
+ /** Returns the WebGL2 context for external rendering (e.g. CRT shader). */
873
+ getGL() {
874
+ return this.b;
875
+ }
876
+ /** Returns the ready FBO's texture — the latest fully rendered frame. */
877
+ getReadyTexture() {
878
+ return this.c[this.e].m;
879
+ }
880
+ // -------------------------------------------------------------------------
881
+ // Private: display loop
882
+ // -------------------------------------------------------------------------
883
+ p() {
884
+ this.d = requestAnimationFrame(() => this.p());
885
+ const t = this.i.takeCommands();
886
+ t.length && this.submitBatch(t), this.q();
887
+ }
888
+ /**
889
+ * Render the ready FBO to the display canvas via passthrough shader.
890
+ * Called at vsync rate by RAF, or once synchronously for screenshots.
891
+ */
892
+ q() {
893
+ if (!this.o) return;
894
+ const t = this.b;
895
+ t.bindFramebuffer(t.FRAMEBUFFER, null), t.viewport(0, 0, this.a.width, this.a.height), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.c[this.e].m), t.useProgram(this.f), t.bindBuffer(t.ARRAY_BUFFER, this.g), t.enableVertexAttribArray(this.k), t.vertexAttribPointer(this.k, 2, t.FLOAT, !1, 0, 0), t.disable(t.BLEND), t.drawArrays(t.TRIANGLES, 0, 6), t.enable(t.BLEND);
896
+ }
897
+ // -------------------------------------------------------------------------
898
+ // Private: initialization
899
+ // -------------------------------------------------------------------------
900
+ s() {
901
+ this.c = [
902
+ this.n(),
903
+ this.n(),
904
+ this.n()
905
+ ];
906
+ }
907
+ n() {
908
+ const t = this.b, e = this.a.width, s = this.a.height, i = t.createFramebuffer();
909
+ t.bindFramebuffer(t.FRAMEBUFFER, i);
910
+ const r = t.createTexture();
911
+ t.bindTexture(t.TEXTURE_2D, r), t.texStorage2D(t.TEXTURE_2D, 1, t.RGBA8, e, s), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MIN_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_MAG_FILTER, t.LINEAR), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_S, t.CLAMP_TO_EDGE), t.texParameteri(t.TEXTURE_2D, t.TEXTURE_WRAP_T, t.CLAMP_TO_EDGE), t.framebufferTexture2D(t.FRAMEBUFFER, t.COLOR_ATTACHMENT0, t.TEXTURE_2D, r, 0);
912
+ const a = t.checkFramebufferStatus(t.FRAMEBUFFER);
913
+ if (a !== t.FRAMEBUFFER_COMPLETE)
914
+ throw new Error("Framebuffer incomplete: 0x" + a.toString(16));
915
+ return t.bindFramebuffer(t.FRAMEBUFFER, null), { l: i, m: r };
916
+ }
917
+ v() {
918
+ const t = this.b;
919
+ for (const e of this.c)
920
+ t.bindFramebuffer(t.FRAMEBUFFER, e.l), t.clearColor(0, 0, 0, 1), t.clear(t.COLOR_BUFFER_BIT);
921
+ t.bindFramebuffer(t.FRAMEBUFFER, null), t.clearColor(0, 0, 0, 1), t.clear(t.COLOR_BUFFER_BIT);
922
+ }
923
+ u() {
924
+ const t = this.b;
925
+ this.g = t.createBuffer(), t.bindBuffer(t.ARRAY_BUFFER, this.g), t.bufferData(t.ARRAY_BUFFER, new Float32Array([
926
+ 0,
927
+ 0,
928
+ 1,
929
+ 0,
930
+ 0,
931
+ 1,
932
+ 0,
933
+ 1,
934
+ 1,
935
+ 0,
936
+ 1,
937
+ 1
938
+ ]), t.STATIC_DRAW), this.k = t.getAttribLocation(this.f, "a_position");
939
+ }
940
+ // -------------------------------------------------------------------------
941
+ // Private: shader helpers
942
+ // -------------------------------------------------------------------------
943
+ t(t, e) {
944
+ const s = this.b, i = this.r(s.VERTEX_SHADER, t), r = this.r(s.FRAGMENT_SHADER, e), a = s.createProgram();
945
+ if (s.attachShader(a, i), s.attachShader(a, r), s.linkProgram(a), !s.getProgramParameter(a, s.LINK_STATUS))
946
+ throw new Error("Shader link failed: " + s.getProgramInfoLog(a));
947
+ return s.deleteShader(i), s.deleteShader(r), a;
948
+ }
949
+ r(t, e) {
950
+ const s = this.b, i = s.createShader(t);
951
+ if (s.shaderSource(i, e), s.compileShader(i), !s.getShaderParameter(i, s.COMPILE_STATUS)) {
952
+ const r = s.getShaderInfoLog(i);
953
+ throw s.deleteShader(i), new Error("Shader compile failed: " + r);
954
+ }
955
+ return i;
956
+ }
957
+ }
958
+ export {
959
+ v as CanvasAPI,
960
+ L as MatrixStack,
961
+ W as UltrafastRenderer,
962
+ g as parseColor
963
+ };