q5 2.18.3 → 2.19.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 +1 -1
- package/q5.d.ts +194 -49
- package/q5.js +284 -128
- package/q5.min.js +2 -2
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.19
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -104,9 +104,9 @@ function Q5(scope, parent, renderer) {
|
|
|
104
104
|
throw e;
|
|
105
105
|
}
|
|
106
106
|
for (let m of Q5.methods.post) m.call($);
|
|
107
|
+
$.postProcess();
|
|
107
108
|
if ($._render) $._render();
|
|
108
109
|
if ($._finishRender) $._finishRender();
|
|
109
|
-
$.postProcess();
|
|
110
110
|
q.pmouseX = $.mouseX;
|
|
111
111
|
q.pmouseY = $.mouseY;
|
|
112
112
|
q.moveX = q.moveY = 0;
|
|
@@ -313,7 +313,7 @@ function createCanvas(w, h, opt) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
Q5.version = Q5.VERSION = '2.
|
|
316
|
+
Q5.version = Q5.VERSION = '2.19';
|
|
317
317
|
|
|
318
318
|
if (typeof document == 'object') {
|
|
319
319
|
document.addEventListener('DOMContentLoaded', () => {
|
|
@@ -422,49 +422,6 @@ Q5.modules.canvas = ($, q) => {
|
|
|
422
422
|
return g;
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
-
async function saveFile(data, name, ext) {
|
|
426
|
-
name = name || 'untitled';
|
|
427
|
-
ext = ext || 'png';
|
|
428
|
-
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
429
|
-
if (data instanceof OffscreenCanvas) {
|
|
430
|
-
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
431
|
-
data = await new Promise((resolve) => {
|
|
432
|
-
const reader = new FileReader();
|
|
433
|
-
reader.onloadend = () => resolve(reader.result);
|
|
434
|
-
reader.readAsDataURL(blob);
|
|
435
|
-
});
|
|
436
|
-
} else {
|
|
437
|
-
data = data.toDataURL('image/' + ext);
|
|
438
|
-
}
|
|
439
|
-
} else {
|
|
440
|
-
let type = 'text/plain';
|
|
441
|
-
if (ext == 'json') {
|
|
442
|
-
if (typeof data != 'string') data = JSON.stringify(data);
|
|
443
|
-
type = 'text/json';
|
|
444
|
-
}
|
|
445
|
-
data = new Blob([data], { type });
|
|
446
|
-
data = URL.createObjectURL(data);
|
|
447
|
-
}
|
|
448
|
-
let a = document.createElement('a');
|
|
449
|
-
a.href = data;
|
|
450
|
-
a.download = name + '.' + ext;
|
|
451
|
-
a.click();
|
|
452
|
-
URL.revokeObjectURL(a.href);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
$.save = (a, b, c) => {
|
|
456
|
-
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
457
|
-
c = b;
|
|
458
|
-
b = a;
|
|
459
|
-
a = $.canvas;
|
|
460
|
-
}
|
|
461
|
-
if (c) return saveFile(a, b, c);
|
|
462
|
-
if (b) {
|
|
463
|
-
b = b.split('.');
|
|
464
|
-
saveFile(a, b[0], b.at(-1));
|
|
465
|
-
} else saveFile(a);
|
|
466
|
-
};
|
|
467
|
-
|
|
468
425
|
$._setCanvasSize = (w, h) => {
|
|
469
426
|
if (!w) h ??= window.innerHeight;
|
|
470
427
|
else h ??= w;
|
|
@@ -549,10 +506,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
549
506
|
$._resizeCanvas(w, h);
|
|
550
507
|
};
|
|
551
508
|
|
|
552
|
-
if (c && !Q5._createServerCanvas)
|
|
553
|
-
c.resize = $.resizeCanvas;
|
|
554
|
-
c.save = $.saveCanvas = $.save;
|
|
555
|
-
}
|
|
509
|
+
if (c && !Q5._createServerCanvas) c.resize = $.resizeCanvas;
|
|
556
510
|
|
|
557
511
|
$.pixelDensity = (v) => {
|
|
558
512
|
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
@@ -1324,10 +1278,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1324
1278
|
opt ??= {};
|
|
1325
1279
|
opt.alpha ??= true;
|
|
1326
1280
|
opt.colorSpace ??= $.canvas.colorSpace || Q5.canvasOptions.colorSpace;
|
|
1327
|
-
|
|
1328
|
-
img.defaultWidth = w * $._defaultImageScale;
|
|
1329
|
-
img.defaultHeight = h * $._defaultImageScale;
|
|
1330
|
-
return img;
|
|
1281
|
+
return new Q5.Image(w, h, opt);
|
|
1331
1282
|
};
|
|
1332
1283
|
|
|
1333
1284
|
$.loadImage = function (url, cb, opt) {
|
|
@@ -1483,7 +1434,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1483
1434
|
$.ctx.globalCompositeOperation = 'source-over';
|
|
1484
1435
|
$.ctx.drawImage($.canvas, 0, 0, $.canvas.w, $.canvas.h);
|
|
1485
1436
|
$.ctx.restore();
|
|
1486
|
-
$._retint = true;
|
|
1437
|
+
$.modified = $._retint = true;
|
|
1487
1438
|
};
|
|
1488
1439
|
|
|
1489
1440
|
if ($._scope == 'image') {
|
|
@@ -1501,7 +1452,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1501
1452
|
$.ctx.clearRect(0, 0, c.width, c.height);
|
|
1502
1453
|
$.ctx.drawImage(o, 0, 0, c.width, c.height);
|
|
1503
1454
|
|
|
1504
|
-
$._retint = true;
|
|
1455
|
+
$.modified = $._retint = true;
|
|
1505
1456
|
};
|
|
1506
1457
|
}
|
|
1507
1458
|
|
|
@@ -1548,17 +1499,25 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1548
1499
|
$.ctx.globalCompositeOperation = old;
|
|
1549
1500
|
$.ctx.restore();
|
|
1550
1501
|
|
|
1551
|
-
$._retint = true;
|
|
1502
|
+
$.modified = $._retint = true;
|
|
1552
1503
|
};
|
|
1553
1504
|
|
|
1554
1505
|
$.inset = (x, y, w, h, dx, dy, dw, dh) => {
|
|
1555
1506
|
let pd = $._pixelDensity || 1;
|
|
1556
1507
|
$.ctx.drawImage($.canvas, x * pd, y * pd, w * pd, h * pd, dx, dy, dw, dh);
|
|
1557
1508
|
|
|
1558
|
-
$._retint = true;
|
|
1509
|
+
$.modified = $._retint = true;
|
|
1559
1510
|
};
|
|
1560
1511
|
|
|
1561
|
-
$.copy = () =>
|
|
1512
|
+
$.copy = () => {
|
|
1513
|
+
let img = $.get();
|
|
1514
|
+
for (let prop in $) {
|
|
1515
|
+
if (typeof $[prop] != 'function' && !/(canvas|ctx|texture|textureIndex)/.test(prop)) {
|
|
1516
|
+
img[prop] = $[prop];
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return img;
|
|
1520
|
+
};
|
|
1562
1521
|
|
|
1563
1522
|
$.get = (x, y, w, h) => {
|
|
1564
1523
|
let pd = $._pixelDensity || 1;
|
|
@@ -1568,22 +1527,19 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1568
1527
|
}
|
|
1569
1528
|
x = Math.floor(x || 0) * pd;
|
|
1570
1529
|
y = Math.floor(y || 0) * pd;
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
w
|
|
1574
|
-
h
|
|
1575
|
-
|
|
1576
|
-
img.
|
|
1577
|
-
img._pixelDensity = pd;
|
|
1578
|
-
img.width = _w;
|
|
1579
|
-
img.height = _h;
|
|
1530
|
+
w ??= $.width;
|
|
1531
|
+
h ??= $.height;
|
|
1532
|
+
let img = $.createImage(w, h, { pixelDensity: pd });
|
|
1533
|
+
img.ctx.drawImage($.canvas, x, y, w * pd, h * pd, 0, 0, w, h);
|
|
1534
|
+
img.width = w;
|
|
1535
|
+
img.height = h;
|
|
1580
1536
|
return img;
|
|
1581
1537
|
};
|
|
1582
1538
|
|
|
1583
1539
|
$.set = (x, y, c) => {
|
|
1584
1540
|
x = Math.floor(x);
|
|
1585
1541
|
y = Math.floor(y);
|
|
1586
|
-
$._retint = true;
|
|
1542
|
+
$.modified = $._retint = true;
|
|
1587
1543
|
if (c.canvas) {
|
|
1588
1544
|
let old = $._tint;
|
|
1589
1545
|
$._tint = null;
|
|
@@ -1611,7 +1567,7 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1611
1567
|
$.updatePixels = () => {
|
|
1612
1568
|
if (imgData != null) {
|
|
1613
1569
|
$.ctx.putImageData(imgData, 0, 0);
|
|
1614
|
-
$._retint = true;
|
|
1570
|
+
$.modified = $._retint = true;
|
|
1615
1571
|
}
|
|
1616
1572
|
};
|
|
1617
1573
|
|
|
@@ -1620,6 +1576,20 @@ Q5.renderers.c2d.image = ($, q) => {
|
|
|
1620
1576
|
|
|
1621
1577
|
if ($._scope == 'image') return;
|
|
1622
1578
|
|
|
1579
|
+
$._saveCanvas = async (data, ext) => {
|
|
1580
|
+
data = data.canvas || data;
|
|
1581
|
+
if (data instanceof OffscreenCanvas) {
|
|
1582
|
+
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
1583
|
+
|
|
1584
|
+
return await new Promise((resolve) => {
|
|
1585
|
+
const reader = new FileReader();
|
|
1586
|
+
reader.onloadend = () => resolve(reader.result);
|
|
1587
|
+
reader.readAsDataURL(blob);
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
return data.toDataURL('image/' + ext);
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1623
1593
|
$.tint = function (c) {
|
|
1624
1594
|
$._tint = (c._q5Color ? c : $.color(...arguments)).toString();
|
|
1625
1595
|
};
|
|
@@ -1963,10 +1933,15 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1963
1933
|
tY = leading * lines.length;
|
|
1964
1934
|
|
|
1965
1935
|
if (!img) {
|
|
1936
|
+
let ogBaseline = $.ctx.textBaseline;
|
|
1937
|
+
$.ctx.textBaseline = 'alphabetic';
|
|
1938
|
+
|
|
1966
1939
|
let measure = ctx.measureText(' ');
|
|
1967
1940
|
let ascent = measure.fontBoundingBoxAscent;
|
|
1968
1941
|
let descent = measure.fontBoundingBoxDescent;
|
|
1969
1942
|
|
|
1943
|
+
$.ctx.textBaseline = ogBaseline;
|
|
1944
|
+
|
|
1970
1945
|
img = $.createImage.call($, Math.ceil(ctx.measureText(str).width), Math.ceil(tY + descent), {
|
|
1971
1946
|
pixelDensity: $._pixelDensity
|
|
1972
1947
|
});
|
|
@@ -1977,12 +1952,13 @@ Q5.renderers.c2d.text = ($, q) => {
|
|
|
1977
1952
|
img._middle = img._top + ascent * 0.5;
|
|
1978
1953
|
img._bottom = img._top + ascent;
|
|
1979
1954
|
img._leading = leading;
|
|
1955
|
+
} else {
|
|
1956
|
+
img.modified = true;
|
|
1980
1957
|
}
|
|
1981
1958
|
|
|
1982
1959
|
img._fill = $._fill;
|
|
1983
1960
|
img._stroke = $._stroke;
|
|
1984
1961
|
img._strokeWeight = $._strokeWeight;
|
|
1985
|
-
img.modified = true;
|
|
1986
1962
|
|
|
1987
1963
|
ctx = img.ctx;
|
|
1988
1964
|
|
|
@@ -2913,7 +2889,7 @@ Q5.modules.input = ($, q) => {
|
|
|
2913
2889
|
$._onwheel = (e) => {
|
|
2914
2890
|
$._updateMouse(e);
|
|
2915
2891
|
e.delta = e.deltaY;
|
|
2916
|
-
if ($.mouseWheel(e) == false) e.preventDefault();
|
|
2892
|
+
if ($.mouseWheel(e) == false || $._noScroll) e.preventDefault();
|
|
2917
2893
|
};
|
|
2918
2894
|
|
|
2919
2895
|
$.cursor = (name, x, y) => {
|
|
@@ -2928,9 +2904,8 @@ Q5.modules.input = ($, q) => {
|
|
|
2928
2904
|
$.canvas.style.cursor = name + pfx;
|
|
2929
2905
|
};
|
|
2930
2906
|
|
|
2931
|
-
$.noCursor = () =>
|
|
2932
|
-
|
|
2933
|
-
};
|
|
2907
|
+
$.noCursor = () => ($.canvas.style.cursor = 'none');
|
|
2908
|
+
$.noScroll = () => ($._noScroll = true);
|
|
2934
2909
|
|
|
2935
2910
|
if (window) {
|
|
2936
2911
|
$.lockMouse = document.body?.requestPointerLock;
|
|
@@ -4044,6 +4019,10 @@ Q5.modules.util = ($, q) => {
|
|
|
4044
4019
|
$.loadJSON = (url, cb) => $._loadFile(url, cb, 'json');
|
|
4045
4020
|
$.loadCSV = (url, cb) => $._loadFile(url, cb, 'csv');
|
|
4046
4021
|
|
|
4022
|
+
const imgRegex = /(jpe?g|png|gif|webp|avif|svg)/,
|
|
4023
|
+
fontRegex = /(ttf|otf|woff2?|eot|json)/,
|
|
4024
|
+
audioRegex = /(wav|flac|mp3|ogg|m4a|aac|aiff|weba)/;
|
|
4025
|
+
|
|
4047
4026
|
$.load = function (...urls) {
|
|
4048
4027
|
if (Array.isArray(urls[0])) urls = urls[0];
|
|
4049
4028
|
|
|
@@ -4057,11 +4036,11 @@ Q5.modules.util = ($, q) => {
|
|
|
4057
4036
|
obj = $.loadJSON(url);
|
|
4058
4037
|
} else if (ext == 'csv') {
|
|
4059
4038
|
obj = $.loadCSV(url);
|
|
4060
|
-
} else if (
|
|
4039
|
+
} else if (imgRegex.test(ext)) {
|
|
4061
4040
|
obj = $.loadImage(url);
|
|
4062
|
-
} else if (
|
|
4041
|
+
} else if (fontRegex.test(ext)) {
|
|
4063
4042
|
obj = $.loadFont(url);
|
|
4064
|
-
} else if (
|
|
4043
|
+
} else if (audioRegex.test(ext)) {
|
|
4065
4044
|
obj = $.loadSound(url);
|
|
4066
4045
|
} else {
|
|
4067
4046
|
obj = $.loadText(url);
|
|
@@ -4073,6 +4052,40 @@ Q5.modules.util = ($, q) => {
|
|
|
4073
4052
|
return Promise.all(loaders);
|
|
4074
4053
|
};
|
|
4075
4054
|
|
|
4055
|
+
async function saveFile(data, name, ext) {
|
|
4056
|
+
name = name || 'untitled';
|
|
4057
|
+
ext = ext || 'png';
|
|
4058
|
+
if (imgRegex.test(ext)) {
|
|
4059
|
+
data = await $._saveCanvas(data, ext);
|
|
4060
|
+
} else {
|
|
4061
|
+
let type = 'text/plain';
|
|
4062
|
+
if (ext == 'json') {
|
|
4063
|
+
if (typeof data != 'string') data = JSON.stringify(data);
|
|
4064
|
+
type = 'text/json';
|
|
4065
|
+
}
|
|
4066
|
+
data = new Blob([data], { type });
|
|
4067
|
+
data = URL.createObjectURL(data);
|
|
4068
|
+
}
|
|
4069
|
+
let a = document.createElement('a');
|
|
4070
|
+
a.href = data;
|
|
4071
|
+
a.download = name + '.' + ext;
|
|
4072
|
+
a.click();
|
|
4073
|
+
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
$.save = (a, b, c) => {
|
|
4077
|
+
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
4078
|
+
c = b;
|
|
4079
|
+
b = a;
|
|
4080
|
+
a = $.canvas;
|
|
4081
|
+
}
|
|
4082
|
+
if (c) saveFile(a, b, c);
|
|
4083
|
+
else if (b) {
|
|
4084
|
+
let lastDot = b.lastIndexOf('.');
|
|
4085
|
+
saveFile(a, b.slice(0, lastDot), b.slice(lastDot + 1));
|
|
4086
|
+
} else saveFile(a);
|
|
4087
|
+
};
|
|
4088
|
+
|
|
4076
4089
|
$.CSV = {};
|
|
4077
4090
|
$.CSV.parse = (csv, sep = ',', lineSep = '\n') => {
|
|
4078
4091
|
if (!csv.length) return [];
|
|
@@ -4088,6 +4101,10 @@ Q5.modules.util = ($, q) => {
|
|
|
4088
4101
|
return a;
|
|
4089
4102
|
};
|
|
4090
4103
|
|
|
4104
|
+
if ($.canvas && !Q5._createServerCanvas) {
|
|
4105
|
+
$.canvas.save = $.saveCanvas = $.save;
|
|
4106
|
+
}
|
|
4107
|
+
|
|
4091
4108
|
if (typeof localStorage == 'object') {
|
|
4092
4109
|
$.storeItem = localStorage.setItem;
|
|
4093
4110
|
$.getItem = localStorage.getItem;
|
|
@@ -4406,11 +4423,6 @@ Q5.Vector.sub = (v, u) => v.copy().sub(u);
|
|
|
4406
4423
|
for (let k of ['fromAngle', 'fromAngles', 'random2D', 'random3D']) {
|
|
4407
4424
|
Q5.Vector[k] = (u, v, t) => new Q5.Vector()[k](u, v, t);
|
|
4408
4425
|
}
|
|
4409
|
-
/**
|
|
4410
|
-
* q5-webgpu
|
|
4411
|
-
*
|
|
4412
|
-
* EXPERIMENTAL, for developer testing only!
|
|
4413
|
-
*/
|
|
4414
4426
|
Q5.renderers.webgpu = {};
|
|
4415
4427
|
|
|
4416
4428
|
Q5.renderers.webgpu.canvas = ($, q) => {
|
|
@@ -4426,6 +4438,11 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
4426
4438
|
|
|
4427
4439
|
let pass,
|
|
4428
4440
|
mainView,
|
|
4441
|
+
frameTextureA,
|
|
4442
|
+
frameTextureB,
|
|
4443
|
+
frameSampler,
|
|
4444
|
+
framePipeline,
|
|
4445
|
+
frameBindGroup,
|
|
4429
4446
|
colorIndex = 1,
|
|
4430
4447
|
colorStackIndex = 8;
|
|
4431
4448
|
|
|
@@ -4474,14 +4491,60 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
4474
4491
|
});
|
|
4475
4492
|
|
|
4476
4493
|
let createMainView = () => {
|
|
4494
|
+
let w = $.canvas.width,
|
|
4495
|
+
h = $.canvas.height,
|
|
4496
|
+
size = [w, h],
|
|
4497
|
+
format = 'bgra8unorm';
|
|
4498
|
+
|
|
4477
4499
|
mainView = Q5.device
|
|
4478
4500
|
.createTexture({
|
|
4479
|
-
size
|
|
4501
|
+
size,
|
|
4480
4502
|
sampleCount: 4,
|
|
4481
|
-
format
|
|
4503
|
+
format,
|
|
4482
4504
|
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
4483
4505
|
})
|
|
4484
4506
|
.createView();
|
|
4507
|
+
|
|
4508
|
+
let usage =
|
|
4509
|
+
GPUTextureUsage.COPY_SRC |
|
|
4510
|
+
GPUTextureUsage.COPY_DST |
|
|
4511
|
+
GPUTextureUsage.TEXTURE_BINDING |
|
|
4512
|
+
GPUTextureUsage.RENDER_ATTACHMENT;
|
|
4513
|
+
|
|
4514
|
+
frameTextureA = Q5.device.createTexture({ size, format, usage });
|
|
4515
|
+
frameTextureB = Q5.device.createTexture({ size, format, usage });
|
|
4516
|
+
|
|
4517
|
+
let finalShader = Q5.device.createShaderModule({
|
|
4518
|
+
code: `
|
|
4519
|
+
@vertex fn v(@builtin(vertex_index)i:u32)->@builtin(position)vec4<f32>{
|
|
4520
|
+
const pos=array(vec2(-1f,-1f),vec2(1f,-1f),vec2(-1f,1f),vec2(1f,1f));
|
|
4521
|
+
return vec4(pos[i],0f,1f);
|
|
4522
|
+
}
|
|
4523
|
+
@group(0) @binding(0) var s: sampler;
|
|
4524
|
+
@group(0) @binding(1) var t: texture_2d<f32>;
|
|
4525
|
+
@fragment fn f(@builtin(position)c:vec4<f32>)->@location(0)vec4<f32>{
|
|
4526
|
+
let uv=c.xy/vec2(${w}, ${h});
|
|
4527
|
+
return textureSample(t,s,uv);
|
|
4528
|
+
}`
|
|
4529
|
+
});
|
|
4530
|
+
|
|
4531
|
+
frameSampler = Q5.device.createSampler({
|
|
4532
|
+
magFilter: 'linear',
|
|
4533
|
+
minFilter: 'linear'
|
|
4534
|
+
});
|
|
4535
|
+
|
|
4536
|
+
// Create a pipeline for rendering
|
|
4537
|
+
framePipeline = Q5.device.createRenderPipeline({
|
|
4538
|
+
layout: 'auto',
|
|
4539
|
+
vertex: { module: finalShader, entryPoint: 'v' },
|
|
4540
|
+
fragment: {
|
|
4541
|
+
module: finalShader,
|
|
4542
|
+
entryPoint: 'f',
|
|
4543
|
+
targets: [{ format, writeMask: GPUColorWrite.ALL }]
|
|
4544
|
+
},
|
|
4545
|
+
primitive: { topology: 'triangle-strip' },
|
|
4546
|
+
multisample: { count: 4 }
|
|
4547
|
+
});
|
|
4485
4548
|
};
|
|
4486
4549
|
|
|
4487
4550
|
$._createCanvas = (w, h, opt) => {
|
|
@@ -4847,23 +4910,52 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
4847
4910
|
}
|
|
4848
4911
|
};
|
|
4849
4912
|
|
|
4850
|
-
|
|
4913
|
+
let shouldClear = false;
|
|
4914
|
+
$.clear = () => {
|
|
4915
|
+
shouldClear = true;
|
|
4916
|
+
};
|
|
4917
|
+
|
|
4918
|
+
const _drawFrame = () => {
|
|
4919
|
+
pass.setPipeline(framePipeline);
|
|
4920
|
+
pass.setBindGroup(0, frameBindGroup);
|
|
4921
|
+
pass.draw(4);
|
|
4922
|
+
};
|
|
4851
4923
|
|
|
4852
4924
|
$._beginRender = () => {
|
|
4925
|
+
// swap the frame textures
|
|
4926
|
+
const temp = frameTextureA;
|
|
4927
|
+
frameTextureA = frameTextureB;
|
|
4928
|
+
frameTextureB = temp;
|
|
4929
|
+
$.canvas.texture = frameTextureA;
|
|
4930
|
+
|
|
4853
4931
|
$.encoder = Q5.device.createCommandEncoder();
|
|
4854
4932
|
|
|
4933
|
+
let target = shouldClear ? $.ctx.getCurrentTexture().createView() : frameTextureA.createView();
|
|
4934
|
+
|
|
4855
4935
|
pass = q.pass = $.encoder.beginRenderPass({
|
|
4856
4936
|
label: 'q5-webgpu',
|
|
4857
4937
|
colorAttachments: [
|
|
4858
4938
|
{
|
|
4859
4939
|
view: mainView,
|
|
4860
|
-
resolveTarget:
|
|
4940
|
+
resolveTarget: target,
|
|
4861
4941
|
loadOp: 'clear',
|
|
4862
4942
|
storeOp: 'store',
|
|
4863
4943
|
clearValue: [0, 0, 0, 0]
|
|
4864
4944
|
}
|
|
4865
4945
|
]
|
|
4866
4946
|
});
|
|
4947
|
+
|
|
4948
|
+
if (!shouldClear) {
|
|
4949
|
+
frameBindGroup = Q5.device.createBindGroup({
|
|
4950
|
+
layout: framePipeline.getBindGroupLayout(0),
|
|
4951
|
+
entries: [
|
|
4952
|
+
{ binding: 0, resource: frameSampler },
|
|
4953
|
+
{ binding: 1, resource: frameTextureB.createView() }
|
|
4954
|
+
]
|
|
4955
|
+
});
|
|
4956
|
+
|
|
4957
|
+
_drawFrame();
|
|
4958
|
+
}
|
|
4867
4959
|
};
|
|
4868
4960
|
|
|
4869
4961
|
$._render = () => {
|
|
@@ -4938,8 +5030,33 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
4938
5030
|
|
|
4939
5031
|
$._finishRender = () => {
|
|
4940
5032
|
pass.end();
|
|
4941
|
-
|
|
4942
|
-
|
|
5033
|
+
|
|
5034
|
+
if (!shouldClear) {
|
|
5035
|
+
pass = $.encoder.beginRenderPass({
|
|
5036
|
+
colorAttachments: [
|
|
5037
|
+
{
|
|
5038
|
+
view: mainView,
|
|
5039
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
5040
|
+
loadOp: 'clear',
|
|
5041
|
+
storeOp: 'store',
|
|
5042
|
+
clearValue: [0, 0, 0, 0]
|
|
5043
|
+
}
|
|
5044
|
+
]
|
|
5045
|
+
});
|
|
5046
|
+
|
|
5047
|
+
frameBindGroup = Q5.device.createBindGroup({
|
|
5048
|
+
layout: framePipeline.getBindGroupLayout(0),
|
|
5049
|
+
entries: [
|
|
5050
|
+
{ binding: 0, resource: frameSampler },
|
|
5051
|
+
{ binding: 1, resource: frameTextureA.createView() }
|
|
5052
|
+
]
|
|
5053
|
+
});
|
|
5054
|
+
_drawFrame();
|
|
5055
|
+
pass.end();
|
|
5056
|
+
shouldClear = false;
|
|
5057
|
+
}
|
|
5058
|
+
|
|
5059
|
+
Q5.device.queue.submit([$.encoder.finish()]);
|
|
4943
5060
|
|
|
4944
5061
|
q.pass = $.encoder = null;
|
|
4945
5062
|
|
|
@@ -5734,12 +5851,10 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
|
5734
5851
|
|
|
5735
5852
|
$.smooth();
|
|
5736
5853
|
|
|
5737
|
-
let MAX_TEXTURES = 12000;
|
|
5738
|
-
|
|
5739
|
-
$._textures = [];
|
|
5740
5854
|
let tIdx = 0;
|
|
5741
5855
|
|
|
5742
5856
|
$._createTexture = (img) => {
|
|
5857
|
+
let g = img;
|
|
5743
5858
|
if (img.canvas) img = img.canvas;
|
|
5744
5859
|
|
|
5745
5860
|
let textureSize = [img.width, img.height, 1];
|
|
@@ -5759,26 +5874,18 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
|
5759
5874
|
textureSize
|
|
5760
5875
|
);
|
|
5761
5876
|
|
|
5762
|
-
|
|
5763
|
-
|
|
5877
|
+
g.texture = texture;
|
|
5878
|
+
g.textureIndex = tIdx;
|
|
5764
5879
|
|
|
5765
|
-
|
|
5880
|
+
$._textureBindGroups[tIdx] = Q5.device.createBindGroup({
|
|
5766
5881
|
layout: textureLayout,
|
|
5767
5882
|
entries: [
|
|
5768
5883
|
{ binding: 0, resource: sampler },
|
|
5769
5884
|
{ binding: 1, resource: texture.createView() }
|
|
5770
5885
|
]
|
|
5771
5886
|
});
|
|
5772
|
-
$._textureBindGroups[tIdx] = textureBindGroup;
|
|
5773
|
-
|
|
5774
|
-
tIdx = (tIdx + 1) % MAX_TEXTURES;
|
|
5775
5887
|
|
|
5776
|
-
|
|
5777
|
-
if ($._textures[tIdx]) {
|
|
5778
|
-
$._textures[tIdx].destroy();
|
|
5779
|
-
delete $._textures[tIdx];
|
|
5780
|
-
delete $._textureBindGroups[tIdx];
|
|
5781
|
-
}
|
|
5888
|
+
tIdx++;
|
|
5782
5889
|
};
|
|
5783
5890
|
|
|
5784
5891
|
$.loadImage = (src, cb) => {
|
|
@@ -5809,23 +5916,31 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
|
5809
5916
|
};
|
|
5810
5917
|
|
|
5811
5918
|
$.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
5812
|
-
let g = img;
|
|
5813
|
-
if (img.canvas) img = img.canvas;
|
|
5814
5919
|
if (img.textureIndex == undefined) return;
|
|
5815
5920
|
|
|
5921
|
+
let cnv = img.canvas || img;
|
|
5922
|
+
|
|
5816
5923
|
if ($._matrixDirty) $._saveMatrix();
|
|
5817
5924
|
|
|
5818
|
-
let w =
|
|
5819
|
-
h =
|
|
5820
|
-
pd =
|
|
5925
|
+
let w = cnv.width,
|
|
5926
|
+
h = cnv.height,
|
|
5927
|
+
pd = img._pixelDensity || 1;
|
|
5821
5928
|
|
|
5822
|
-
|
|
5823
|
-
|
|
5929
|
+
if (img.modified) {
|
|
5930
|
+
Q5.device.queue.copyExternalImageToTexture(
|
|
5931
|
+
{ source: cnv },
|
|
5932
|
+
{ texture: img.texture, colorSpace: $.canvas.colorSpace },
|
|
5933
|
+
[w, h, 1]
|
|
5934
|
+
);
|
|
5935
|
+
img.modified = false;
|
|
5936
|
+
}
|
|
5937
|
+
|
|
5938
|
+
dw ??= img.defaultWidth;
|
|
5939
|
+
dh ??= img.defaultHeight;
|
|
5824
5940
|
sw ??= w;
|
|
5825
5941
|
sh ??= h;
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
dh *= pd;
|
|
5942
|
+
sx *= pd;
|
|
5943
|
+
sy *= pd;
|
|
5829
5944
|
|
|
5830
5945
|
let [l, r, t, b] = $._calcBox(dx, dy, dw, dh, $._imageMode);
|
|
5831
5946
|
|
|
@@ -5845,8 +5960,55 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
|
|
|
5845
5960
|
$.drawStack.push(1, img.textureIndex);
|
|
5846
5961
|
};
|
|
5847
5962
|
|
|
5963
|
+
$._saveCanvas = async (data, ext) => {
|
|
5964
|
+
let texture = data.texture,
|
|
5965
|
+
w = texture.width,
|
|
5966
|
+
h = texture.height,
|
|
5967
|
+
bytesPerRow = Math.ceil((w * 4) / 256) * 256;
|
|
5968
|
+
|
|
5969
|
+
let buffer = Q5.device.createBuffer({
|
|
5970
|
+
size: bytesPerRow * h,
|
|
5971
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
|
|
5972
|
+
});
|
|
5973
|
+
|
|
5974
|
+
let en = Q5.device.createCommandEncoder();
|
|
5975
|
+
|
|
5976
|
+
en.copyTextureToBuffer({ texture }, { buffer, bytesPerRow, rowsPerImage: h }, { width: w, height: h });
|
|
5977
|
+
|
|
5978
|
+
Q5.device.queue.submit([en.finish()]);
|
|
5979
|
+
|
|
5980
|
+
await buffer.mapAsync(GPUMapMode.READ);
|
|
5981
|
+
|
|
5982
|
+
let pad = new Uint8Array(buffer.getMappedRange());
|
|
5983
|
+
data = new Uint8Array(w * h * 4); // unpadded data
|
|
5984
|
+
|
|
5985
|
+
// Remove padding from each row
|
|
5986
|
+
for (let y = 0; y < h; y++) {
|
|
5987
|
+
const p = y * bytesPerRow; // padded row offset
|
|
5988
|
+
const u = y * w * 4; // unpadded row offset
|
|
5989
|
+
data.set(pad.subarray(p, p + w * 4), u);
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5992
|
+
buffer.unmap();
|
|
5993
|
+
|
|
5994
|
+
let colorSpace = $.canvas.colorSpace;
|
|
5995
|
+
data = new Uint8ClampedArray(data.buffer);
|
|
5996
|
+
data = new ImageData(data, w, h, { colorSpace });
|
|
5997
|
+
let cnv = new OffscreenCanvas(w, h);
|
|
5998
|
+
let ctx = cnv.getContext('2d', { colorSpace });
|
|
5999
|
+
ctx.putImageData(data, 0, 0);
|
|
6000
|
+
|
|
6001
|
+
// Convert to blob then data URL
|
|
6002
|
+
let blob = await cnv.convertToBlob({ type: 'image/' + ext });
|
|
6003
|
+
return await new Promise((resolve) => {
|
|
6004
|
+
let r = new FileReader();
|
|
6005
|
+
r.onloadend = () => resolve(r.result);
|
|
6006
|
+
r.readAsDataURL(blob);
|
|
6007
|
+
});
|
|
6008
|
+
};
|
|
6009
|
+
|
|
5848
6010
|
$._hooks.preRender.push(() => {
|
|
5849
|
-
if (
|
|
6011
|
+
if (!vertIndex) return;
|
|
5850
6012
|
|
|
5851
6013
|
// Switch to image pipeline
|
|
5852
6014
|
$.pass.setPipeline($._pipelines[1]);
|
|
@@ -6387,20 +6549,14 @@ fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
|
|
|
6387
6549
|
}
|
|
6388
6550
|
|
|
6389
6551
|
let img = $._g.createTextImage(str, w, h);
|
|
6390
|
-
|
|
6391
|
-
if (img.canvas.textureIndex == undefined) {
|
|
6552
|
+
if (img.textureIndex == undefined) {
|
|
6392
6553
|
$._createTexture(img);
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
{ source: cnv },
|
|
6400
|
-
{ texture, colorSpace: $.canvas.colorSpace },
|
|
6401
|
-
textureSize
|
|
6402
|
-
);
|
|
6403
|
-
img.modified = false;
|
|
6554
|
+
let _copy = img.copy;
|
|
6555
|
+
img.copy = function () {
|
|
6556
|
+
let copy = _copy();
|
|
6557
|
+
$._createTexture(copy);
|
|
6558
|
+
return copy;
|
|
6559
|
+
};
|
|
6404
6560
|
}
|
|
6405
6561
|
return img;
|
|
6406
6562
|
};
|