q5 3.9.1 → 4.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.
- package/README.md +17 -13
- package/deno.json +2 -2
- package/package.json +6 -4
- package/q5.d.ts +1144 -791
- package/q5.js +359 -163
- package/q5.min.js +2 -2
- package/teach/accessibility.html +503 -0
- package/defs/q5-c2d-es.d.ts +0 -4049
- package/defs/q5-c2d.d.ts +0 -3974
- package/defs/q5-es.d.ts +0 -4345
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version
|
|
3
|
+
* @version 4.0
|
|
4
4
|
* @author quinton-ashley
|
|
5
5
|
* @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
|
|
6
6
|
* @license LGPL-3.0
|
|
@@ -131,14 +131,16 @@ function Q5(scope, parent, renderer) {
|
|
|
131
131
|
$.resetMatrix();
|
|
132
132
|
|
|
133
133
|
if ($._beginRender) $._beginRender();
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
try {
|
|
136
|
+
await runHooks('predraw');
|
|
136
137
|
await $.draw();
|
|
137
138
|
} catch (e) {
|
|
138
139
|
if (!Q5.errorTolerant) $.noLoop();
|
|
139
140
|
if ($._fes) $._fes(e);
|
|
140
141
|
throw e;
|
|
141
142
|
}
|
|
143
|
+
|
|
142
144
|
await runHooks('postdraw');
|
|
143
145
|
await $.postProcess();
|
|
144
146
|
if ($._render) $._render();
|
|
@@ -315,11 +317,10 @@ function Q5(scope, parent, renderer) {
|
|
|
315
317
|
$.preload();
|
|
316
318
|
}
|
|
317
319
|
|
|
318
|
-
// wait for the user to define setup, update, or draw
|
|
319
320
|
await Promise.race([
|
|
320
321
|
new Promise((resolve) => {
|
|
321
322
|
function checkUserFns() {
|
|
322
|
-
if ($.setup || $.update || $.draw || t.setup || t.update || t.draw) {
|
|
323
|
+
if ($._disablePreload || $.setup || $.update || $.draw || t.setup || t.update || t.draw) {
|
|
323
324
|
resolve();
|
|
324
325
|
} else if (!$._setupDone) {
|
|
325
326
|
// render during loading
|
|
@@ -336,7 +337,7 @@ function Q5(scope, parent, renderer) {
|
|
|
336
337
|
new Promise((resolve) => {
|
|
337
338
|
setTimeout(() => {
|
|
338
339
|
// if not loading
|
|
339
|
-
if (!$._loaders.length) resolve();
|
|
340
|
+
if (!$._loaders.length && !$._g?._loaders.length) resolve();
|
|
340
341
|
}, 500);
|
|
341
342
|
})
|
|
342
343
|
]);
|
|
@@ -468,7 +469,7 @@ if (typeof window == 'object') {
|
|
|
468
469
|
window.WEBGPU = 'webgpu';
|
|
469
470
|
} else global.window = 0;
|
|
470
471
|
|
|
471
|
-
Q5.version = Q5.VERSION = '
|
|
472
|
+
Q5.version = Q5.VERSION = '4.0';
|
|
472
473
|
|
|
473
474
|
if (typeof document == 'object') {
|
|
474
475
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -637,7 +638,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
637
638
|
|
|
638
639
|
let m = Q5._libMap;
|
|
639
640
|
|
|
640
|
-
if (m
|
|
641
|
+
if (m?.width) {
|
|
641
642
|
q[m.width] = w;
|
|
642
643
|
q[m.height] = h;
|
|
643
644
|
q[m.halfWidth] = q.halfWidth;
|
|
@@ -1602,7 +1603,11 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1602
1603
|
$._retint = true;
|
|
1603
1604
|
|
|
1604
1605
|
if ($._owner?._makeDrawable) {
|
|
1605
|
-
$._texture
|
|
1606
|
+
if ($._owner._texturesToDestroy && $._texture) {
|
|
1607
|
+
$._owner._texturesToDestroy.push($._texture);
|
|
1608
|
+
} else {
|
|
1609
|
+
$._texture.destroy();
|
|
1610
|
+
}
|
|
1606
1611
|
delete $._texture;
|
|
1607
1612
|
$._owner._makeDrawable($);
|
|
1608
1613
|
}
|
|
@@ -1775,24 +1780,26 @@ Q5.Image = class {
|
|
|
1775
1780
|
delete $.createCanvas;
|
|
1776
1781
|
$._loop = false;
|
|
1777
1782
|
|
|
1778
|
-
let
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1783
|
+
let m = Q5._libMap;
|
|
1784
|
+
if (m) {
|
|
1785
|
+
let imgFns = [
|
|
1786
|
+
'copy',
|
|
1787
|
+
'filter',
|
|
1788
|
+
'get',
|
|
1789
|
+
'set',
|
|
1790
|
+
'resize',
|
|
1791
|
+
'mask',
|
|
1792
|
+
'trim',
|
|
1793
|
+
'inset',
|
|
1794
|
+
'pixels',
|
|
1795
|
+
'loadPixels',
|
|
1796
|
+
'updatePixels',
|
|
1797
|
+
'smooth',
|
|
1798
|
+
'noSmooth'
|
|
1799
|
+
];
|
|
1800
|
+
for (let name of imgFns) {
|
|
1801
|
+
if (m[name]) $[m[name]] = $[name];
|
|
1802
|
+
}
|
|
1796
1803
|
}
|
|
1797
1804
|
}
|
|
1798
1805
|
get w() {
|
|
@@ -2100,7 +2107,7 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2100
2107
|
|
|
2101
2108
|
$.textStyle = (x) => {
|
|
2102
2109
|
if (!x) return emphasis;
|
|
2103
|
-
emphasis = x;
|
|
2110
|
+
$._textStyle = emphasis = x;
|
|
2104
2111
|
$._fontMod = true;
|
|
2105
2112
|
styleHash = -1;
|
|
2106
2113
|
};
|
|
@@ -2230,7 +2237,6 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2230
2237
|
if ($._textBaseline == 'middle') tY -= leading * (lines.length - 1) * 0.5;
|
|
2231
2238
|
else if ($._textBaseline == 'bottom') tY -= leading * (lines.length - 1);
|
|
2232
2239
|
} else {
|
|
2233
|
-
tX = 0;
|
|
2234
2240
|
tY = leading;
|
|
2235
2241
|
|
|
2236
2242
|
if (!img) {
|
|
@@ -2271,9 +2277,14 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2271
2277
|
|
|
2272
2278
|
ctx = img.ctx;
|
|
2273
2279
|
|
|
2280
|
+
ctx.textAlign = $._textAlign;
|
|
2281
|
+
if ($._textAlign == 'center') tX = img.width / 2;
|
|
2282
|
+
else if ($._textAlign == 'right') tX = img.width;
|
|
2283
|
+
else tX = 0;
|
|
2284
|
+
|
|
2274
2285
|
ctx.font = $.ctx.font;
|
|
2275
|
-
ctx.fillStyle = $._fill;
|
|
2276
|
-
ctx.strokeStyle = $._stroke;
|
|
2286
|
+
if ($._doFill && $._fillSet) ctx.fillStyle = $._fill;
|
|
2287
|
+
if ($._doStroke && $._strokeSet) ctx.strokeStyle = $._stroke;
|
|
2277
2288
|
ctx.lineWidth = $.ctx.lineWidth;
|
|
2278
2289
|
}
|
|
2279
2290
|
|
|
@@ -2317,7 +2328,11 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2317
2328
|
colorCache = styleCache[hash];
|
|
2318
2329
|
for (let c in colorCache) {
|
|
2319
2330
|
let _img = colorCache[c];
|
|
2320
|
-
if (_img._texture)
|
|
2331
|
+
if (_img._texture) {
|
|
2332
|
+
let owner = _img._owner || $;
|
|
2333
|
+
if (owner._texturesToDestroy) owner._texturesToDestroy.push(_img._texture);
|
|
2334
|
+
else _img._texture.destroy();
|
|
2335
|
+
}
|
|
2321
2336
|
delete colorCache[c];
|
|
2322
2337
|
}
|
|
2323
2338
|
}
|
|
@@ -2347,6 +2362,79 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
2347
2362
|
$.image(img, x, y);
|
|
2348
2363
|
$._imageMode = og;
|
|
2349
2364
|
};
|
|
2365
|
+
|
|
2366
|
+
$.textToPoints = (str, x = 0, y = 0, sampleRate = 0.1, density = 1) => {
|
|
2367
|
+
let pd = $._pixelDensity;
|
|
2368
|
+
$._pixelDensity = density;
|
|
2369
|
+
let img = $.createTextImage(str);
|
|
2370
|
+
$._pixelDensity = pd;
|
|
2371
|
+
|
|
2372
|
+
img.loadPixels();
|
|
2373
|
+
|
|
2374
|
+
let w = img.canvas.width,
|
|
2375
|
+
h = img.canvas.height;
|
|
2376
|
+
|
|
2377
|
+
let points = [];
|
|
2378
|
+
|
|
2379
|
+
let ta = $._textAlign,
|
|
2380
|
+
bl = $._textBaseline,
|
|
2381
|
+
offsetX = 0,
|
|
2382
|
+
offsetY = 0;
|
|
2383
|
+
|
|
2384
|
+
if (ta == 'center') offsetX = -w / 2;
|
|
2385
|
+
else if (ta == 'right') offsetX = -w;
|
|
2386
|
+
|
|
2387
|
+
if (bl == 'alphabetic') offsetY = -img._leading;
|
|
2388
|
+
else if (bl == 'middle') offsetY = -img._middle;
|
|
2389
|
+
else if (bl == 'bottom') offsetY = -img._bottom;
|
|
2390
|
+
else if (bl == 'top') offsetY = -img._top;
|
|
2391
|
+
|
|
2392
|
+
offsetY *= density;
|
|
2393
|
+
|
|
2394
|
+
let allPoints = [];
|
|
2395
|
+
|
|
2396
|
+
// Z-order curve (Morton code)
|
|
2397
|
+
const part1by1 = (n) => {
|
|
2398
|
+
n &= 0x0000ffff;
|
|
2399
|
+
n = (n | (n << 8)) & 0x00ff00ff;
|
|
2400
|
+
n = (n | (n << 4)) & 0x0f0f0f0f;
|
|
2401
|
+
n = (n | (n << 2)) & 0x33333333;
|
|
2402
|
+
n = (n | (n << 1)) & 0x55555555;
|
|
2403
|
+
return n;
|
|
2404
|
+
};
|
|
2405
|
+
|
|
2406
|
+
let r = Math.max(0.5, sampleRate);
|
|
2407
|
+
|
|
2408
|
+
for (let py = 0; py < h; py++) {
|
|
2409
|
+
for (let px = 0; px < w; px++) {
|
|
2410
|
+
let index = (py * w + px) * 4;
|
|
2411
|
+
|
|
2412
|
+
if ((r == 1 || $.random() < r) && img.pixels[index + 3] > 128) {
|
|
2413
|
+
allPoints.push({
|
|
2414
|
+
x: px,
|
|
2415
|
+
y: py,
|
|
2416
|
+
z: part1by1(px) | (part1by1(py) << 1)
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
let total = allPoints.length;
|
|
2423
|
+
let numPoints = total * sampleRate * (1 / r);
|
|
2424
|
+
|
|
2425
|
+
if (sampleRate < 1) allPoints.sort((a, b) => a.z - b.z);
|
|
2426
|
+
|
|
2427
|
+
let step = total / numPoints;
|
|
2428
|
+
for (let i = 0; i < total; i += step) {
|
|
2429
|
+
let p = allPoints[Math.floor(i)];
|
|
2430
|
+
points.push({
|
|
2431
|
+
x: (p.x + offsetX) / density + x,
|
|
2432
|
+
y: (p.y + offsetY) / density + y
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
return points;
|
|
2437
|
+
};
|
|
2350
2438
|
};
|
|
2351
2439
|
|
|
2352
2440
|
Q5.fonts = [];
|
|
@@ -3316,8 +3404,8 @@ Q5.modules.dom = ($, q) => {
|
|
|
3316
3404
|
return vid;
|
|
3317
3405
|
};
|
|
3318
3406
|
|
|
3319
|
-
$.
|
|
3320
|
-
$.
|
|
3407
|
+
$.findEl = (selector) => document.querySelector(selector);
|
|
3408
|
+
$.findEls = (selector) => document.querySelectorAll(selector);
|
|
3321
3409
|
};
|
|
3322
3410
|
Q5.modules.fes = ($) => {
|
|
3323
3411
|
$._fes = async (e) => {
|
|
@@ -3334,7 +3422,7 @@ Q5.modules.fes = ($) => {
|
|
|
3334
3422
|
idx = 0;
|
|
3335
3423
|
sep = '@';
|
|
3336
3424
|
}
|
|
3337
|
-
while (stackLines[idx].indexOf('q5') >= 0) idx++;
|
|
3425
|
+
while (idx > stackLines.length && stackLines[idx].indexOf('q5') >= 0) idx++;
|
|
3338
3426
|
|
|
3339
3427
|
let errFile = stackLines[idx].split(sep).at(-1);
|
|
3340
3428
|
if (errFile.startsWith('blob:')) errFile = errFile.slice(5);
|
|
@@ -3469,6 +3557,7 @@ Q5.modules.input = ($, q) => {
|
|
|
3469
3557
|
if ($._webgpu) {
|
|
3470
3558
|
x -= c.hw;
|
|
3471
3559
|
y -= c.hh;
|
|
3560
|
+
if (!$._flippedY) y *= -1;
|
|
3472
3561
|
}
|
|
3473
3562
|
} else {
|
|
3474
3563
|
x = e.clientX;
|
|
@@ -3582,18 +3671,24 @@ Q5.modules.input = ($, q) => {
|
|
|
3582
3671
|
$.keyIsDown = (v) => !!keysHeld[typeof v == 'string' ? v.toLowerCase() : v];
|
|
3583
3672
|
|
|
3584
3673
|
function getTouchInfo(touch) {
|
|
3585
|
-
const rect = $.canvas.getBoundingClientRect()
|
|
3586
|
-
|
|
3587
|
-
|
|
3674
|
+
const rect = $.canvas.getBoundingClientRect(),
|
|
3675
|
+
sx = $.canvas.scrollWidth / $.width || 1,
|
|
3676
|
+
sy = $.canvas.scrollHeight / $.height || 1;
|
|
3588
3677
|
let modX = 0,
|
|
3589
3678
|
modY = 0;
|
|
3590
3679
|
if ($._webgpu) {
|
|
3591
3680
|
modX = $.halfWidth;
|
|
3592
3681
|
modY = $.halfHeight;
|
|
3593
3682
|
}
|
|
3683
|
+
|
|
3684
|
+
let x = (touch.clientX - rect.left) / sx - modX,
|
|
3685
|
+
y = (touch.clientY - rect.top) / sy - modY;
|
|
3686
|
+
|
|
3687
|
+
if ($._webgpu && !$._flippedY) y *= -1;
|
|
3688
|
+
|
|
3594
3689
|
return {
|
|
3595
|
-
x
|
|
3596
|
-
y
|
|
3690
|
+
x,
|
|
3691
|
+
y,
|
|
3597
3692
|
id: touch.identifier
|
|
3598
3693
|
};
|
|
3599
3694
|
}
|
|
@@ -3781,6 +3876,7 @@ Q5.modules.math = ($, q) => {
|
|
|
3781
3876
|
return {
|
|
3782
3877
|
setSeed(val) {
|
|
3783
3878
|
jsr = seed = (val == null ? Math.random() * m : val) >>> 0;
|
|
3879
|
+
if (jsr === 0) jsr = 1;
|
|
3784
3880
|
},
|
|
3785
3881
|
getSeed() {
|
|
3786
3882
|
return seed;
|
|
@@ -5276,7 +5372,8 @@ struct Q5 {
|
|
|
5276
5372
|
mouseY: f32,
|
|
5277
5373
|
mouseIsPressed: f32,
|
|
5278
5374
|
keyCode: f32,
|
|
5279
|
-
keyIsPressed: f32
|
|
5375
|
+
keyIsPressed: f32,
|
|
5376
|
+
yUp: f32
|
|
5280
5377
|
}`;
|
|
5281
5378
|
|
|
5282
5379
|
$._g = $.createGraphics(1, 1, 'c2d');
|
|
@@ -5300,6 +5397,7 @@ struct Q5 {
|
|
|
5300
5397
|
$._pipelineConfigs = [];
|
|
5301
5398
|
$._pipelines = [];
|
|
5302
5399
|
$._buffers = [];
|
|
5400
|
+
$._texturesToDestroy = [];
|
|
5303
5401
|
|
|
5304
5402
|
// local variables used for slightly better performance
|
|
5305
5403
|
|
|
@@ -5398,7 +5496,8 @@ fn vertexMain(v: VertexParams) -> FragParams {
|
|
|
5398
5496
|
@fragment
|
|
5399
5497
|
fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
5400
5498
|
return textureSample(tex, samp, f.texCoord);
|
|
5401
|
-
}
|
|
5499
|
+
}
|
|
5500
|
+
`;
|
|
5402
5501
|
|
|
5403
5502
|
let frameShader = Q5.device.createShaderModule({
|
|
5404
5503
|
label: 'frameShader',
|
|
@@ -5619,17 +5718,32 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5619
5718
|
matrixIdx = 0,
|
|
5620
5719
|
matrixDirty = false; // tracks if the matrix has been modified
|
|
5621
5720
|
|
|
5622
|
-
// 4x4 identity matrix
|
|
5721
|
+
// 4x4 identity matrix
|
|
5623
5722
|
// prettier-ignore
|
|
5624
5723
|
matrices.push([
|
|
5625
5724
|
1, 0, 0, 0,
|
|
5626
|
-
0,
|
|
5725
|
+
0, 1, 0, 0,
|
|
5627
5726
|
0, 0, 1, 0,
|
|
5628
5727
|
0, 0, 0, 1
|
|
5629
5728
|
]);
|
|
5630
5729
|
|
|
5631
5730
|
transforms.set(matrices[0]);
|
|
5632
5731
|
|
|
5732
|
+
let flippedY = false,
|
|
5733
|
+
yDir = 1;
|
|
5734
|
+
|
|
5735
|
+
$.flipY = () => {
|
|
5736
|
+
$._flippedY = flippedY = !flippedY;
|
|
5737
|
+
yDir *= -1;
|
|
5738
|
+
|
|
5739
|
+
// edit the identity matrix to flip Y axis
|
|
5740
|
+
matrices[0][5] *= -1;
|
|
5741
|
+
transforms.set(matrices[0], 0);
|
|
5742
|
+
};
|
|
5743
|
+
|
|
5744
|
+
// current default is y-down for q5 WebGPU
|
|
5745
|
+
$.flipY();
|
|
5746
|
+
|
|
5633
5747
|
$.translate = (x, y) => {
|
|
5634
5748
|
if (!x && !y) return;
|
|
5635
5749
|
let m = matrix;
|
|
@@ -5887,20 +6001,20 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5887
6001
|
l = x;
|
|
5888
6002
|
r = x + w;
|
|
5889
6003
|
t = y;
|
|
5890
|
-
b = y
|
|
6004
|
+
b = y - h * yDir;
|
|
5891
6005
|
} else if (mode == 'center') {
|
|
5892
6006
|
let hw = w / 2,
|
|
5893
6007
|
hh = h / 2;
|
|
5894
6008
|
l = x - hw;
|
|
5895
6009
|
r = x + hw;
|
|
5896
|
-
t = y
|
|
5897
|
-
b = y
|
|
6010
|
+
t = y + hh * yDir;
|
|
6011
|
+
b = y - hh * yDir;
|
|
5898
6012
|
} else {
|
|
5899
6013
|
// CORNERS
|
|
5900
6014
|
l = x;
|
|
5901
6015
|
r = w;
|
|
5902
6016
|
t = y;
|
|
5903
|
-
b = h;
|
|
6017
|
+
b = -h * yDir;
|
|
5904
6018
|
}
|
|
5905
6019
|
|
|
5906
6020
|
boxCache[0] = l;
|
|
@@ -5930,7 +6044,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5930
6044
|
];
|
|
5931
6045
|
|
|
5932
6046
|
const blendModes = {
|
|
5933
|
-
'source-over': [2, 3, 0,
|
|
6047
|
+
'source-over': [2, 3, 0, 1, 3, 0],
|
|
5934
6048
|
'destination-over': [6, 1, 0, 6, 1, 0],
|
|
5935
6049
|
'source-in': [5, 0, 0, 5, 0, 0],
|
|
5936
6050
|
'destination-in': [0, 2, 0, 0, 2, 0],
|
|
@@ -5939,8 +6053,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5939
6053
|
'source-atop': [5, 3, 0, 5, 3, 0],
|
|
5940
6054
|
'destination-atop': [6, 2, 0, 6, 2, 0],
|
|
5941
6055
|
lighter: [1, 1, 0, 1, 1, 0],
|
|
5942
|
-
darken: [1, 1, 3,
|
|
5943
|
-
lighten: [1, 1, 4,
|
|
6056
|
+
darken: [1, 1, 3, 1, 3, 0],
|
|
6057
|
+
lighten: [1, 1, 4, 1, 3, 0],
|
|
5944
6058
|
replace: [1, 0, 0, 1, 0, 0]
|
|
5945
6059
|
};
|
|
5946
6060
|
|
|
@@ -6076,6 +6190,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6076
6190
|
$._uniforms[10] = $.mouseIsPressed ? 1 : 0;
|
|
6077
6191
|
$._uniforms[11] = $.keyCode;
|
|
6078
6192
|
$._uniforms[12] = $.keyIsPressed ? 1 : 0;
|
|
6193
|
+
$._uniforms[13] = yDir;
|
|
6079
6194
|
|
|
6080
6195
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, $._uniforms);
|
|
6081
6196
|
|
|
@@ -6329,9 +6444,14 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6329
6444
|
ellipseStackIdx = 0;
|
|
6330
6445
|
|
|
6331
6446
|
// destroy buffers
|
|
6447
|
+
let bufs = $._buffers;
|
|
6448
|
+
let texs = $._texturesToDestroy;
|
|
6449
|
+
$._buffers = [];
|
|
6450
|
+
$._texturesToDestroy = [];
|
|
6451
|
+
|
|
6332
6452
|
Q5.device.queue.onSubmittedWorkDone().then(() => {
|
|
6333
|
-
for (let b of
|
|
6334
|
-
|
|
6453
|
+
for (let b of bufs) b.destroy();
|
|
6454
|
+
for (let t of texs) t.destroy();
|
|
6335
6455
|
});
|
|
6336
6456
|
};
|
|
6337
6457
|
|
|
@@ -6903,7 +7023,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6903
7023
|
if (_rectMode != 'center') {
|
|
6904
7024
|
if (_rectMode == 'corner') {
|
|
6905
7025
|
x += hw;
|
|
6906
|
-
y += hh;
|
|
7026
|
+
y += hh * -yDir;
|
|
6907
7027
|
hw = Math.abs(hw);
|
|
6908
7028
|
hh = Math.abs(hh);
|
|
6909
7029
|
} else if (_rectMode == 'radius') {
|
|
@@ -6936,7 +7056,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6936
7056
|
|
|
6937
7057
|
function addCapsule(x1, y1, x2, y2, r, strokeW, fillCapsule) {
|
|
6938
7058
|
let dx = x2 - x1,
|
|
6939
|
-
dy = y2 - y1,
|
|
7059
|
+
dy = flippedY ? y2 - y1 : y1 - y2,
|
|
6940
7060
|
len = Math.hypot(dx, dy);
|
|
6941
7061
|
|
|
6942
7062
|
if (len === 0) return;
|
|
@@ -7046,7 +7166,12 @@ fn vertexMain(v: VertexParams) -> FragParams {
|
|
|
7046
7166
|
f.position = transformVertex(pos, ellipse.matrixIndex);
|
|
7047
7167
|
f.outerEdge = dist / (ellipse.size + halfStrokeSize);
|
|
7048
7168
|
f.fillEdge = dist / ellipse.size;
|
|
7049
|
-
|
|
7169
|
+
let innerSize = ellipse.size - halfStrokeSize;
|
|
7170
|
+
if (innerSize.x <= 0.0 || innerSize.y <= 0.0) {
|
|
7171
|
+
f.innerEdge = vec2f(2.0, 0.0);
|
|
7172
|
+
} else {
|
|
7173
|
+
f.innerEdge = dist / innerSize;
|
|
7174
|
+
}
|
|
7050
7175
|
f.strokeWeight = ellipse.strokeWeight;
|
|
7051
7176
|
|
|
7052
7177
|
let fill = colors[i32(ellipse.fillIndex)];
|
|
@@ -7105,7 +7230,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7105
7230
|
let strokeAlpha = innerAlpha * outerAlpha;
|
|
7106
7231
|
return vec4f(f.stroke.rgb, f.stroke.a * strokeAlpha);
|
|
7107
7232
|
}
|
|
7108
|
-
|
|
7233
|
+
`;
|
|
7109
7234
|
|
|
7110
7235
|
let ellipseShader = Q5.device.createShaderModule({
|
|
7111
7236
|
label: 'ellipseShader',
|
|
@@ -7285,61 +7410,61 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7285
7410
|
$._imageShaderCode =
|
|
7286
7411
|
$._baseShaderCode +
|
|
7287
7412
|
/* wgsl */ `
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7413
|
+
struct VertexParams {
|
|
7414
|
+
@builtin(vertex_index) vertexIndex : u32,
|
|
7415
|
+
@location(0) pos: vec2f,
|
|
7416
|
+
@location(1) texCoord: vec2f,
|
|
7417
|
+
@location(2) tintIndex: f32,
|
|
7418
|
+
@location(3) matrixIndex: f32,
|
|
7419
|
+
@location(4) imageAlpha: f32
|
|
7420
|
+
}
|
|
7421
|
+
struct FragParams {
|
|
7422
|
+
@builtin(position) position: vec4f,
|
|
7423
|
+
@location(0) texCoord: vec2f,
|
|
7424
|
+
@location(1) tintColor: vec4f,
|
|
7425
|
+
@location(2) imageAlpha: f32
|
|
7426
|
+
}
|
|
7427
|
+
|
|
7428
|
+
@group(0) @binding(0) var<uniform> q: Q5;
|
|
7429
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
7430
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
7431
|
+
|
|
7432
|
+
@group(1) @binding(0) var samp: sampler;
|
|
7433
|
+
@group(1) @binding(1) var tex: texture_2d<f32>;
|
|
7434
|
+
|
|
7435
|
+
fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
|
|
7436
|
+
var vert = vec4f(pos, 0f, 1f);
|
|
7437
|
+
vert = transforms[i32(matrixIndex)] * vert;
|
|
7438
|
+
vert.x /= q.halfWidth;
|
|
7439
|
+
vert.y /= q.halfHeight;
|
|
7440
|
+
return vert;
|
|
7441
|
+
}
|
|
7442
|
+
|
|
7443
|
+
fn applyTint(texColor: vec4f, tintColor: vec4f) -> vec4f {
|
|
7444
|
+
// apply the tint color to the sampled texture color at full strength
|
|
7445
|
+
let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a);
|
|
7446
|
+
// mix in the tint using the tint alpha as the blend strength
|
|
7447
|
+
return mix(texColor, tinted, tintColor.a);
|
|
7448
|
+
}
|
|
7449
|
+
|
|
7450
|
+
@vertex
|
|
7451
|
+
fn vertexMain(v: VertexParams) -> FragParams {
|
|
7452
|
+
var vert = transformVertex(v.pos, v.matrixIndex);
|
|
7453
|
+
|
|
7454
|
+
var f: FragParams;
|
|
7455
|
+
f.position = vert;
|
|
7456
|
+
f.texCoord = v.texCoord;
|
|
7457
|
+
f.tintColor = colors[i32(v.tintIndex)];
|
|
7458
|
+
f.imageAlpha = v.imageAlpha;
|
|
7459
|
+
return f;
|
|
7460
|
+
}
|
|
7461
|
+
|
|
7462
|
+
@fragment
|
|
7463
|
+
fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
7464
|
+
var texColor = textureSample(tex, samp, f.texCoord);
|
|
7465
|
+
texColor.a *= f.imageAlpha;
|
|
7466
|
+
return applyTint(texColor, f.tintColor);
|
|
7467
|
+
}
|
|
7343
7468
|
`;
|
|
7344
7469
|
|
|
7345
7470
|
let imageShader = Q5.device.createShaderModule({
|
|
@@ -7548,8 +7673,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7548
7673
|
GPUTextureUsage.RENDER_ATTACHMENT
|
|
7549
7674
|
});
|
|
7550
7675
|
|
|
7676
|
+
let src = { source: cnv };
|
|
7677
|
+
if (cnv.tagName == 'IMG') src.colorSpace = $.canvas.colorSpace;
|
|
7678
|
+
|
|
7551
7679
|
Q5.device.queue.copyExternalImageToTexture(
|
|
7552
|
-
|
|
7680
|
+
src,
|
|
7553
7681
|
{
|
|
7554
7682
|
texture,
|
|
7555
7683
|
colorSpace: $.canvas.colorSpace
|
|
@@ -7627,7 +7755,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7627
7755
|
$._getImageMode = () => _imageMode;
|
|
7628
7756
|
|
|
7629
7757
|
// Reusable uniform buffer array to avoid GC
|
|
7630
|
-
$._uniforms = new Float32Array(
|
|
7758
|
+
$._uniforms = new Float32Array(14);
|
|
7631
7759
|
|
|
7632
7760
|
const addImgVert = (x, y, u, v, ci, ti, ia) => {
|
|
7633
7761
|
let s = imgVertStack,
|
|
@@ -7647,7 +7775,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7647
7775
|
let isVideo;
|
|
7648
7776
|
if (img._texture == undefined) {
|
|
7649
7777
|
isVideo = img.tagName == 'VIDEO';
|
|
7650
|
-
if (!
|
|
7778
|
+
if (!isVideo || !img.currentTime) return;
|
|
7651
7779
|
if (img.flipped) $.scale(-1, 1);
|
|
7652
7780
|
}
|
|
7653
7781
|
|
|
@@ -7686,8 +7814,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7686
7814
|
let u0 = sx / w,
|
|
7687
7815
|
v0 = sy / h,
|
|
7688
7816
|
u1 = (sx + sw) / w,
|
|
7689
|
-
v1 = (sy + sh) / h
|
|
7690
|
-
|
|
7817
|
+
v1 = (sy + sh) / h;
|
|
7818
|
+
|
|
7819
|
+
let ti = matrixIdx,
|
|
7691
7820
|
ci = tintIdx,
|
|
7692
7821
|
ia = globalAlpha;
|
|
7693
7822
|
|
|
@@ -7772,12 +7901,11 @@ struct Text {
|
|
|
7772
7901
|
@group(2) @binding(0) var<storage> textChars: array<vec4f>;
|
|
7773
7902
|
@group(2) @binding(1) var<storage> textMetadata: array<Text>;
|
|
7774
7903
|
|
|
7775
|
-
const quad = array(vec2f(0, 1), vec2f(1, 1), vec2f(0, 0), vec2f(1, 0));
|
|
7904
|
+
const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
7776
7905
|
const uvs = array(vec2f(0, 1), vec2f(1, 1), vec2f(0, 0), vec2f(1, 0));
|
|
7777
7906
|
|
|
7778
7907
|
fn calcPos(i: u32, char: vec4f, fontChar: Char, text: Text) -> vec2f {
|
|
7779
|
-
return ((quad[i] * fontChar.size + char.xy + fontChar.offset) *
|
|
7780
|
-
text.scale) + text.pos;
|
|
7908
|
+
return ((vec2f(quad[i].x, quad[i].y * q.yUp) * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
|
|
7781
7909
|
}
|
|
7782
7910
|
|
|
7783
7911
|
fn calcUV(i: u32, fontChar: Char) -> vec2f {
|
|
@@ -8008,7 +8136,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8008
8136
|
fontChars[o + 4] = char.width; // size.x
|
|
8009
8137
|
fontChars[o + 5] = char.height; // size.y
|
|
8010
8138
|
fontChars[o + 6] = char.xoffset; // offset.x
|
|
8011
|
-
fontChars[o + 7] = char.yoffset; // offset.y
|
|
8139
|
+
fontChars[o + 7] = -char.yoffset * yDir; // offset.y
|
|
8012
8140
|
o += 8;
|
|
8013
8141
|
}
|
|
8014
8142
|
charsBuffer.unmap();
|
|
@@ -8084,9 +8212,10 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8084
8212
|
|
|
8085
8213
|
let _textSize = 18,
|
|
8086
8214
|
_textAlign = 'left',
|
|
8215
|
+
_textStyle = 'normal',
|
|
8087
8216
|
_textBaseline = 'alphabetic',
|
|
8088
8217
|
leadingSet = false,
|
|
8089
|
-
|
|
8218
|
+
_textLeading = 22.5,
|
|
8090
8219
|
leadDiff = 4.5,
|
|
8091
8220
|
leadPercent = 1.25;
|
|
8092
8221
|
|
|
@@ -8107,8 +8236,8 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8107
8236
|
if (size == undefined) return _textSize;
|
|
8108
8237
|
_textSize = size;
|
|
8109
8238
|
if (!leadingSet) {
|
|
8110
|
-
|
|
8111
|
-
leadDiff =
|
|
8239
|
+
_textLeading = size * leadPercent;
|
|
8240
|
+
leadDiff = _textLeading - size;
|
|
8112
8241
|
}
|
|
8113
8242
|
};
|
|
8114
8243
|
|
|
@@ -8141,29 +8270,29 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8141
8270
|
|
|
8142
8271
|
$.textLeading = (lineHeight) => {
|
|
8143
8272
|
if (!$._font) return $._g.textLeading(lineHeight);
|
|
8144
|
-
if (!lineHeight) return
|
|
8145
|
-
$._font.lineHeight =
|
|
8146
|
-
leadDiff =
|
|
8147
|
-
leadPercent =
|
|
8273
|
+
if (!lineHeight) return _textLeading;
|
|
8274
|
+
$._font.lineHeight = _textLeading = lineHeight;
|
|
8275
|
+
leadDiff = _textLeading - _textSize;
|
|
8276
|
+
leadPercent = _textLeading / _textSize;
|
|
8148
8277
|
leadingSet = true;
|
|
8149
8278
|
};
|
|
8150
8279
|
|
|
8151
8280
|
$.textAlign = (horiz, vert) => {
|
|
8152
8281
|
_textAlign = horiz;
|
|
8153
|
-
if (vert) _textBaseline = vert;
|
|
8282
|
+
if (vert) _textBaseline = vert[0] == 'c' ? 'middle' : vert;
|
|
8154
8283
|
};
|
|
8155
8284
|
|
|
8156
8285
|
$.textStyle = (style) => {
|
|
8157
|
-
|
|
8286
|
+
_textStyle = style;
|
|
8158
8287
|
};
|
|
8159
8288
|
|
|
8160
8289
|
let charStack = [],
|
|
8161
8290
|
textStack = [];
|
|
8162
8291
|
|
|
8163
8292
|
// Reusable array for line widths to avoid GC
|
|
8164
|
-
let
|
|
8293
|
+
let lineWidths = new Array(100);
|
|
8165
8294
|
|
|
8166
|
-
// Reusable buffers for text data to avoid
|
|
8295
|
+
// Reusable buffers for text data to avoid GC
|
|
8167
8296
|
let charDataBuffer = new Float32Array(Q5.MAX_CHARS * 4); // reusable buffer for char data
|
|
8168
8297
|
let textDataBuffer = new Float32Array(Q5.MAX_TEXTS * 8); // reusable buffer for text metadata
|
|
8169
8298
|
|
|
@@ -8173,7 +8302,6 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8173
8302
|
offsetY = 0,
|
|
8174
8303
|
line = 0,
|
|
8175
8304
|
printedCharCount = 0,
|
|
8176
|
-
lineWidths = lineWidthsCache, // reuse array
|
|
8177
8305
|
nextCharCode = text.charCodeAt(0);
|
|
8178
8306
|
|
|
8179
8307
|
for (let i = 0; i < text.length; ++i) {
|
|
@@ -8181,7 +8309,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8181
8309
|
nextCharCode = i < text.length - 1 ? text.charCodeAt(i + 1) : -1;
|
|
8182
8310
|
switch (charCode) {
|
|
8183
8311
|
case 10: // newline
|
|
8184
|
-
lineWidths
|
|
8312
|
+
lineWidths[line] = offsetX;
|
|
8185
8313
|
line++;
|
|
8186
8314
|
maxWidth = Math.max(maxWidth, offsetX);
|
|
8187
8315
|
offsetX = 0;
|
|
@@ -8217,7 +8345,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8217
8345
|
};
|
|
8218
8346
|
|
|
8219
8347
|
$.text = (str, x, y, w, h) => {
|
|
8220
|
-
if (_textSize < 1) return;
|
|
8348
|
+
if (_textSize * _scale < 1) return;
|
|
8221
8349
|
|
|
8222
8350
|
let type = typeof str;
|
|
8223
8351
|
if (type != 'string') {
|
|
@@ -8273,17 +8401,17 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8273
8401
|
o += 4;
|
|
8274
8402
|
});
|
|
8275
8403
|
|
|
8276
|
-
if (tb == 'alphabetic') y
|
|
8277
|
-
else if (tb == '
|
|
8278
|
-
else if (tb == 'bottom') y
|
|
8404
|
+
if (tb == 'alphabetic') y += _textSize * yDir;
|
|
8405
|
+
else if (tb == 'middle') y += _textSize * 0.5 * yDir;
|
|
8406
|
+
else if (tb == 'bottom') y += _textLeading * yDir;
|
|
8279
8407
|
} else {
|
|
8280
8408
|
// measure the text to get the line height before setting
|
|
8281
8409
|
// the x position to properly align the text
|
|
8282
8410
|
measurements = measureText($._font, str);
|
|
8283
8411
|
|
|
8284
8412
|
let offsetY = 0;
|
|
8285
|
-
if (tb == 'alphabetic') y
|
|
8286
|
-
else if (tb == '
|
|
8413
|
+
if (tb == 'alphabetic') y += _textSize * yDir;
|
|
8414
|
+
else if (tb == 'middle') offsetY = measurements.height * 0.5;
|
|
8287
8415
|
else if (tb == 'bottom') offsetY = measurements.height;
|
|
8288
8416
|
|
|
8289
8417
|
measureText($._font, str, (textX, textY, line, char) => {
|
|
@@ -8294,7 +8422,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8294
8422
|
offsetX = -measurements.lineWidths[line];
|
|
8295
8423
|
}
|
|
8296
8424
|
charsData[o] = textX + offsetX;
|
|
8297
|
-
charsData[o + 1] =
|
|
8425
|
+
charsData[o + 1] = (textY + offsetY) * yDir;
|
|
8298
8426
|
charsData[o + 2] = char.charIndex;
|
|
8299
8427
|
charsData[o + 3] = textIndex;
|
|
8300
8428
|
o += 4;
|
|
@@ -8332,7 +8460,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8332
8460
|
$._g.textSize(_textSize);
|
|
8333
8461
|
return $._g.textAscent(str);
|
|
8334
8462
|
}
|
|
8335
|
-
return
|
|
8463
|
+
return _textLeading - leadDiff;
|
|
8336
8464
|
};
|
|
8337
8465
|
|
|
8338
8466
|
$.textDescent = (str) => {
|
|
@@ -8343,18 +8471,29 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8343
8471
|
return leadDiff;
|
|
8344
8472
|
};
|
|
8345
8473
|
|
|
8346
|
-
$.
|
|
8347
|
-
$._g.
|
|
8348
|
-
|
|
8349
|
-
if (doFill && fillSet) {
|
|
8474
|
+
$._applyTextStylesToC2D = () => {
|
|
8475
|
+
if (!doFill) $._g.noFill();
|
|
8476
|
+
else if (fillSet) {
|
|
8350
8477
|
let fi = fillIdx * 4;
|
|
8351
8478
|
$._g.fill(colorStack.slice(fi, fi + 4));
|
|
8352
8479
|
}
|
|
8353
|
-
|
|
8480
|
+
|
|
8481
|
+
if (!doStroke) $._g.noStroke();
|
|
8482
|
+
else if (strokeSet) {
|
|
8354
8483
|
let si = strokeIdx * 4;
|
|
8355
8484
|
$._g.stroke(colorStack.slice(si, si + 4));
|
|
8356
8485
|
}
|
|
8357
8486
|
|
|
8487
|
+
if (sw != $._g._strokeWeight) $._g.strokeWeight(sw);
|
|
8488
|
+
if (_textSize != $._g._textSize) $._g.textSize(_textSize);
|
|
8489
|
+
if (_textStyle != $._g._textStyle) $._g.textStyle(_textStyle);
|
|
8490
|
+
if (_textLeading != $._g.textLeading()) $._g.textLeading(_textLeading);
|
|
8491
|
+
$._g.textAlign(_textAlign, _textBaseline);
|
|
8492
|
+
};
|
|
8493
|
+
|
|
8494
|
+
$.createTextImage = (str, w, h) => {
|
|
8495
|
+
$._applyTextStylesToC2D();
|
|
8496
|
+
|
|
8358
8497
|
let g = $._g.createTextImage(str, w, h);
|
|
8359
8498
|
$._makeDrawable(g);
|
|
8360
8499
|
return g;
|
|
@@ -8371,15 +8510,20 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8371
8510
|
else if (ta == 'right') x -= img.width;
|
|
8372
8511
|
|
|
8373
8512
|
let bl = _textBaseline;
|
|
8374
|
-
if (bl == 'alphabetic') y
|
|
8375
|
-
else if (bl == '
|
|
8376
|
-
else if (bl == 'bottom') y
|
|
8377
|
-
else if (bl == 'top') y
|
|
8513
|
+
if (bl == 'alphabetic') y += img._leading * yDir;
|
|
8514
|
+
else if (bl == 'middle') y += img._middle * yDir;
|
|
8515
|
+
else if (bl == 'bottom') y += img._bottom * yDir;
|
|
8516
|
+
else if (bl == 'top') y += img._top * yDir;
|
|
8378
8517
|
|
|
8379
8518
|
$.image(img, x, y);
|
|
8380
8519
|
_imageMode = og;
|
|
8381
8520
|
};
|
|
8382
8521
|
|
|
8522
|
+
$.textToPoints = (str, x, y, sampleRate, density) => {
|
|
8523
|
+
$._applyTextStylesToC2D();
|
|
8524
|
+
return $._g.textToPoints(str, x, y, sampleRate, density);
|
|
8525
|
+
};
|
|
8526
|
+
|
|
8383
8527
|
/* SHADERS */
|
|
8384
8528
|
|
|
8385
8529
|
let pipelineTypes = ['frame', 'shapes', 'image', 'video', 'text'];
|
|
@@ -8464,6 +8608,28 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
8464
8608
|
videoPL = 3;
|
|
8465
8609
|
textPL = 4;
|
|
8466
8610
|
};
|
|
8611
|
+
|
|
8612
|
+
const _remove = $.remove;
|
|
8613
|
+
$.remove = () => {
|
|
8614
|
+
$._frameA?.destroy();
|
|
8615
|
+
$._frameB?.destroy();
|
|
8616
|
+
uniformBuffer?.destroy();
|
|
8617
|
+
transformsBuffer?.destroy();
|
|
8618
|
+
colorsBuffer?.destroy();
|
|
8619
|
+
shapesVertBuff?.destroy();
|
|
8620
|
+
imgVertBuff?.destroy();
|
|
8621
|
+
charBuffer?.destroy();
|
|
8622
|
+
textBuffer?.destroy();
|
|
8623
|
+
rectBuffer?.destroy();
|
|
8624
|
+
rectIndexBuffer?.destroy();
|
|
8625
|
+
ellipseBuffer?.destroy();
|
|
8626
|
+
ellipseIndexBuffer?.destroy();
|
|
8627
|
+
|
|
8628
|
+
for (let b of $._buffers) b.destroy();
|
|
8629
|
+
$._buffers = [];
|
|
8630
|
+
|
|
8631
|
+
_remove();
|
|
8632
|
+
};
|
|
8467
8633
|
};
|
|
8468
8634
|
|
|
8469
8635
|
Q5.THRESHOLD = 1;
|
|
@@ -8475,7 +8641,7 @@ Q5.DILATE = 6;
|
|
|
8475
8641
|
Q5.ERODE = 7;
|
|
8476
8642
|
Q5.BLUR = 8;
|
|
8477
8643
|
|
|
8478
|
-
Q5.MAX_TRANSFORMS =
|
|
8644
|
+
Q5.MAX_TRANSFORMS = 2097152;
|
|
8479
8645
|
Q5.MAX_RECTS = 200200;
|
|
8480
8646
|
Q5.MAX_ELLIPSES = 200200;
|
|
8481
8647
|
Q5.MAX_CHARS = 100000;
|
|
@@ -8486,20 +8652,50 @@ Q5.initWebGPU = async () => {
|
|
|
8486
8652
|
console.warn('q5 WebGPU not supported on this browser! Use Google Chrome or Edge.');
|
|
8487
8653
|
return false;
|
|
8488
8654
|
}
|
|
8489
|
-
if (!Q5.requestedGPU) {
|
|
8490
|
-
Q5.requestedGPU = true;
|
|
8491
|
-
let adapter = await navigator.gpu.requestAdapter();
|
|
8492
|
-
if (!adapter) {
|
|
8493
|
-
console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, Vulkan may need to be enabled.');
|
|
8494
|
-
return false;
|
|
8495
|
-
}
|
|
8496
|
-
Q5.device = await adapter.requestDevice();
|
|
8497
8655
|
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8656
|
+
// fn can only be called once
|
|
8657
|
+
if (Q5.requestedGPU) return;
|
|
8658
|
+
Q5.requestedGPU = true;
|
|
8659
|
+
|
|
8660
|
+
let adapter = await navigator.gpu.requestAdapter();
|
|
8661
|
+
|
|
8662
|
+
adapter ??= await navigator.gpu.requestAdapter({
|
|
8663
|
+
featureLevel: 'compatibility'
|
|
8664
|
+
});
|
|
8665
|
+
|
|
8666
|
+
if (!adapter) {
|
|
8667
|
+
console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, Vulkan may need to be enabled.');
|
|
8668
|
+
return false;
|
|
8669
|
+
}
|
|
8670
|
+
|
|
8671
|
+
let device = await adapter.requestDevice();
|
|
8672
|
+
|
|
8673
|
+
const vertexStorageLimit =
|
|
8674
|
+
device.limits.maxStorageBuffersInVertexStage ?? device.limits.maxStorageBuffersPerShaderStage;
|
|
8675
|
+
if (vertexStorageLimit < 3) {
|
|
8676
|
+
console.warn('q5 WebGPU requires vertex storage buffers, which are not supported by this device.');
|
|
8677
|
+
return false;
|
|
8502
8678
|
}
|
|
8679
|
+
|
|
8680
|
+
// Update to fit device limits
|
|
8681
|
+
const maxStorage = device.limits.maxStorageBufferBindingSize;
|
|
8682
|
+
|
|
8683
|
+
let min = Math.min,
|
|
8684
|
+
floor = Math.floor;
|
|
8685
|
+
|
|
8686
|
+
Q5.MAX_TRANSFORMS = min(Q5.MAX_TRANSFORMS, floor(maxStorage / 64));
|
|
8687
|
+
Q5.MAX_RECTS = min(Q5.MAX_RECTS, floor(maxStorage / 64));
|
|
8688
|
+
Q5.MAX_ELLIPSES = min(Q5.MAX_ELLIPSES, floor(maxStorage / 64));
|
|
8689
|
+
Q5.MAX_CHARS = min(Q5.MAX_CHARS, floor(maxStorage / 16));
|
|
8690
|
+
Q5.MAX_TEXTS = min(Q5.MAX_TEXTS, floor(maxStorage / 32));
|
|
8691
|
+
|
|
8692
|
+
device.lost.then((e) => {
|
|
8693
|
+
console.error('WebGPU crashed!');
|
|
8694
|
+
console.error(e);
|
|
8695
|
+
});
|
|
8696
|
+
|
|
8697
|
+
Q5.device = device;
|
|
8698
|
+
|
|
8503
8699
|
return true;
|
|
8504
8700
|
};
|
|
8505
8701
|
|