q5 3.4.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +1 -1
- package/package.json +3 -3
- package/q5.d.ts +431 -384
- package/q5.js +163 -89
- package/q5.min.js +2 -2
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 3.
|
|
3
|
+
* @version 3.5
|
|
4
4
|
* @author quinton-ashley
|
|
5
5
|
* @contributors evanalulu, Tezumie, ormaq, Dukemz, LingDong-
|
|
6
6
|
* @license LGPL-3.0
|
|
@@ -426,12 +426,14 @@ Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.preloadMethods[n] = fn[n]);
|
|
|
426
426
|
function createCanvas(w, h, opt) {
|
|
427
427
|
if (Q5._hasGlobal) return;
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
let useC2D = w == 'c2d' || h == 'c2d' || opt == 'c2d' || opt?.renderer == 'c2d' || !Q5._esm;
|
|
430
|
+
|
|
431
|
+
if (useC2D) {
|
|
432
432
|
let q = new Q5();
|
|
433
433
|
let c = q.createCanvas(w, h, opt);
|
|
434
434
|
return q.ready.then(() => c);
|
|
435
|
+
} else {
|
|
436
|
+
return Q5.WebGPU().then((q) => q.createCanvas(w, h, opt));
|
|
435
437
|
}
|
|
436
438
|
}
|
|
437
439
|
|
|
@@ -440,14 +442,21 @@ if (Q5._server) global.p5 ??= global.Q5 = Q5;
|
|
|
440
442
|
if (typeof window == 'object') {
|
|
441
443
|
window.p5 ??= window.Q5 = Q5;
|
|
442
444
|
window.createCanvas = createCanvas;
|
|
443
|
-
window.
|
|
445
|
+
window.C2D = 'c2d';
|
|
446
|
+
window.WEBGPU = 'webgpu';
|
|
444
447
|
} else global.window = 0;
|
|
445
448
|
|
|
446
|
-
Q5.version = Q5.VERSION = '3.
|
|
449
|
+
Q5.version = Q5.VERSION = '3.5';
|
|
447
450
|
|
|
448
451
|
if (typeof document == 'object') {
|
|
449
452
|
document.addEventListener('DOMContentLoaded', () => {
|
|
450
|
-
if (!Q5._hasGlobal)
|
|
453
|
+
if (!Q5._hasGlobal) {
|
|
454
|
+
if (Q5.setup || Q5.update || Q5.draw) {
|
|
455
|
+
Q5.WebGPU();
|
|
456
|
+
} else {
|
|
457
|
+
new Q5('auto');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
451
460
|
});
|
|
452
461
|
}
|
|
453
462
|
Q5.modules.canvas = ($, q) => {
|
|
@@ -2286,6 +2295,7 @@ Q5.modules.color = ($, q) => {
|
|
|
2286
2295
|
pink: [255, 192, 203],
|
|
2287
2296
|
purple: [128, 0, 128],
|
|
2288
2297
|
red: [255, 0, 0],
|
|
2298
|
+
silver: [192, 192, 192],
|
|
2289
2299
|
skyblue: [135, 206, 235],
|
|
2290
2300
|
tan: [210, 180, 140],
|
|
2291
2301
|
turquoise: [64, 224, 208],
|
|
@@ -3094,8 +3104,8 @@ Q5.modules.dom = ($, q) => {
|
|
|
3094
3104
|
el.type = 'range';
|
|
3095
3105
|
el.min = min;
|
|
3096
3106
|
el.max = max;
|
|
3097
|
-
el.value = value;
|
|
3098
3107
|
el.step = step;
|
|
3108
|
+
el.value = value;
|
|
3099
3109
|
el.val = () => parseFloat(el.value);
|
|
3100
3110
|
return el;
|
|
3101
3111
|
};
|
|
@@ -3194,6 +3204,7 @@ Q5.modules.fes = ($) => {
|
|
|
3194
3204
|
|
|
3195
3205
|
let errFile = stackLines[idx].split(sep).at(-1);
|
|
3196
3206
|
if (errFile.startsWith('blob:')) errFile = errFile.slice(5);
|
|
3207
|
+
errFile = errFile.split(')')[0];
|
|
3197
3208
|
let parts = errFile.split(':');
|
|
3198
3209
|
let lineNum = parseInt(parts.at(-2));
|
|
3199
3210
|
parts[parts.length - 1] = parts.at(-1).split(')')[0];
|
|
@@ -3246,24 +3257,24 @@ Q5.modules.fes = ($) => {
|
|
|
3246
3257
|
});
|
|
3247
3258
|
}
|
|
3248
3259
|
}
|
|
3249
|
-
};
|
|
3250
3260
|
|
|
3251
|
-
if (typeof navigator != undefined && navigator.onLine) {
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3261
|
+
if (Q5.online != false && typeof navigator != undefined && navigator.onLine) {
|
|
3262
|
+
async function checkLatestVersion() {
|
|
3263
|
+
try {
|
|
3264
|
+
let response = await fetch('https://data.jsdelivr.com/v1/package/npm/q5');
|
|
3265
|
+
if (!response.ok) return;
|
|
3266
|
+
let data = await response.json();
|
|
3267
|
+
let l = data.tags.latest;
|
|
3268
|
+
l = l.slice(0, l.lastIndexOf('.'));
|
|
3269
|
+
if (l != Q5.version) {
|
|
3270
|
+
console.warn(`q5.js v${l} is now available! Consider updating from v${Q5.version}.`);
|
|
3271
|
+
}
|
|
3272
|
+
} catch (e) {}
|
|
3273
|
+
}
|
|
3264
3274
|
|
|
3265
|
-
|
|
3266
|
-
}
|
|
3275
|
+
checkLatestVersion();
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3267
3278
|
Q5.modules.input = ($, q) => {
|
|
3268
3279
|
if ($._isGraphics) return;
|
|
3269
3280
|
|
|
@@ -5071,6 +5082,8 @@ struct Q5 {
|
|
|
5071
5082
|
frameLayout,
|
|
5072
5083
|
frameSampler,
|
|
5073
5084
|
frameBindGroup,
|
|
5085
|
+
frameBindGroupA,
|
|
5086
|
+
frameBindGroupB,
|
|
5074
5087
|
colorIndex = 2,
|
|
5075
5088
|
colorStackIndex = 12,
|
|
5076
5089
|
prevFramePL = 0,
|
|
@@ -5228,6 +5241,25 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5228
5241
|
|
|
5229
5242
|
// Create a pipeline for rendering frames
|
|
5230
5243
|
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
5244
|
+
|
|
5245
|
+
// Create persistent bind groups for both frame buffers
|
|
5246
|
+
frameBindGroupA = Q5.device.createBindGroup({
|
|
5247
|
+
layout: frameLayout,
|
|
5248
|
+
entries: [
|
|
5249
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5250
|
+
{ binding: 1, resource: frameSampler },
|
|
5251
|
+
{ binding: 2, resource: frameA.createView() }
|
|
5252
|
+
]
|
|
5253
|
+
});
|
|
5254
|
+
|
|
5255
|
+
frameBindGroupB = Q5.device.createBindGroup({
|
|
5256
|
+
layout: frameLayout,
|
|
5257
|
+
entries: [
|
|
5258
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5259
|
+
{ binding: 1, resource: frameSampler },
|
|
5260
|
+
{ binding: 2, resource: frameB.createView() }
|
|
5261
|
+
]
|
|
5262
|
+
});
|
|
5231
5263
|
};
|
|
5232
5264
|
|
|
5233
5265
|
$._createCanvas = (w, h, opt) => {
|
|
@@ -5608,6 +5640,9 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5608
5640
|
$.popStyles();
|
|
5609
5641
|
};
|
|
5610
5642
|
|
|
5643
|
+
// Reusable array for calcBox to avoid GC
|
|
5644
|
+
let boxCache = [0, 0, 0, 0];
|
|
5645
|
+
|
|
5611
5646
|
const calcBox = (x, y, w, h, mode) => {
|
|
5612
5647
|
// left, right, top, bottom
|
|
5613
5648
|
let l, r, t, b;
|
|
@@ -5631,7 +5666,11 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5631
5666
|
b = h;
|
|
5632
5667
|
}
|
|
5633
5668
|
|
|
5634
|
-
|
|
5669
|
+
boxCache[0] = l;
|
|
5670
|
+
boxCache[1] = r;
|
|
5671
|
+
boxCache[2] = t;
|
|
5672
|
+
boxCache[3] = b;
|
|
5673
|
+
return boxCache;
|
|
5635
5674
|
};
|
|
5636
5675
|
|
|
5637
5676
|
// prettier-ignore
|
|
@@ -5725,11 +5764,15 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5725
5764
|
};
|
|
5726
5765
|
|
|
5727
5766
|
$._beginRender = () => {
|
|
5728
|
-
// swap the frame textures
|
|
5767
|
+
// swap the frame textures and bind groups
|
|
5729
5768
|
const temp = frameA;
|
|
5730
5769
|
frameA = frameB;
|
|
5731
5770
|
frameB = temp;
|
|
5732
5771
|
|
|
5772
|
+
const tempBindGroup = frameBindGroupA;
|
|
5773
|
+
frameBindGroupA = frameBindGroupB;
|
|
5774
|
+
frameBindGroupB = tempBindGroup;
|
|
5775
|
+
|
|
5733
5776
|
encoder = Q5.device.createCommandEncoder();
|
|
5734
5777
|
|
|
5735
5778
|
$._pass = pass = encoder.beginRenderPass({
|
|
@@ -5745,14 +5788,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5745
5788
|
]
|
|
5746
5789
|
});
|
|
5747
5790
|
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
entries: [
|
|
5751
|
-
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5752
|
-
{ binding: 1, resource: frameSampler },
|
|
5753
|
-
{ binding: 2, resource: frameB.createView() }
|
|
5754
|
-
]
|
|
5755
|
-
});
|
|
5791
|
+
// Use pre-created bind group instead of creating new one
|
|
5792
|
+
frameBindGroup = frameBindGroupB;
|
|
5756
5793
|
|
|
5757
5794
|
if (!shouldClear) {
|
|
5758
5795
|
pass.setPipeline($._pipelines[prevFramePL]);
|
|
@@ -5763,6 +5800,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5763
5800
|
};
|
|
5764
5801
|
|
|
5765
5802
|
let transformsBuffer, colorsBuffer, shapesVertBuff, imgVertBuff, charBuffer, textBuffer;
|
|
5803
|
+
let mainBindGroup, lastTransformsBuffer, lastColorsBuffer;
|
|
5766
5804
|
|
|
5767
5805
|
$._render = () => {
|
|
5768
5806
|
let transformsSize = matrices.length * MATRIX_SIZE * 4; // 4 bytes per float
|
|
@@ -5787,32 +5825,36 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5787
5825
|
|
|
5788
5826
|
Q5.device.queue.writeBuffer(colorsBuffer, 0, colorStack.subarray(0, colorStackIndex));
|
|
5789
5827
|
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5828
|
+
// Reuse uniform array instead of creating new one each frame
|
|
5829
|
+
$._uniforms[0] = $.width;
|
|
5830
|
+
$._uniforms[1] = $.height;
|
|
5831
|
+
$._uniforms[2] = $.halfWidth;
|
|
5832
|
+
$._uniforms[3] = $.halfHeight;
|
|
5833
|
+
$._uniforms[4] = $._pixelDensity;
|
|
5834
|
+
$._uniforms[5] = $.frameCount;
|
|
5835
|
+
$._uniforms[6] = performance.now();
|
|
5836
|
+
$._uniforms[7] = $.deltaTime;
|
|
5837
|
+
$._uniforms[8] = $.mouseX;
|
|
5838
|
+
$._uniforms[9] = $.mouseY;
|
|
5839
|
+
$._uniforms[10] = $.mouseIsPressed ? 1 : 0;
|
|
5840
|
+
$._uniforms[11] = $.keyCode;
|
|
5841
|
+
$._uniforms[12] = $.keyIsPressed ? 1 : 0;
|
|
5842
|
+
|
|
5843
|
+
Q5.device.queue.writeBuffer(uniformBuffer, 0, $._uniforms);
|
|
5844
|
+
|
|
5845
|
+
// Only recreate bind group if buffers changed
|
|
5846
|
+
if (!mainBindGroup || lastTransformsBuffer !== transformsBuffer || lastColorsBuffer !== colorsBuffer) {
|
|
5847
|
+
mainBindGroup = Q5.device.createBindGroup({
|
|
5848
|
+
layout: mainLayout,
|
|
5849
|
+
entries: [
|
|
5850
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
5851
|
+
{ binding: 1, resource: { buffer: transformsBuffer } },
|
|
5852
|
+
{ binding: 2, resource: { buffer: colorsBuffer } }
|
|
5853
|
+
]
|
|
5854
|
+
});
|
|
5855
|
+
lastTransformsBuffer = transformsBuffer;
|
|
5856
|
+
lastColorsBuffer = colorsBuffer;
|
|
5857
|
+
}
|
|
5816
5858
|
|
|
5817
5859
|
pass.setBindGroup(0, mainBindGroup);
|
|
5818
5860
|
|
|
@@ -5860,11 +5902,13 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5860
5902
|
// prepare to render text
|
|
5861
5903
|
|
|
5862
5904
|
if (charStack.length) {
|
|
5863
|
-
//
|
|
5864
|
-
let
|
|
5905
|
+
// Flatten char data into reusable buffer instead of creating new array
|
|
5906
|
+
let charOffset = 0;
|
|
5865
5907
|
for (let charsData of charStack) {
|
|
5866
|
-
|
|
5908
|
+
charDataBuffer.set(charsData, charOffset);
|
|
5909
|
+
charOffset += charsData.length;
|
|
5867
5910
|
}
|
|
5911
|
+
let totalTextSize = charOffset * 4;
|
|
5868
5912
|
|
|
5869
5913
|
if (!charBuffer || charBuffer.size < totalTextSize) {
|
|
5870
5914
|
if (charBuffer) charBuffer.destroy();
|
|
@@ -5874,10 +5918,16 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5874
5918
|
});
|
|
5875
5919
|
}
|
|
5876
5920
|
|
|
5877
|
-
Q5.device.queue.writeBuffer(charBuffer, 0,
|
|
5921
|
+
Q5.device.queue.writeBuffer(charBuffer, 0, charDataBuffer.buffer, 0, totalTextSize);
|
|
5922
|
+
|
|
5923
|
+
// Flatten text metadata into reusable buffer
|
|
5924
|
+
let textOffset = 0;
|
|
5925
|
+
for (let textData of textStack) {
|
|
5926
|
+
textDataBuffer.set(textData, textOffset);
|
|
5927
|
+
textOffset += textData.length;
|
|
5928
|
+
}
|
|
5929
|
+
let totalMetadataSize = textOffset * 4;
|
|
5878
5930
|
|
|
5879
|
-
// Calculate total buffer size for metadata
|
|
5880
|
-
let totalMetadataSize = textStack.length * 8 * 4;
|
|
5881
5931
|
if (!textBuffer || textBuffer.size < totalMetadataSize) {
|
|
5882
5932
|
if (textBuffer) textBuffer.destroy();
|
|
5883
5933
|
textBuffer = Q5.device.createBuffer({
|
|
@@ -5887,7 +5937,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
5887
5937
|
});
|
|
5888
5938
|
}
|
|
5889
5939
|
|
|
5890
|
-
Q5.device.queue.writeBuffer(textBuffer, 0,
|
|
5940
|
+
Q5.device.queue.writeBuffer(textBuffer, 0, textDataBuffer.buffer, 0, totalMetadataSize);
|
|
5891
5941
|
|
|
5892
5942
|
// create a single bind group for the text buffer and metadata buffer
|
|
5893
5943
|
$._textBindGroup = Q5.device.createBindGroup({
|
|
@@ -6005,14 +6055,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6005
6055
|
]
|
|
6006
6056
|
});
|
|
6007
6057
|
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
entries: [
|
|
6011
|
-
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
6012
|
-
{ binding: 1, resource: frameSampler },
|
|
6013
|
-
{ binding: 2, resource: frameA.createView() }
|
|
6014
|
-
]
|
|
6015
|
-
});
|
|
6058
|
+
// Use pre-created bind group instead of creating new one
|
|
6059
|
+
frameBindGroup = frameBindGroupA;
|
|
6016
6060
|
|
|
6017
6061
|
pass.setPipeline($._pipelines[framePL]);
|
|
6018
6062
|
pass.setBindGroup(0, frameBindGroup);
|
|
@@ -6024,11 +6068,11 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6024
6068
|
$._pass = pass = encoder = null;
|
|
6025
6069
|
|
|
6026
6070
|
// clear the stacks for the next frame
|
|
6027
|
-
drawStack.
|
|
6071
|
+
drawStack.length = 0; // faster than splice for clearing
|
|
6028
6072
|
colorIndex = 2;
|
|
6029
6073
|
colorStackIndex = 12;
|
|
6030
|
-
matrices =
|
|
6031
|
-
matricesIdxStack =
|
|
6074
|
+
matrices.length = 1; // keep first matrix, clear rest
|
|
6075
|
+
matricesIdxStack.length = 0;
|
|
6032
6076
|
|
|
6033
6077
|
// frameA can now be saved when saveCanvas is run
|
|
6034
6078
|
$._texture = frameA;
|
|
@@ -6036,13 +6080,15 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
|
|
|
6036
6080
|
// reset
|
|
6037
6081
|
shapesVertIdx = 0;
|
|
6038
6082
|
imgVertIdx = 0;
|
|
6039
|
-
|
|
6083
|
+
// Remove video frames without creating new array
|
|
6084
|
+
if (vidFrames > 0) {
|
|
6085
|
+
$._textureBindGroups.length = tIdx;
|
|
6086
|
+
}
|
|
6040
6087
|
vidFrames = 0;
|
|
6041
|
-
charStack =
|
|
6042
|
-
textStack =
|
|
6043
|
-
|
|
6088
|
+
charStack.length = 0;
|
|
6089
|
+
textStack.length = 0;
|
|
6090
|
+
// Don't create new typed arrays - just reset index
|
|
6044
6091
|
rectStackIdx = 0;
|
|
6045
|
-
ellipseStack = new Float32Array(Q5.MAX_ELLIPSES * 16);
|
|
6046
6092
|
ellipseStackIdx = 0;
|
|
6047
6093
|
|
|
6048
6094
|
// destroy buffers
|
|
@@ -6614,6 +6660,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6614
6660
|
$.rectMode = (x) => (_rectMode = x);
|
|
6615
6661
|
$._getRectMode = () => _rectMode;
|
|
6616
6662
|
|
|
6663
|
+
// Reusable array for rect mode calculations
|
|
6664
|
+
let rectModeCache = [0, 0, 0, 0];
|
|
6665
|
+
|
|
6617
6666
|
function applyRectMode(x, y, w, h) {
|
|
6618
6667
|
let hw = w / 2,
|
|
6619
6668
|
hh = h / 2;
|
|
@@ -6631,7 +6680,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6631
6680
|
y += hh;
|
|
6632
6681
|
}
|
|
6633
6682
|
}
|
|
6634
|
-
|
|
6683
|
+
rectModeCache[0] = x;
|
|
6684
|
+
rectModeCache[1] = y;
|
|
6685
|
+
rectModeCache[2] = hw;
|
|
6686
|
+
rectModeCache[3] = hh;
|
|
6687
|
+
return rectModeCache;
|
|
6635
6688
|
}
|
|
6636
6689
|
|
|
6637
6690
|
$.rect = (x, y, w, h, rr = 0) => {
|
|
@@ -6909,6 +6962,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6909
6962
|
$.ellipseMode = (x) => (_ellipseMode = x);
|
|
6910
6963
|
$._getEllipseMode = () => _ellipseMode;
|
|
6911
6964
|
|
|
6965
|
+
// Reusable array for ellipse mode calculations
|
|
6966
|
+
let ellipseModeCache = [0, 0, 0, 0];
|
|
6967
|
+
|
|
6912
6968
|
function applyEllipseMode(x, y, w, h) {
|
|
6913
6969
|
h ??= w;
|
|
6914
6970
|
let a, b;
|
|
@@ -6929,7 +6985,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
6929
6985
|
a = (w - x) / 2;
|
|
6930
6986
|
b = (h - y) / 2;
|
|
6931
6987
|
}
|
|
6932
|
-
|
|
6988
|
+
ellipseModeCache[0] = x;
|
|
6989
|
+
ellipseModeCache[1] = y;
|
|
6990
|
+
ellipseModeCache[2] = a;
|
|
6991
|
+
ellipseModeCache[3] = b;
|
|
6992
|
+
return ellipseModeCache;
|
|
6933
6993
|
}
|
|
6934
6994
|
|
|
6935
6995
|
$.ellipse = (x, y, w, h) => {
|
|
@@ -7326,6 +7386,9 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7326
7386
|
$.imageMode = (x) => (_imageMode = x);
|
|
7327
7387
|
$._getImageMode = () => _imageMode;
|
|
7328
7388
|
|
|
7389
|
+
// Reusable uniform buffer array to avoid GC
|
|
7390
|
+
$._uniforms = new Float32Array(13);
|
|
7391
|
+
|
|
7329
7392
|
const addImgVert = (x, y, u, v, ci, ti, ia) => {
|
|
7330
7393
|
let s = imgVertStack,
|
|
7331
7394
|
i = imgVertIdx;
|
|
@@ -7340,6 +7403,7 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
|
|
|
7340
7403
|
};
|
|
7341
7404
|
|
|
7342
7405
|
$.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
7406
|
+
if (!img) return;
|
|
7343
7407
|
let isVideo;
|
|
7344
7408
|
if (img._texture == undefined) {
|
|
7345
7409
|
isVideo = img.tagName == 'VIDEO';
|
|
@@ -7754,7 +7818,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7754
7818
|
$._loadDefaultFont = (fontName, cb) => {
|
|
7755
7819
|
fonts[fontName] = null;
|
|
7756
7820
|
let url = `https://q5js.org/fonts/${fontName}-msdf.json`;
|
|
7757
|
-
if (!navigator.onLine) {
|
|
7821
|
+
if (Q5.online == false || !navigator.onLine) {
|
|
7758
7822
|
url = `/node_modules/q5/builtinFonts/${fontName}-msdf.json`;
|
|
7759
7823
|
}
|
|
7760
7824
|
return $.loadFont(url, cb);
|
|
@@ -7827,13 +7891,20 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7827
7891
|
let charStack = [],
|
|
7828
7892
|
textStack = [];
|
|
7829
7893
|
|
|
7894
|
+
// Reusable array for line widths to avoid GC
|
|
7895
|
+
let lineWidthsCache = new Array(100);
|
|
7896
|
+
|
|
7897
|
+
// Reusable buffers for text data to avoid creating new arrays
|
|
7898
|
+
let charDataBuffer = new Float32Array(100000); // reusable buffer for char data
|
|
7899
|
+
let textDataBuffer = new Float32Array(10000); // reusable buffer for text metadata
|
|
7900
|
+
|
|
7830
7901
|
let measureText = (font, text, charCallback) => {
|
|
7831
7902
|
let maxWidth = 0,
|
|
7832
7903
|
offsetX = 0,
|
|
7833
7904
|
offsetY = 0,
|
|
7834
7905
|
line = 0,
|
|
7835
7906
|
printedCharCount = 0,
|
|
7836
|
-
lineWidths =
|
|
7907
|
+
lineWidths = lineWidthsCache, // reuse array
|
|
7837
7908
|
nextCharCode = text.charCodeAt(0);
|
|
7838
7909
|
|
|
7839
7910
|
for (let i = 0; i < text.length; ++i) {
|
|
@@ -7864,12 +7935,14 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
|
|
|
7864
7935
|
printedCharCount++;
|
|
7865
7936
|
}
|
|
7866
7937
|
}
|
|
7867
|
-
lineWidths
|
|
7938
|
+
lineWidths[line] = offsetX;
|
|
7868
7939
|
maxWidth = Math.max(maxWidth, offsetX);
|
|
7940
|
+
let lineCount = line + 1;
|
|
7869
7941
|
return {
|
|
7870
7942
|
width: maxWidth,
|
|
7871
|
-
height:
|
|
7943
|
+
height: lineCount * font.lineHeight * leadPercent,
|
|
7872
7944
|
lineWidths,
|
|
7945
|
+
lineCount,
|
|
7873
7946
|
printedCharCount
|
|
7874
7947
|
};
|
|
7875
7948
|
};
|
|
@@ -8128,6 +8201,7 @@ Q5.initWebGPU = async () => {
|
|
|
8128
8201
|
return false;
|
|
8129
8202
|
}
|
|
8130
8203
|
if (!Q5.requestedGPU) {
|
|
8204
|
+
Q5.requestedGPU = true;
|
|
8131
8205
|
let adapter = await navigator.gpu.requestAdapter();
|
|
8132
8206
|
if (!adapter) {
|
|
8133
8207
|
console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, Vulkan may need to be enabled.');
|