q5 2.0.17 → 2.2.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/.vscode/settings.json +3 -0
- package/README.md +10 -7
- package/package.json +6 -4
- package/q5-webgpu.js +3554 -0
- package/q5-webgpu.min.js +8 -0
- package/q5.js +543 -590
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +51 -174
- package/src/q5-2d-drawing.js +9 -55
- package/src/q5-2d-image.js +141 -279
- package/src/q5-2d-soft-filters.js +6 -6
- package/src/q5-2d-text.js +3 -3
- package/src/q5-ai.js +2 -2
- package/src/q5-canvas.js +200 -0
- package/src/q5-color.js +66 -39
- package/src/q5-core.js +41 -33
- package/src/q5-display.js +6 -0
- package/src/q5-input.js +46 -34
- package/src/q5-math.js +3 -3
- package/src/q5-sound.js +10 -3
- package/src/q5-util.js +3 -3
- package/src/q5-vector.js +1 -1
- package/src/q5-webgpu-canvas.js +290 -0
- package/src/q5-webgpu-drawing.js +369 -0
- package/src/q5-webgpu-image.js +1 -0
- package/src/q5-webgpu-text.js +1 -0
- package/src/readme.md +83 -6
package/q5.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.2
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
7
7
|
*/
|
|
8
|
-
function Q5(scope, parent) {
|
|
8
|
+
function Q5(scope, parent, renderer) {
|
|
9
9
|
let $ = this;
|
|
10
10
|
$._q5 = true;
|
|
11
|
-
$._scope = scope;
|
|
12
11
|
$._parent = parent;
|
|
12
|
+
$._renderer = renderer || 'q2d';
|
|
13
13
|
$._preloadCount = 0;
|
|
14
14
|
|
|
15
15
|
scope ??= 'global';
|
|
@@ -17,13 +17,14 @@ function Q5(scope, parent) {
|
|
|
17
17
|
if (!(window.setup || window.draw)) return;
|
|
18
18
|
scope = 'global';
|
|
19
19
|
}
|
|
20
|
+
$._scope = scope;
|
|
20
21
|
let globalScope;
|
|
21
22
|
if (scope == 'global') {
|
|
22
23
|
Q5._hasGlobal = $._isGlobal = true;
|
|
23
24
|
globalScope = !Q5._nodejs ? window : global;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
let
|
|
27
|
+
let q = new Proxy($, {
|
|
27
28
|
set: (t, p, v) => {
|
|
28
29
|
$[p] = v;
|
|
29
30
|
if ($._isGlobal) globalScope[p] = v;
|
|
@@ -41,6 +42,10 @@ function Q5(scope, parent) {
|
|
|
41
42
|
$._targetFrameDuration = 16.666666666666668;
|
|
42
43
|
$._frameRate = $._fps = 60;
|
|
43
44
|
$._loop = true;
|
|
45
|
+
$._hooks = {
|
|
46
|
+
postCanvas: [],
|
|
47
|
+
preRender: []
|
|
48
|
+
};
|
|
44
49
|
|
|
45
50
|
let millisStart = 0;
|
|
46
51
|
$.millis = () => performance.now() - millisStart;
|
|
@@ -48,7 +53,7 @@ function Q5(scope, parent) {
|
|
|
48
53
|
$.noCanvas = () => {
|
|
49
54
|
if ($.canvas?.remove) $.canvas.remove();
|
|
50
55
|
$.canvas = 0;
|
|
51
|
-
|
|
56
|
+
q.ctx = q.drawingContext = 0;
|
|
52
57
|
};
|
|
53
58
|
|
|
54
59
|
if (window) {
|
|
@@ -57,10 +62,10 @@ function Q5(scope, parent) {
|
|
|
57
62
|
$.deviceOrientation = window.screen?.orientation?.type;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
$._incrementPreload = () =>
|
|
61
|
-
$._decrementPreload = () =>
|
|
65
|
+
$._incrementPreload = () => q._preloadCount++;
|
|
66
|
+
$._decrementPreload = () => q._preloadCount--;
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
$._draw = (timestamp) => {
|
|
64
69
|
let ts = timestamp || performance.now();
|
|
65
70
|
$._lastFrameTime ??= ts - $._targetFrameDuration;
|
|
66
71
|
|
|
@@ -69,44 +74,43 @@ function Q5(scope, parent) {
|
|
|
69
74
|
$._shouldResize = false;
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
if ($._loop) looper = raf(_draw);
|
|
77
|
+
if ($._loop) looper = raf($._draw);
|
|
73
78
|
else if ($.frameCount && !$._redraw) return;
|
|
74
79
|
|
|
75
80
|
if (looper && $.frameCount) {
|
|
76
81
|
let time_since_last = ts - $._lastFrameTime;
|
|
77
|
-
if (time_since_last < $._targetFrameDuration -
|
|
82
|
+
if (time_since_last < $._targetFrameDuration - 4) return;
|
|
78
83
|
}
|
|
79
|
-
|
|
84
|
+
q.deltaTime = ts - $._lastFrameTime;
|
|
80
85
|
$._frameRate = 1000 / $.deltaTime;
|
|
81
|
-
|
|
86
|
+
q.frameCount++;
|
|
82
87
|
let pre = performance.now();
|
|
83
|
-
|
|
84
|
-
if ($.
|
|
88
|
+
if ($.ctx) $.resetMatrix();
|
|
89
|
+
if ($._beginRender) $._beginRender();
|
|
90
|
+
for (let m of Q5.methods.pre) m.call($);
|
|
85
91
|
$.draw();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
p.pmouseX = $.mouseX;
|
|
92
|
-
p.pmouseY = $.mouseY;
|
|
92
|
+
if ($._render) $._render();
|
|
93
|
+
for (let m of Q5.methods.post) m.call($);
|
|
94
|
+
if ($._finishRender) $._finishRender();
|
|
95
|
+
q.pmouseX = $.mouseX;
|
|
96
|
+
q.pmouseY = $.mouseY;
|
|
93
97
|
$._lastFrameTime = ts;
|
|
94
98
|
let post = performance.now();
|
|
95
99
|
$._fps = Math.round(1000 / (post - pre));
|
|
96
|
-
}
|
|
100
|
+
};
|
|
97
101
|
$.noLoop = () => {
|
|
98
102
|
$._loop = false;
|
|
99
103
|
looper = null;
|
|
100
104
|
};
|
|
101
105
|
$.loop = () => {
|
|
102
106
|
$._loop = true;
|
|
103
|
-
if (looper == null) _draw();
|
|
107
|
+
if (looper == null) $._draw();
|
|
104
108
|
};
|
|
105
109
|
$.isLooping = () => $._loop;
|
|
106
110
|
$.redraw = (n = 1) => {
|
|
107
111
|
$._redraw = true;
|
|
108
112
|
for (let i = 0; i < n; i++) {
|
|
109
|
-
_draw();
|
|
113
|
+
$._draw();
|
|
110
114
|
}
|
|
111
115
|
$._redraw = false;
|
|
112
116
|
};
|
|
@@ -136,7 +140,12 @@ function Q5(scope, parent) {
|
|
|
136
140
|
$.describe = () => {};
|
|
137
141
|
|
|
138
142
|
for (let m in Q5.modules) {
|
|
139
|
-
Q5.modules[m]($,
|
|
143
|
+
Q5.modules[m]($, q);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let r = Q5.renderers[$._renderer];
|
|
147
|
+
for (let m in r) {
|
|
148
|
+
r[m]($, q);
|
|
140
149
|
}
|
|
141
150
|
|
|
142
151
|
// INIT
|
|
@@ -147,12 +156,14 @@ function Q5(scope, parent) {
|
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
|
|
159
|
+
if (scope == 'graphics') return;
|
|
160
|
+
|
|
150
161
|
if (scope == 'global') {
|
|
151
162
|
Object.assign(Q5, $);
|
|
152
163
|
delete Q5.Q5;
|
|
153
164
|
}
|
|
154
165
|
|
|
155
|
-
for (let m of Q5.
|
|
166
|
+
for (let m of Q5.methods.init) {
|
|
156
167
|
m.call($);
|
|
157
168
|
}
|
|
158
169
|
|
|
@@ -169,8 +180,6 @@ function Q5(scope, parent) {
|
|
|
169
180
|
|
|
170
181
|
if (typeof scope == 'function') scope($);
|
|
171
182
|
|
|
172
|
-
if (scope == 'graphics') return;
|
|
173
|
-
|
|
174
183
|
Q5._instanceCount++;
|
|
175
184
|
|
|
176
185
|
let raf =
|
|
@@ -218,8 +227,6 @@ function Q5(scope, parent) {
|
|
|
218
227
|
|
|
219
228
|
if (!($.setup || $.draw)) return;
|
|
220
229
|
|
|
221
|
-
$._startDone = false;
|
|
222
|
-
|
|
223
230
|
async function _start() {
|
|
224
231
|
$._startDone = true;
|
|
225
232
|
if ($._preloadCount > 0) return raf(_start);
|
|
@@ -229,7 +236,7 @@ function Q5(scope, parent) {
|
|
|
229
236
|
if ($.ctx === null) $.createCanvas(100, 100);
|
|
230
237
|
$._setupDone = true;
|
|
231
238
|
if ($.ctx) $.resetMatrix();
|
|
232
|
-
raf(_draw);
|
|
239
|
+
raf($._draw);
|
|
233
240
|
}
|
|
234
241
|
|
|
235
242
|
if ((arguments.length && scope != 'namespace') || preloadDefined) {
|
|
@@ -243,6 +250,7 @@ function Q5(scope, parent) {
|
|
|
243
250
|
}
|
|
244
251
|
}
|
|
245
252
|
|
|
253
|
+
Q5.renderers = {};
|
|
246
254
|
Q5.modules = {};
|
|
247
255
|
|
|
248
256
|
Q5._nodejs = typeof process == 'object';
|
|
@@ -253,13 +261,13 @@ Q5._friendlyError = (msg, func) => {
|
|
|
253
261
|
};
|
|
254
262
|
Q5._validateParameters = () => true;
|
|
255
263
|
|
|
256
|
-
Q5.
|
|
264
|
+
Q5.methods = {
|
|
257
265
|
init: [],
|
|
258
266
|
pre: [],
|
|
259
267
|
post: [],
|
|
260
268
|
remove: []
|
|
261
269
|
};
|
|
262
|
-
Q5.prototype.registerMethod = (m, fn) => Q5.
|
|
270
|
+
Q5.prototype.registerMethod = (m, fn) => Q5.methods[m].push(fn);
|
|
263
271
|
Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.prototype[n] = fn[n]);
|
|
264
272
|
|
|
265
273
|
if (Q5._nodejs) global.p5 ??= global.Q5 = Q5;
|
|
@@ -271,7 +279,7 @@ if (typeof document == 'object') {
|
|
|
271
279
|
if (!Q5._hasGlobal) new Q5('auto');
|
|
272
280
|
});
|
|
273
281
|
}
|
|
274
|
-
Q5.modules.
|
|
282
|
+
Q5.modules.canvas = ($, q) => {
|
|
275
283
|
$._OffscreenCanvas =
|
|
276
284
|
window.OffscreenCanvas ||
|
|
277
285
|
function () {
|
|
@@ -280,59 +288,28 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
280
288
|
|
|
281
289
|
if (Q5._nodejs) {
|
|
282
290
|
if (Q5._createNodeJSCanvas) {
|
|
283
|
-
|
|
291
|
+
q.canvas = Q5._createNodeJSCanvas(100, 100);
|
|
284
292
|
}
|
|
285
293
|
} else if ($._scope == 'image' || $._scope == 'graphics') {
|
|
286
|
-
|
|
294
|
+
q.canvas = new $._OffscreenCanvas(100, 100);
|
|
287
295
|
}
|
|
296
|
+
|
|
288
297
|
if (!$.canvas) {
|
|
289
298
|
if (typeof document == 'object') {
|
|
290
|
-
|
|
299
|
+
q.canvas = document.createElement('canvas');
|
|
291
300
|
$.canvas.id = 'q5Canvas' + Q5._instanceCount;
|
|
292
301
|
$.canvas.classList.add('q5Canvas');
|
|
293
302
|
} else $.noCanvas();
|
|
294
303
|
}
|
|
295
304
|
|
|
296
305
|
let c = $.canvas;
|
|
297
|
-
|
|
298
306
|
c.width = $.width = 100;
|
|
299
307
|
c.height = $.height = 100;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
$.
|
|
303
|
-
let parent = $._parent;
|
|
304
|
-
if (parent && typeof parent == 'string') {
|
|
305
|
-
parent = document.getElementById(parent);
|
|
306
|
-
}
|
|
307
|
-
c.parent = (el) => {
|
|
308
|
-
if (typeof el == 'string') el = document.getElementById(el);
|
|
309
|
-
el.append(c);
|
|
310
|
-
|
|
311
|
-
function parentResized() {
|
|
312
|
-
if ($.frameCount > 1) {
|
|
313
|
-
$._shouldResize = true;
|
|
314
|
-
$._adjustDisplay();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (typeof ResizeObserver == 'function') {
|
|
318
|
-
if ($._ro) $._ro.disconnect();
|
|
319
|
-
$._ro = new ResizeObserver(parentResized);
|
|
320
|
-
$._ro.observe(parent);
|
|
321
|
-
} else if (!$.frameCount) {
|
|
322
|
-
window.addEventListener('resize', parentResized);
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
function appendCanvas() {
|
|
326
|
-
parent ??= document.getElementsByTagName('main')[0];
|
|
327
|
-
if (!parent) {
|
|
328
|
-
parent = document.createElement('main');
|
|
329
|
-
document.body.append(parent);
|
|
330
|
-
}
|
|
331
|
-
c.parent(parent);
|
|
332
|
-
}
|
|
333
|
-
if (document.body) appendCanvas();
|
|
334
|
-
else document.addEventListener('DOMContentLoaded', appendCanvas);
|
|
308
|
+
if ($._scope != 'image') {
|
|
309
|
+
c.renderer = $._renderer;
|
|
310
|
+
c[$._renderer] = true;
|
|
335
311
|
}
|
|
312
|
+
$._pixelDensity = 1;
|
|
336
313
|
|
|
337
314
|
$._adjustDisplay = () => {
|
|
338
315
|
if (c.style) {
|
|
@@ -341,25 +318,12 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
341
318
|
}
|
|
342
319
|
};
|
|
343
320
|
|
|
344
|
-
$.createCanvas = function (w, h,
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
p.width = c.width = c.w = w || window.innerWidth;
|
|
348
|
-
p.height = c.height = c.h = h || window.innerHeight;
|
|
349
|
-
c.hw = w / 2;
|
|
350
|
-
c.hh = h / 2;
|
|
351
|
-
c.renderer = '2d';
|
|
321
|
+
$.createCanvas = function (w, h, options) {
|
|
322
|
+
options ??= arguments[3];
|
|
323
|
+
|
|
352
324
|
let opt = Object.assign({}, Q5.canvasOptions);
|
|
353
|
-
if (options) Object.assign(opt, options);
|
|
325
|
+
if (typeof options == 'object') Object.assign(opt, options);
|
|
354
326
|
|
|
355
|
-
p.ctx = p.drawingContext = c.getContext('2d', opt);
|
|
356
|
-
Object.assign(c, opt);
|
|
357
|
-
if ($._colorMode == 'rgb') $.colorMode('rgb');
|
|
358
|
-
if ($._scope != 'image') {
|
|
359
|
-
$._defaultStyle();
|
|
360
|
-
$._da = 0;
|
|
361
|
-
}
|
|
362
|
-
$.ctx.save();
|
|
363
327
|
if ($._scope != 'image') {
|
|
364
328
|
let pd = $.displayDensity();
|
|
365
329
|
if ($._scope == 'graphics') pd = this._pixelDensity;
|
|
@@ -368,96 +332,226 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
368
332
|
c.visible = e[0].isIntersecting;
|
|
369
333
|
}).observe(c);
|
|
370
334
|
}
|
|
371
|
-
$.
|
|
372
|
-
}
|
|
335
|
+
$._pixelDensity = Math.ceil(pd);
|
|
336
|
+
}
|
|
373
337
|
|
|
374
|
-
|
|
375
|
-
else $._adjustDisplay();
|
|
376
|
-
return c;
|
|
377
|
-
};
|
|
378
|
-
$._createCanvas = $.createCanvas;
|
|
338
|
+
$._setCanvasSize(w, h);
|
|
379
339
|
|
|
380
|
-
|
|
340
|
+
Object.assign(c, opt);
|
|
341
|
+
let rend = $._createCanvas(c.w, c.h, opt);
|
|
381
342
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
$.ctx.lineJoin = 'miter';
|
|
387
|
-
$.ctx.textAlign = 'left';
|
|
343
|
+
if ($._hooks) {
|
|
344
|
+
for (let m of $._hooks.postCanvas) m();
|
|
345
|
+
}
|
|
346
|
+
return rend;
|
|
388
347
|
};
|
|
389
348
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
349
|
+
$._save = async (data, name, ext) => {
|
|
350
|
+
name = name || 'untitled';
|
|
351
|
+
ext = ext || 'png';
|
|
352
|
+
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
353
|
+
if (data instanceof OffscreenCanvas) {
|
|
354
|
+
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
355
|
+
data = await new Promise((resolve) => {
|
|
356
|
+
const reader = new FileReader();
|
|
357
|
+
reader.onloadend = () => resolve(reader.result);
|
|
358
|
+
reader.readAsDataURL(blob);
|
|
359
|
+
});
|
|
360
|
+
} else {
|
|
361
|
+
data = data.toDataURL('image/' + ext);
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
let type = 'text/plain';
|
|
365
|
+
if (ext == 'json') {
|
|
366
|
+
if (typeof data != 'string') data = JSON.stringify(data);
|
|
367
|
+
type = 'text/json';
|
|
368
|
+
}
|
|
369
|
+
data = new Blob([data], { type });
|
|
370
|
+
data = URL.createObjectURL(data);
|
|
394
371
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
372
|
+
let a = document.createElement('a');
|
|
373
|
+
a.href = data;
|
|
374
|
+
a.download = name + '.' + ext;
|
|
375
|
+
a.click();
|
|
376
|
+
URL.revokeObjectURL(a.href);
|
|
377
|
+
};
|
|
378
|
+
$.save = (a, b, c) => {
|
|
379
|
+
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
380
|
+
c = b;
|
|
381
|
+
b = a;
|
|
382
|
+
a = $.canvas;
|
|
383
|
+
}
|
|
384
|
+
if (c) return $._save(a, b, c);
|
|
385
|
+
if (b) {
|
|
386
|
+
b = b.split('.');
|
|
387
|
+
$._save(a, b[0], b.at(-1));
|
|
388
|
+
} else $._save(a);
|
|
389
|
+
};
|
|
398
390
|
|
|
399
|
-
|
|
391
|
+
$._setCanvasSize = (w, h) => {
|
|
400
392
|
w ??= window.innerWidth;
|
|
401
393
|
h ??= window.innerHeight;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
let o;
|
|
405
|
-
if ($.frameCount) {
|
|
406
|
-
o = new $._OffscreenCanvas(c.width, c.height);
|
|
407
|
-
o.w = c.w;
|
|
408
|
-
o.h = c.h;
|
|
409
|
-
let oCtx = o.getContext('2d');
|
|
410
|
-
oCtx.drawImage(c, 0, 0);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
c.width = Math.ceil(w * $._pixelDensity);
|
|
414
|
-
c.height = Math.ceil(h * $._pixelDensity);
|
|
415
|
-
c.w = w;
|
|
416
|
-
c.h = h;
|
|
394
|
+
c.w = w = Math.ceil(w);
|
|
395
|
+
c.h = h = Math.ceil(h);
|
|
417
396
|
c.hw = w / 2;
|
|
418
397
|
c.hh = h / 2;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if ($.frameCount) $.ctx.drawImage(o, 0, 0, o.w, o.h);
|
|
398
|
+
c.width = Math.ceil(w * $._pixelDensity);
|
|
399
|
+
c.height = Math.ceil(h * $._pixelDensity);
|
|
423
400
|
|
|
424
401
|
if (!$._da) {
|
|
425
|
-
|
|
426
|
-
|
|
402
|
+
q.width = w;
|
|
403
|
+
q.height = h;
|
|
427
404
|
} else $.flexibleCanvas($._dau);
|
|
428
405
|
|
|
429
|
-
if ($.
|
|
406
|
+
if ($.displayMode && !c.displayMode) $.displayMode();
|
|
407
|
+
else $._adjustDisplay();
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
if ($._scope == 'image') return;
|
|
411
|
+
|
|
412
|
+
if (c && $._scope != 'graphics') {
|
|
413
|
+
c.parent = (el) => {
|
|
414
|
+
if (c.parentElement) c.parentElement.removeChild(c);
|
|
415
|
+
|
|
416
|
+
if (typeof el == 'string') el = document.getElementById(el);
|
|
417
|
+
el.append(c);
|
|
418
|
+
|
|
419
|
+
function parentResized() {
|
|
420
|
+
if ($.frameCount > 1) {
|
|
421
|
+
$._shouldResize = true;
|
|
422
|
+
$._adjustDisplay();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (typeof ResizeObserver == 'function') {
|
|
426
|
+
if ($._ro) $._ro.disconnect();
|
|
427
|
+
$._ro = new ResizeObserver(parentResized);
|
|
428
|
+
$._ro.observe(el);
|
|
429
|
+
} else if (!$.frameCount) {
|
|
430
|
+
window.addEventListener('resize', parentResized);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
function addCanvas() {
|
|
435
|
+
let el = $._parent;
|
|
436
|
+
el ??= document.getElementsByTagName('main')[0];
|
|
437
|
+
if (!el) {
|
|
438
|
+
el = document.createElement('main');
|
|
439
|
+
document.body.append(el);
|
|
440
|
+
}
|
|
441
|
+
c.parent(el);
|
|
442
|
+
}
|
|
443
|
+
if (document.body) addCanvas();
|
|
444
|
+
else document.addEventListener('DOMContentLoaded', addCanvas);
|
|
430
445
|
}
|
|
431
446
|
|
|
432
447
|
$.resizeCanvas = (w, h) => {
|
|
448
|
+
if (!$.ctx) return $.createCanvas(w, h);
|
|
433
449
|
if (w == c.w && h == c.h) return;
|
|
434
|
-
|
|
450
|
+
|
|
451
|
+
$._resizeCanvas(w, h);
|
|
435
452
|
};
|
|
436
453
|
|
|
437
|
-
$.
|
|
454
|
+
$.canvas.resize = $.resizeCanvas;
|
|
455
|
+
$.canvas.save = $.saveCanvas = $.save;
|
|
456
|
+
|
|
438
457
|
$.displayDensity = () => window.devicePixelRatio;
|
|
439
458
|
$.pixelDensity = (v) => {
|
|
440
459
|
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
441
460
|
$._pixelDensity = v;
|
|
442
|
-
|
|
461
|
+
$._setCanvasSize(c.w, c.h);
|
|
443
462
|
return v;
|
|
444
463
|
};
|
|
445
464
|
|
|
446
|
-
if ($._scope == 'image') return;
|
|
447
|
-
|
|
448
|
-
$.fullscreen = (v) => {
|
|
449
|
-
if (v === undefined) return document.fullscreenElement;
|
|
450
|
-
if (v) document.body.requestFullscreen();
|
|
451
|
-
else document.body.exitFullscreen();
|
|
452
|
-
};
|
|
453
|
-
|
|
454
465
|
$.flexibleCanvas = (unit = 400) => {
|
|
455
466
|
if (unit) {
|
|
456
467
|
$._da = c.width / (unit * $._pixelDensity);
|
|
457
|
-
|
|
458
|
-
|
|
468
|
+
q.width = $._dau = unit;
|
|
469
|
+
q.height = (c.h / c.w) * unit;
|
|
459
470
|
} else $._da = 0;
|
|
460
471
|
};
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
Q5.canvasOptions = {
|
|
475
|
+
alpha: false,
|
|
476
|
+
colorSpace: 'display-p3'
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
|
|
480
|
+
Q5.canvasOptions.colorSpace = 'srgb';
|
|
481
|
+
} else Q5.supportsHDR = true;
|
|
482
|
+
Q5.renderers.q2d = {};
|
|
483
|
+
|
|
484
|
+
Q5.renderers.q2d.canvas = ($, q) => {
|
|
485
|
+
let c = $.canvas;
|
|
486
|
+
|
|
487
|
+
if ($.colorMode) $.colorMode('rgb', 'integer');
|
|
488
|
+
|
|
489
|
+
$._createCanvas = function (w, h, options) {
|
|
490
|
+
q.ctx = q.drawingContext = c.getContext('2d', options);
|
|
491
|
+
|
|
492
|
+
if ($._scope != 'image') {
|
|
493
|
+
// default styles
|
|
494
|
+
$.ctx.fillStyle = 'white';
|
|
495
|
+
$.ctx.strokeStyle = 'black';
|
|
496
|
+
$.ctx.lineCap = 'round';
|
|
497
|
+
$.ctx.lineJoin = 'miter';
|
|
498
|
+
$.ctx.textAlign = 'left';
|
|
499
|
+
}
|
|
500
|
+
$.ctx.scale($._pixelDensity, $._pixelDensity);
|
|
501
|
+
$.ctx.save();
|
|
502
|
+
return c;
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
if ($._scope == 'image') return;
|
|
506
|
+
|
|
507
|
+
$._resizeCanvas = (w, h) => {
|
|
508
|
+
let t = {};
|
|
509
|
+
for (let prop in $.ctx) {
|
|
510
|
+
if (typeof $.ctx[prop] != 'function') t[prop] = $.ctx[prop];
|
|
511
|
+
}
|
|
512
|
+
delete t.canvas;
|
|
513
|
+
|
|
514
|
+
let o = new $._OffscreenCanvas(c.width, c.height);
|
|
515
|
+
o.w = c.w;
|
|
516
|
+
o.h = c.h;
|
|
517
|
+
let oCtx = o.getContext('2d');
|
|
518
|
+
oCtx.drawImage(c, 0, 0);
|
|
519
|
+
|
|
520
|
+
$._setCanvasSize(w, h);
|
|
521
|
+
|
|
522
|
+
for (let prop in t) $.ctx[prop] = t[prop];
|
|
523
|
+
$.scale($._pixelDensity);
|
|
524
|
+
$.ctx.drawImage(o, 0, 0, o.w, o.h);
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
$.fill = function (c) {
|
|
528
|
+
$._doFill = true;
|
|
529
|
+
$._fillSet = true;
|
|
530
|
+
if (Q5.Color) {
|
|
531
|
+
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
532
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
533
|
+
if (c.a <= 0) return ($._doFill = false);
|
|
534
|
+
}
|
|
535
|
+
$.ctx.fillStyle = c.toString();
|
|
536
|
+
};
|
|
537
|
+
$.noFill = () => ($._doFill = false);
|
|
538
|
+
$.stroke = function (c) {
|
|
539
|
+
$._doStroke = true;
|
|
540
|
+
$._strokeSet = true;
|
|
541
|
+
if (Q5.Color) {
|
|
542
|
+
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
543
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
544
|
+
if (c.a <= 0) return ($._doStroke = false);
|
|
545
|
+
}
|
|
546
|
+
$.ctx.strokeStyle = c.toString();
|
|
547
|
+
};
|
|
548
|
+
$.strokeWeight = (n) => {
|
|
549
|
+
if (!n) $._doStroke = false;
|
|
550
|
+
if ($._da) n *= $._da;
|
|
551
|
+
$.ctx.lineWidth = n || 0.0001;
|
|
552
|
+
};
|
|
553
|
+
$.noStroke = () => ($._doStroke = false);
|
|
554
|
+
$.clear = () => $.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
461
555
|
|
|
462
556
|
// DRAWING MATRIX
|
|
463
557
|
|
|
@@ -482,7 +576,7 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
482
576
|
$.shearY = (ang) => $.ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
|
|
483
577
|
$.resetMatrix = () => {
|
|
484
578
|
$.ctx.resetTransform();
|
|
485
|
-
$.
|
|
579
|
+
$.scale($._pixelDensity);
|
|
486
580
|
};
|
|
487
581
|
|
|
488
582
|
$._styleNames = [
|
|
@@ -536,29 +630,20 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
536
630
|
opt ??= {};
|
|
537
631
|
opt.alpha ??= true;
|
|
538
632
|
opt.colorSpace ??= $.canvas.colorSpace;
|
|
539
|
-
g.
|
|
633
|
+
g.createCanvas.call($, w, h, opt);
|
|
540
634
|
return g;
|
|
541
635
|
};
|
|
542
636
|
|
|
543
637
|
if (window && $._scope != 'graphics') {
|
|
544
638
|
window.addEventListener('resize', () => {
|
|
545
639
|
$._shouldResize = true;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
640
|
+
q.windowWidth = window.innerWidth;
|
|
641
|
+
q.windowHeight = window.innerHeight;
|
|
642
|
+
q.deviceOrientation = window.screen?.orientation?.type;
|
|
549
643
|
});
|
|
550
644
|
}
|
|
551
645
|
};
|
|
552
|
-
|
|
553
|
-
Q5.canvasOptions = {
|
|
554
|
-
alpha: false,
|
|
555
|
-
colorSpace: 'display-p3'
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
|
|
559
|
-
Q5.canvasOptions.colorSpace = 'srgb';
|
|
560
|
-
} else Q5.supportsHDR = true;
|
|
561
|
-
Q5.modules.q2d_drawing = ($) => {
|
|
646
|
+
Q5.renderers.q2d.drawing = ($) => {
|
|
562
647
|
$.CHORD = 0;
|
|
563
648
|
$.PIE = 1;
|
|
564
649
|
$.OPEN = 2;
|
|
@@ -620,33 +705,6 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
620
705
|
|
|
621
706
|
// DRAWING SETTINGS
|
|
622
707
|
|
|
623
|
-
$.strokeWeight = (n) => {
|
|
624
|
-
if (!n) $._doStroke = false;
|
|
625
|
-
if ($._da) n *= $._da;
|
|
626
|
-
$.ctx.lineWidth = n || 0.0001;
|
|
627
|
-
};
|
|
628
|
-
$.stroke = function (c) {
|
|
629
|
-
$._doStroke = true;
|
|
630
|
-
$._strokeSet = true;
|
|
631
|
-
if (Q5.Color) {
|
|
632
|
-
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
633
|
-
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
634
|
-
if (c.a <= 0) return ($._doStroke = false);
|
|
635
|
-
}
|
|
636
|
-
$.ctx.strokeStyle = c.toString();
|
|
637
|
-
};
|
|
638
|
-
$.noStroke = () => ($._doStroke = false);
|
|
639
|
-
$.fill = function (c) {
|
|
640
|
-
$._doFill = true;
|
|
641
|
-
$._fillSet = true;
|
|
642
|
-
if (Q5.Color) {
|
|
643
|
-
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
644
|
-
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
645
|
-
if (c.a <= 0) return ($._doFill = false);
|
|
646
|
-
}
|
|
647
|
-
$.ctx.fillStyle = c.toString();
|
|
648
|
-
};
|
|
649
|
-
$.noFill = () => ($._doFill = false);
|
|
650
708
|
$.blendMode = (x) => ($.ctx.globalCompositeOperation = x);
|
|
651
709
|
$.strokeCap = (x) => ($.ctx.lineCap = x);
|
|
652
710
|
$.strokeJoin = (x) => ($.ctx.lineJoin = x);
|
|
@@ -658,10 +716,6 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
658
716
|
|
|
659
717
|
// DRAWING
|
|
660
718
|
|
|
661
|
-
$.clear = () => {
|
|
662
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
663
|
-
};
|
|
664
|
-
|
|
665
719
|
$.background = function (c) {
|
|
666
720
|
if (c.canvas) return $.image(c, 0, 0, $.width, $.height);
|
|
667
721
|
$.ctx.save();
|
|
@@ -819,18 +873,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
819
873
|
bl *= $._da;
|
|
820
874
|
br *= $._da;
|
|
821
875
|
}
|
|
822
|
-
|
|
823
|
-
tl = Math.min(hh, tl);
|
|
824
|
-
tr = Math.min(hh, tr);
|
|
825
|
-
bl = Math.min(hh, bl);
|
|
826
|
-
br = Math.min(hh, br);
|
|
827
|
-
$.ctx.beginPath();
|
|
828
|
-
$.ctx.moveTo(x + tl, y);
|
|
829
|
-
$.ctx.arcTo(x + w, y, x + w, y + h, tr);
|
|
830
|
-
$.ctx.arcTo(x + w, y + h, x, y + h, br);
|
|
831
|
-
$.ctx.arcTo(x, y + h, x, y, bl);
|
|
832
|
-
$.ctx.arcTo(x, y, x + w, y, tl);
|
|
833
|
-
$.ctx.closePath();
|
|
876
|
+
$.ctx.roundRect(x, y, w, h, [tl, tr, br, bl]);
|
|
834
877
|
ink();
|
|
835
878
|
}
|
|
836
879
|
|
|
@@ -849,22 +892,18 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
849
892
|
return $.rect(x, y, s, s, tl, tr, br, bl);
|
|
850
893
|
};
|
|
851
894
|
|
|
852
|
-
function clearBuff() {
|
|
853
|
-
curveBuff = [];
|
|
854
|
-
}
|
|
855
|
-
|
|
856
895
|
$.beginShape = () => {
|
|
857
|
-
|
|
896
|
+
curveBuff = [];
|
|
858
897
|
$.ctx.beginPath();
|
|
859
898
|
firstVertex = true;
|
|
860
899
|
};
|
|
861
900
|
$.beginContour = () => {
|
|
862
901
|
$.ctx.closePath();
|
|
863
|
-
|
|
902
|
+
curveBuff = [];
|
|
864
903
|
firstVertex = true;
|
|
865
904
|
};
|
|
866
905
|
$.endContour = () => {
|
|
867
|
-
|
|
906
|
+
curveBuff = [];
|
|
868
907
|
firstVertex = true;
|
|
869
908
|
};
|
|
870
909
|
$.vertex = (x, y) => {
|
|
@@ -872,7 +911,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
872
911
|
x *= $._da;
|
|
873
912
|
y *= $._da;
|
|
874
913
|
}
|
|
875
|
-
|
|
914
|
+
curveBuff = [];
|
|
876
915
|
if (firstVertex) {
|
|
877
916
|
$.ctx.moveTo(x, y);
|
|
878
917
|
} else {
|
|
@@ -889,7 +928,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
889
928
|
x *= $._da;
|
|
890
929
|
y *= $._da;
|
|
891
930
|
}
|
|
892
|
-
|
|
931
|
+
curveBuff = [];
|
|
893
932
|
$.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
894
933
|
};
|
|
895
934
|
$.quadraticVertex = (cp1x, cp1y, x, y) => {
|
|
@@ -899,7 +938,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
899
938
|
x *= $._da;
|
|
900
939
|
y *= $._da;
|
|
901
940
|
}
|
|
902
|
-
|
|
941
|
+
curveBuff = [];
|
|
903
942
|
$.ctx.quadraticCurveTo(cp1x, cp1y, x, y);
|
|
904
943
|
};
|
|
905
944
|
$.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
@@ -924,7 +963,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
924
963
|
$.endShape($.CLOSE);
|
|
925
964
|
};
|
|
926
965
|
$.endShape = (close) => {
|
|
927
|
-
|
|
966
|
+
curveBuff = [];
|
|
928
967
|
if (close) $.ctx.closePath();
|
|
929
968
|
ink();
|
|
930
969
|
};
|
|
@@ -1076,7 +1115,32 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
1076
1115
|
return $.ctx.isPointInStroke(x * pd, y * pd);
|
|
1077
1116
|
};
|
|
1078
1117
|
};
|
|
1079
|
-
Q5.
|
|
1118
|
+
Q5.renderers.q2d.image = ($, q) => {
|
|
1119
|
+
class Q5Image {
|
|
1120
|
+
constructor(w, h, opt) {
|
|
1121
|
+
let $ = this;
|
|
1122
|
+
$._scope = 'image';
|
|
1123
|
+
$.canvas = $.ctx = $.drawingContext = null;
|
|
1124
|
+
$.pixels = [];
|
|
1125
|
+
Q5.modules.canvas($, $);
|
|
1126
|
+
let r = Q5.renderers.q2d;
|
|
1127
|
+
for (let m of ['canvas', 'image', 'soft_filters']) {
|
|
1128
|
+
if (r[m]) r[m]($, $);
|
|
1129
|
+
}
|
|
1130
|
+
$.createCanvas(w, h, opt);
|
|
1131
|
+
delete $.createCanvas;
|
|
1132
|
+
$._loop = false;
|
|
1133
|
+
}
|
|
1134
|
+
get w() {
|
|
1135
|
+
return this.width;
|
|
1136
|
+
}
|
|
1137
|
+
get h() {
|
|
1138
|
+
return this.height;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
Q5.Image ??= Q5Image;
|
|
1143
|
+
|
|
1080
1144
|
$.createImage = (w, h, opt) => {
|
|
1081
1145
|
opt ??= {};
|
|
1082
1146
|
opt.alpha ??= true;
|
|
@@ -1084,115 +1148,145 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1084
1148
|
return new Q5.Image(w, h, opt);
|
|
1085
1149
|
};
|
|
1086
1150
|
|
|
1087
|
-
$.
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1151
|
+
$.loadImage = function (url, cb, opt) {
|
|
1152
|
+
if (url.canvas) return url;
|
|
1153
|
+
if (url.slice(-3).toLowerCase() == 'gif') {
|
|
1154
|
+
throw new Error(`q5 doesn't support GIFs due to their impact on performance. Use a video or animation instead.`);
|
|
1155
|
+
}
|
|
1156
|
+
q._preloadCount++;
|
|
1157
|
+
let last = [...arguments].at(-1);
|
|
1158
|
+
opt = typeof last == 'object' ? last : null;
|
|
1159
|
+
|
|
1160
|
+
let g = $.createImage(1, 1, opt);
|
|
1161
|
+
|
|
1162
|
+
function loaded(img) {
|
|
1163
|
+
g.resize(img.naturalWidth || img.width, img.naturalHeight || img.height);
|
|
1164
|
+
g.ctx.drawImage(img, 0, 0);
|
|
1165
|
+
q._preloadCount--;
|
|
1166
|
+
if (cb) cb(g);
|
|
1099
1167
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1168
|
+
|
|
1169
|
+
if (Q5._nodejs && global.CairoCanvas) {
|
|
1170
|
+
global.CairoCanvas.loadImage(url)
|
|
1171
|
+
.then(loaded)
|
|
1172
|
+
.catch((e) => {
|
|
1173
|
+
q._preloadCount--;
|
|
1174
|
+
throw e;
|
|
1175
|
+
});
|
|
1176
|
+
} else {
|
|
1177
|
+
let img = new window.Image();
|
|
1178
|
+
img.src = url;
|
|
1179
|
+
img.crossOrigin = 'Anonymous';
|
|
1180
|
+
img._pixelDensity = 1;
|
|
1181
|
+
img.onload = () => loaded(img);
|
|
1182
|
+
img.onerror = (e) => {
|
|
1183
|
+
q._preloadCount--;
|
|
1184
|
+
throw e;
|
|
1185
|
+
};
|
|
1103
1186
|
}
|
|
1104
|
-
|
|
1187
|
+
return g;
|
|
1188
|
+
};
|
|
1105
1189
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
if (
|
|
1110
|
-
|
|
1111
|
-
colorSpace: $.canvas.colorSpace
|
|
1112
|
-
});
|
|
1190
|
+
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1191
|
+
$.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
1192
|
+
let drawable = img.canvas || img;
|
|
1193
|
+
if (Q5._createNodeJSCanvas) {
|
|
1194
|
+
drawable = drawable.context.canvas;
|
|
1113
1195
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1196
|
+
dw ??= img.width || img.videoWidth;
|
|
1197
|
+
dh ??= img.height || img.videoHeight;
|
|
1198
|
+
if ($._imageMode == 'center') {
|
|
1199
|
+
dx -= dw * 0.5;
|
|
1200
|
+
dy -= dh * 0.5;
|
|
1117
1201
|
}
|
|
1118
|
-
|
|
1202
|
+
if ($._da) {
|
|
1203
|
+
dx *= $._da;
|
|
1204
|
+
dy *= $._da;
|
|
1205
|
+
dw *= $._da;
|
|
1206
|
+
dh *= $._da;
|
|
1207
|
+
sx *= $._da;
|
|
1208
|
+
sy *= $._da;
|
|
1209
|
+
sw *= $._da;
|
|
1210
|
+
sh *= $._da;
|
|
1211
|
+
}
|
|
1212
|
+
let pd = img._pixelDensity || 1;
|
|
1213
|
+
if (!sw) {
|
|
1214
|
+
sw = drawable.width || drawable.videoWidth;
|
|
1215
|
+
} else sw *= pd;
|
|
1216
|
+
if (!sh) {
|
|
1217
|
+
sh = drawable.height || drawable.videoHeight;
|
|
1218
|
+
} else sh *= pd;
|
|
1219
|
+
$.ctx.drawImage(drawable, sx * pd, sy * pd, sw, sh, dx, dy, dw, dh);
|
|
1220
|
+
|
|
1221
|
+
if ($._tint) {
|
|
1222
|
+
$.ctx.globalCompositeOperation = 'multiply';
|
|
1223
|
+
$.ctx.fillStyle = $._tint.toString();
|
|
1224
|
+
$.ctx.fillRect(dx, dy, dw, dh);
|
|
1225
|
+
$.ctx.globalCompositeOperation = 'source-over';
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
$._tint = null;
|
|
1230
|
+
let imgData = null;
|
|
1119
1231
|
|
|
1120
1232
|
$._softFilter = () => {
|
|
1121
|
-
throw 'Load q5-2d-soft-filters.js to use software filters.';
|
|
1233
|
+
throw new Error('Load q5-2d-soft-filters.js to use software filters.');
|
|
1122
1234
|
};
|
|
1123
1235
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
tmpCtx.filter = filt;
|
|
1127
|
-
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1128
|
-
$.ctx.save();
|
|
1129
|
-
$.ctx.resetTransform();
|
|
1130
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1131
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1132
|
-
$.ctx.restore();
|
|
1133
|
-
}
|
|
1236
|
+
$.filter = (type, x) => {
|
|
1237
|
+
if (!$.ctx.filter) return $._softFilter(type, x);
|
|
1134
1238
|
|
|
1135
|
-
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1140
|
-
|
|
1239
|
+
if (typeof type == 'string') f = type;
|
|
1240
|
+
else if (type == Q5.GRAY) f = `saturate(0%)`;
|
|
1241
|
+
else if (type == Q5.INVERT) f = `invert(100%)`;
|
|
1242
|
+
else if (type == Q5.BLUR) {
|
|
1243
|
+
let r = Math.ceil(x * $._pixelDensity) || 1;
|
|
1244
|
+
f = `blur(${r}px)`;
|
|
1245
|
+
} else if (type == Q5.THRESHOLD) {
|
|
1141
1246
|
x ??= 0.5;
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
} else if (typ == Q5.GRAY) {
|
|
1146
|
-
nativeFilter(`saturate(0%)`);
|
|
1147
|
-
} else if (typ == Q5.OPAQUE) {
|
|
1148
|
-
tmpCtx.fillStyle = 'black';
|
|
1149
|
-
tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1150
|
-
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1151
|
-
$.ctx.save();
|
|
1152
|
-
$.ctx.resetTransform();
|
|
1153
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1154
|
-
$.ctx.restore();
|
|
1155
|
-
} else if (typ == Q5.INVERT) {
|
|
1156
|
-
nativeFilter(`invert(100%)`);
|
|
1157
|
-
} else if (typ == Q5.BLUR) {
|
|
1158
|
-
nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`);
|
|
1159
|
-
} else {
|
|
1160
|
-
$._softFilter(typ, x);
|
|
1161
|
-
}
|
|
1162
|
-
};
|
|
1247
|
+
let b = Math.floor((0.5 / Math.max(x, 0.00001)) * 100);
|
|
1248
|
+
f = `saturate(0%) brightness(${b}%) contrast(1000000%)`;
|
|
1249
|
+
} else return $._softFilter(type, x);
|
|
1163
1250
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
p.width = w;
|
|
1168
|
-
p.height = h;
|
|
1169
|
-
$.canvas.width = w * $._pixelDensity;
|
|
1170
|
-
$.canvas.height = h * $._pixelDensity;
|
|
1171
|
-
$.ctx.save();
|
|
1172
|
-
$.ctx.resetTransform();
|
|
1173
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1174
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0, $.canvas.width, $.canvas.height);
|
|
1175
|
-
$.ctx.restore();
|
|
1251
|
+
$.ctx.filter = f;
|
|
1252
|
+
$.ctx.drawImage($.canvas, 0, 0, $.canvas.w, $.canvas.h);
|
|
1253
|
+
$.ctx.filter = 'none';
|
|
1176
1254
|
};
|
|
1177
1255
|
|
|
1256
|
+
if ($._scope == 'image') {
|
|
1257
|
+
$.resize = (w, h) => {
|
|
1258
|
+
let o = new $._OffscreenCanvas($.canvas.width, $.canvas.height);
|
|
1259
|
+
let tmpCtx = o.getContext('2d', {
|
|
1260
|
+
colorSpace: $.canvas.colorSpace
|
|
1261
|
+
});
|
|
1262
|
+
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1263
|
+
$._setCanvasSize(w, h);
|
|
1264
|
+
|
|
1265
|
+
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1266
|
+
$.ctx.drawImage(o, 0, 0, $.canvas.width, $.canvas.height);
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1178
1270
|
$.trim = () => {
|
|
1179
1271
|
let pd = $._pixelDensity || 1;
|
|
1180
|
-
let
|
|
1181
|
-
let
|
|
1182
|
-
let
|
|
1272
|
+
let w = $.canvas.width;
|
|
1273
|
+
let h = $.canvas.height;
|
|
1274
|
+
let data = $.ctx.getImageData(0, 0, w, h).data;
|
|
1275
|
+
let left = w,
|
|
1183
1276
|
right = 0,
|
|
1184
|
-
top =
|
|
1277
|
+
top = h,
|
|
1185
1278
|
bottom = 0;
|
|
1186
1279
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if (data[
|
|
1280
|
+
let i = 3;
|
|
1281
|
+
for (let y = 0; y < h; y++) {
|
|
1282
|
+
for (let x = 0; x < w; x++) {
|
|
1283
|
+
if (data[i] !== 0) {
|
|
1191
1284
|
if (x < left) left = x;
|
|
1192
1285
|
if (x > right) right = x;
|
|
1193
1286
|
if (y < top) top = y;
|
|
1194
1287
|
if (y > bottom) bottom = y;
|
|
1195
1288
|
}
|
|
1289
|
+
i += 4;
|
|
1196
1290
|
}
|
|
1197
1291
|
}
|
|
1198
1292
|
top = Math.floor(top / pd);
|
|
@@ -1213,48 +1307,6 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1213
1307
|
$.ctx.restore();
|
|
1214
1308
|
};
|
|
1215
1309
|
|
|
1216
|
-
$._save = async (data, name, ext) => {
|
|
1217
|
-
name = name || 'untitled';
|
|
1218
|
-
ext = ext || 'png';
|
|
1219
|
-
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
1220
|
-
if (data instanceof OffscreenCanvas) {
|
|
1221
|
-
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
1222
|
-
data = await new Promise((resolve) => {
|
|
1223
|
-
const reader = new FileReader();
|
|
1224
|
-
reader.onloadend = () => resolve(reader.result);
|
|
1225
|
-
reader.readAsDataURL(blob);
|
|
1226
|
-
});
|
|
1227
|
-
} else {
|
|
1228
|
-
data = data.toDataURL('image/' + ext);
|
|
1229
|
-
}
|
|
1230
|
-
} else {
|
|
1231
|
-
let type = 'text/plain';
|
|
1232
|
-
if (ext == 'json') {
|
|
1233
|
-
if (typeof data != 'string') data = JSON.stringify(data);
|
|
1234
|
-
type = 'text/json';
|
|
1235
|
-
}
|
|
1236
|
-
data = new Blob([data], { type });
|
|
1237
|
-
data = URL.createObjectURL(data);
|
|
1238
|
-
}
|
|
1239
|
-
let a = document.createElement('a');
|
|
1240
|
-
a.href = data;
|
|
1241
|
-
a.download = name + '.' + ext;
|
|
1242
|
-
a.click();
|
|
1243
|
-
URL.revokeObjectURL(a.href);
|
|
1244
|
-
};
|
|
1245
|
-
$.save = (a, b, c) => {
|
|
1246
|
-
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
1247
|
-
c = b;
|
|
1248
|
-
b = a;
|
|
1249
|
-
a = $.canvas;
|
|
1250
|
-
}
|
|
1251
|
-
if (c) return $._save(a, b, c);
|
|
1252
|
-
if (b) {
|
|
1253
|
-
b = b.split('.');
|
|
1254
|
-
$._save(a, b[0], b.at(-1));
|
|
1255
|
-
} else $._save(a);
|
|
1256
|
-
};
|
|
1257
|
-
|
|
1258
1310
|
$.get = (x, y, w, h) => {
|
|
1259
1311
|
let pd = $._pixelDensity || 1;
|
|
1260
1312
|
if (x !== undefined && w === undefined) {
|
|
@@ -1299,174 +1351,23 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1299
1351
|
|
|
1300
1352
|
$.loadPixels = () => {
|
|
1301
1353
|
imgData = $.ctx.getImageData(0, 0, $.canvas.width, $.canvas.height);
|
|
1302
|
-
|
|
1354
|
+
q.pixels = imgData.data;
|
|
1303
1355
|
};
|
|
1304
1356
|
$.updatePixels = () => {
|
|
1305
1357
|
if (imgData != null) $.ctx.putImageData(imgData, 0, 0);
|
|
1306
1358
|
};
|
|
1307
1359
|
|
|
1308
|
-
$._tinted = function (col) {
|
|
1309
|
-
let alpha = col.a;
|
|
1310
|
-
col.a = 255;
|
|
1311
|
-
makeTmpCtx();
|
|
1312
|
-
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1313
|
-
tmpCtx.fillStyle = col.toString();
|
|
1314
|
-
tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1315
|
-
tmpCtx.globalCompositeOperation = 'multiply';
|
|
1316
|
-
tmpCtx.drawImage($.ctx.canvas, 0, 0);
|
|
1317
|
-
tmpCtx.globalCompositeOperation = 'source-over';
|
|
1318
|
-
|
|
1319
|
-
$.ctx.save();
|
|
1320
|
-
$.ctx.resetTransform();
|
|
1321
|
-
let old = $.ctx.globalCompositeOperation;
|
|
1322
|
-
$.ctx.globalCompositeOperation = 'source-in';
|
|
1323
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1324
|
-
$.ctx.globalCompositeOperation = old;
|
|
1325
|
-
$.ctx.restore();
|
|
1326
|
-
|
|
1327
|
-
tmpCtx.globalAlpha = alpha / 255;
|
|
1328
|
-
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1329
|
-
tmpCtx.drawImage($.ctx.canvas, 0, 0);
|
|
1330
|
-
tmpCtx.globalAlpha = 1;
|
|
1331
|
-
|
|
1332
|
-
$.ctx.save();
|
|
1333
|
-
$.ctx.resetTransform();
|
|
1334
|
-
$.ctx.clearRect(0, 0, $.ctx.canvas.width, $.ctx.canvas.height);
|
|
1335
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1336
|
-
$.ctx.restore();
|
|
1337
|
-
};
|
|
1338
|
-
|
|
1339
1360
|
$.smooth = () => ($.ctx.imageSmoothingEnabled = true);
|
|
1340
1361
|
$.noSmooth = () => ($.ctx.imageSmoothingEnabled = false);
|
|
1341
1362
|
|
|
1342
1363
|
if ($._scope == 'image') return;
|
|
1343
1364
|
|
|
1344
|
-
$.saveCanvas = $.canvas.save = $.save;
|
|
1345
|
-
|
|
1346
1365
|
$.tint = function (c) {
|
|
1347
1366
|
$._tint = c._q5Color ? c : $.color(...arguments);
|
|
1348
1367
|
};
|
|
1349
1368
|
$.noTint = () => ($._tint = null);
|
|
1350
|
-
|
|
1351
|
-
// IMAGING
|
|
1352
|
-
|
|
1353
|
-
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1354
|
-
$.image = (img, dx, dy, dWidth, dHeight, sx = 0, sy = 0, sWidth, sHeight) => {
|
|
1355
|
-
if ($._da) {
|
|
1356
|
-
dx *= $._da;
|
|
1357
|
-
dy *= $._da;
|
|
1358
|
-
dWidth *= $._da;
|
|
1359
|
-
dHeight *= $._da;
|
|
1360
|
-
sx *= $._da;
|
|
1361
|
-
sy *= $._da;
|
|
1362
|
-
sWidth *= $._da;
|
|
1363
|
-
sHeight *= $._da;
|
|
1364
|
-
}
|
|
1365
|
-
let drawable = img.canvas || img;
|
|
1366
|
-
if (Q5._createNodeJSCanvas) {
|
|
1367
|
-
drawable = drawable.context.canvas;
|
|
1368
|
-
}
|
|
1369
|
-
function reset() {
|
|
1370
|
-
if (!img._q5 || !$._tint) return;
|
|
1371
|
-
let c = img.ctx;
|
|
1372
|
-
c.save();
|
|
1373
|
-
c.resetTransform();
|
|
1374
|
-
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
|
|
1375
|
-
c.drawImage(tmpCt2.canvas, 0, 0);
|
|
1376
|
-
c.restore();
|
|
1377
|
-
}
|
|
1378
|
-
if (img.canvas && $._tint != null) {
|
|
1379
|
-
makeTmpCt2(img.canvas.width, img.canvas.height);
|
|
1380
|
-
tmpCt2.drawImage(img.canvas, 0, 0);
|
|
1381
|
-
img._tinted($._tint);
|
|
1382
|
-
}
|
|
1383
|
-
dWidth ??= img.width || img.videoWidth;
|
|
1384
|
-
dHeight ??= img.height || img.videoHeight;
|
|
1385
|
-
if ($._imageMode == 'center') {
|
|
1386
|
-
dx -= dWidth * 0.5;
|
|
1387
|
-
dy -= dHeight * 0.5;
|
|
1388
|
-
}
|
|
1389
|
-
let pd = img._pixelDensity || 1;
|
|
1390
|
-
if (!sWidth) {
|
|
1391
|
-
sWidth = drawable.width || drawable.videoWidth;
|
|
1392
|
-
} else sWidth *= pd;
|
|
1393
|
-
if (!sHeight) {
|
|
1394
|
-
sHeight = drawable.height || drawable.videoHeight;
|
|
1395
|
-
} else sHeight *= pd;
|
|
1396
|
-
$.ctx.drawImage(drawable, sx * pd, sy * pd, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
|
1397
|
-
reset();
|
|
1398
|
-
};
|
|
1399
|
-
|
|
1400
|
-
$.loadImage = function (url, cb, opt) {
|
|
1401
|
-
if (url.canvas) return url;
|
|
1402
|
-
if (url.slice(-3).toLowerCase() == 'gif') {
|
|
1403
|
-
throw new Error(`q5 doesn't support GIFs due to their impact on performance. Use a video or animation instead.`);
|
|
1404
|
-
}
|
|
1405
|
-
p._preloadCount++;
|
|
1406
|
-
let last = [...arguments].at(-1);
|
|
1407
|
-
opt = typeof last == 'object' ? last : null;
|
|
1408
|
-
|
|
1409
|
-
let g = $.createImage(1, 1, opt);
|
|
1410
|
-
|
|
1411
|
-
function loaded(img) {
|
|
1412
|
-
let c = g.ctx;
|
|
1413
|
-
g.width = c.canvas.width = img.naturalWidth || img.width;
|
|
1414
|
-
g.height = c.canvas.height = img.naturalHeight || img.height;
|
|
1415
|
-
c.drawImage(img, 0, 0);
|
|
1416
|
-
p._preloadCount--;
|
|
1417
|
-
if (cb) cb(g);
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
if (Q5._nodejs && global.CairoCanvas) {
|
|
1421
|
-
global.CairoCanvas.loadImage(url)
|
|
1422
|
-
.then(loaded)
|
|
1423
|
-
.catch((e) => {
|
|
1424
|
-
p._preloadCount--;
|
|
1425
|
-
throw e;
|
|
1426
|
-
});
|
|
1427
|
-
} else {
|
|
1428
|
-
let img = new window.Image();
|
|
1429
|
-
img.src = url;
|
|
1430
|
-
img.crossOrigin = 'Anonymous';
|
|
1431
|
-
img._pixelDensity = 1;
|
|
1432
|
-
img.onload = () => loaded(img);
|
|
1433
|
-
img.onerror = (e) => {
|
|
1434
|
-
p._preloadCount--;
|
|
1435
|
-
throw e;
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
|
-
return g;
|
|
1439
|
-
};
|
|
1440
1369
|
};
|
|
1441
1370
|
|
|
1442
|
-
// IMAGE CLASS
|
|
1443
|
-
|
|
1444
|
-
Q5.imageModules = ['q2d_canvas', 'q2d_image'];
|
|
1445
|
-
|
|
1446
|
-
class _Q5Image {
|
|
1447
|
-
constructor(w, h, opt) {
|
|
1448
|
-
let $ = this;
|
|
1449
|
-
$._scope = 'image';
|
|
1450
|
-
$.canvas = $.ctx = $.drawingContext = null;
|
|
1451
|
-
$.pixels = [];
|
|
1452
|
-
for (let m of Q5.imageModules) {
|
|
1453
|
-
Q5.modules[m]($, $);
|
|
1454
|
-
}
|
|
1455
|
-
delete this.createCanvas;
|
|
1456
|
-
|
|
1457
|
-
this._createCanvas(w, h, '2d', opt);
|
|
1458
|
-
this._loop = false;
|
|
1459
|
-
}
|
|
1460
|
-
get w() {
|
|
1461
|
-
return this.width;
|
|
1462
|
-
}
|
|
1463
|
-
get h() {
|
|
1464
|
-
return this.height;
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
Q5.Image ??= _Q5Image;
|
|
1469
|
-
|
|
1470
1371
|
Q5.THRESHOLD = 1;
|
|
1471
1372
|
Q5.GRAY = 2;
|
|
1472
1373
|
Q5.OPAQUE = 3;
|
|
@@ -1476,12 +1377,12 @@ Q5.DILATE = 6;
|
|
|
1476
1377
|
Q5.ERODE = 7;
|
|
1477
1378
|
Q5.BLUR = 8;
|
|
1478
1379
|
/* software implementation of image filters */
|
|
1479
|
-
Q5.
|
|
1380
|
+
Q5.renderers.q2d.soft_filters = ($) => {
|
|
1480
1381
|
let tmpBuf = null;
|
|
1481
1382
|
|
|
1482
|
-
function
|
|
1383
|
+
function ensureTmpBuf() {
|
|
1483
1384
|
let l = $.canvas.width * $.canvas.height * 4;
|
|
1484
|
-
if (!tmpBuf ||
|
|
1385
|
+
if (!tmpBuf || tmpBuf.length != l) {
|
|
1485
1386
|
tmpBuf = new Uint8ClampedArray(l);
|
|
1486
1387
|
}
|
|
1487
1388
|
}
|
|
@@ -1523,7 +1424,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1523
1424
|
}
|
|
1524
1425
|
};
|
|
1525
1426
|
$._filters[Q5.DILATE] = (data) => {
|
|
1526
|
-
|
|
1427
|
+
ensureTmpBuf();
|
|
1527
1428
|
tmpBuf.set(data);
|
|
1528
1429
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1529
1430
|
for (let i = 0; i < h; i++) {
|
|
@@ -1550,7 +1451,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1550
1451
|
}
|
|
1551
1452
|
};
|
|
1552
1453
|
$._filters[Q5.ERODE] = (data) => {
|
|
1553
|
-
|
|
1454
|
+
ensureTmpBuf();
|
|
1554
1455
|
tmpBuf.set(data);
|
|
1555
1456
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1556
1457
|
for (let i = 0; i < h; i++) {
|
|
@@ -1579,7 +1480,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1579
1480
|
$._filters[Q5.BLUR] = (data, rad) => {
|
|
1580
1481
|
rad = rad || 1;
|
|
1581
1482
|
rad = Math.floor(rad * $._pixelDensity);
|
|
1582
|
-
|
|
1483
|
+
ensureTmpBuf();
|
|
1583
1484
|
tmpBuf.set(data);
|
|
1584
1485
|
|
|
1585
1486
|
let ksize = rad * 2 + 1;
|
|
@@ -1651,7 +1552,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1651
1552
|
$.ctx.putImageData(imgData, 0, 0);
|
|
1652
1553
|
};
|
|
1653
1554
|
};
|
|
1654
|
-
Q5.
|
|
1555
|
+
Q5.renderers.q2d.text = ($, q) => {
|
|
1655
1556
|
$.NORMAL = 'normal';
|
|
1656
1557
|
$.ITALIC = 'italic';
|
|
1657
1558
|
$.BOLD = 'bold';
|
|
@@ -1671,12 +1572,12 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1671
1572
|
$._textStyle = 'normal';
|
|
1672
1573
|
|
|
1673
1574
|
$.loadFont = (url, cb) => {
|
|
1674
|
-
|
|
1575
|
+
q._preloadCount++;
|
|
1675
1576
|
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
1676
1577
|
let f = new FontFace(name, `url(${url})`);
|
|
1677
1578
|
document.fonts.add(f);
|
|
1678
1579
|
f.load().then(() => {
|
|
1679
|
-
|
|
1580
|
+
q._preloadCount--;
|
|
1680
1581
|
if (cb) cb(name);
|
|
1681
1582
|
});
|
|
1682
1583
|
return name;
|
|
@@ -1865,8 +1766,8 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1865
1766
|
};
|
|
1866
1767
|
};
|
|
1867
1768
|
Q5.modules.ai = ($) => {
|
|
1868
|
-
$.askAI = (
|
|
1869
|
-
throw Error('Ask AI ✨ ' +
|
|
1769
|
+
$.askAI = (question = '') => {
|
|
1770
|
+
throw Error('Ask AI ✨ ' + question);
|
|
1870
1771
|
};
|
|
1871
1772
|
|
|
1872
1773
|
$._aiErrorAssistance = async (e) => {
|
|
@@ -1929,22 +1830,24 @@ Q5.modules.ai = ($) => {
|
|
|
1929
1830
|
} catch (err) {}
|
|
1930
1831
|
};
|
|
1931
1832
|
};
|
|
1932
|
-
Q5.modules.color = ($,
|
|
1833
|
+
Q5.modules.color = ($, q) => {
|
|
1933
1834
|
$.RGB = $.RGBA = $._colorMode = 'rgb';
|
|
1934
1835
|
$.OKLCH = 'oklch';
|
|
1935
1836
|
|
|
1936
|
-
|
|
1937
|
-
else $.Color = Q5.ColorRGBA;
|
|
1938
|
-
|
|
1939
|
-
$.colorMode = (mode) => {
|
|
1837
|
+
$.colorMode = (mode, format) => {
|
|
1940
1838
|
$._colorMode = mode;
|
|
1839
|
+
let srgb = $.canvas.colorSpace == 'srgb' || mode == 'srgb';
|
|
1840
|
+
format ??= srgb ? 'integer' : 'float';
|
|
1841
|
+
$._colorFormat = format == 'float' || format == 1 ? 1 : 255;
|
|
1941
1842
|
if (mode == 'oklch') {
|
|
1942
|
-
|
|
1943
|
-
} else
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1843
|
+
q.Color = Q5.ColorOKLCH;
|
|
1844
|
+
} else {
|
|
1845
|
+
let srgb = $.canvas.colorSpace == 'srgb';
|
|
1846
|
+
if ($._colorFormat == 255) {
|
|
1847
|
+
q.Color = srgb ? Q5.ColorRGBA_8 : Q5.ColorRGBA_P3_8;
|
|
1848
|
+
} else {
|
|
1849
|
+
q.Color = srgb ? Q5.ColorRGBA : Q5.ColorRGBA_P3;
|
|
1850
|
+
}
|
|
1948
1851
|
$._colorMode = 'rgb';
|
|
1949
1852
|
}
|
|
1950
1853
|
};
|
|
@@ -1983,42 +1886,49 @@ Q5.modules.color = ($, p) => {
|
|
|
1983
1886
|
yellow: [255, 255, 0]
|
|
1984
1887
|
};
|
|
1985
1888
|
|
|
1986
|
-
$.color =
|
|
1889
|
+
$.color = (c0, c1, c2, c3) => {
|
|
1987
1890
|
let C = $.Color;
|
|
1988
1891
|
if (c0._q5Color) return new C(...c0.levels);
|
|
1989
|
-
|
|
1990
|
-
if (args.length == 1) {
|
|
1892
|
+
if (c1 == undefined) {
|
|
1991
1893
|
if (typeof c0 == 'string') {
|
|
1992
1894
|
if (c0[0] == '#') {
|
|
1993
1895
|
if (c0.length <= 5) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
);
|
|
1896
|
+
if (c0.length > 4) c3 = parseInt(c0[4] + c0[4], 16);
|
|
1897
|
+
c2 = parseInt(c0[3] + c0[3], 16);
|
|
1898
|
+
c1 = parseInt(c0[2] + c0[2], 16);
|
|
1899
|
+
c0 = parseInt(c0[1] + c0[1], 16);
|
|
1900
|
+
} else {
|
|
1901
|
+
if (c0.length > 7) c3 = parseInt(c0.slice(7, 9), 16);
|
|
1902
|
+
c2 = parseInt(c0.slice(5, 7), 16);
|
|
1903
|
+
c1 = parseInt(c0.slice(3, 5), 16);
|
|
1904
|
+
c0 = parseInt(c0.slice(1, 3), 16);
|
|
2000
1905
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
1906
|
+
} else if ($._namedColors[c0]) {
|
|
1907
|
+
c0 = $._namedColors[c0];
|
|
1908
|
+
} else {
|
|
1909
|
+
console.error(
|
|
1910
|
+
"q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
|
|
2006
1911
|
);
|
|
1912
|
+
return new C(0, 0, 0);
|
|
2007
1913
|
}
|
|
2008
|
-
if ($._namedColors[c0]) return new C(...$._namedColors[c0]);
|
|
2009
|
-
console.error(
|
|
2010
|
-
"q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
|
|
2011
|
-
);
|
|
2012
|
-
return new C(0, 0, 0);
|
|
2013
1914
|
}
|
|
2014
|
-
if (Array.isArray(c0))
|
|
1915
|
+
if (Array.isArray(c0)) {
|
|
1916
|
+
c1 = c0[1];
|
|
1917
|
+
c2 = c0[2];
|
|
1918
|
+
c3 = c0[3];
|
|
1919
|
+
c0 = c0[0];
|
|
1920
|
+
}
|
|
2015
1921
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
1922
|
+
|
|
1923
|
+
if ($._colorFormat == 1) {
|
|
1924
|
+
c0 /= 255;
|
|
1925
|
+
if (c1) c1 /= 255;
|
|
1926
|
+
if (c2) c2 /= 255;
|
|
1927
|
+
if (c3) c3 /= 255;
|
|
2019
1928
|
}
|
|
2020
|
-
|
|
2021
|
-
if (
|
|
1929
|
+
|
|
1930
|
+
if (c2 == undefined) return new C(c0, c0, c0, c1);
|
|
1931
|
+
return new C(c0, c1, c2, c3);
|
|
2022
1932
|
};
|
|
2023
1933
|
|
|
2024
1934
|
$.red = (c) => c.r;
|
|
@@ -2079,7 +1989,24 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2079
1989
|
this.r = r;
|
|
2080
1990
|
this.g = g;
|
|
2081
1991
|
this.b = b;
|
|
2082
|
-
this.a = a ??
|
|
1992
|
+
this.a = a ?? 1;
|
|
1993
|
+
}
|
|
1994
|
+
get levels() {
|
|
1995
|
+
return [this.r, this.g, this.b, this.a];
|
|
1996
|
+
}
|
|
1997
|
+
toString() {
|
|
1998
|
+
return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
1999
|
+
}
|
|
2000
|
+
};
|
|
2001
|
+
Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
|
|
2002
|
+
toString() {
|
|
2003
|
+
return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
// legacy 8-bit (0-255) integer color format
|
|
2007
|
+
Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
2008
|
+
constructor(r, g, b, a) {
|
|
2009
|
+
super(r, g, b, a ?? 255);
|
|
2083
2010
|
}
|
|
2084
2011
|
setRed(v) {
|
|
2085
2012
|
this.r = v;
|
|
@@ -2100,9 +2027,10 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2100
2027
|
return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
|
|
2101
2028
|
}
|
|
2102
2029
|
};
|
|
2103
|
-
|
|
2030
|
+
// p3 10-bit color in integer color format, for backwards compatibility
|
|
2031
|
+
Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
|
|
2104
2032
|
constructor(r, g, b, a) {
|
|
2105
|
-
super(r, g, b, a);
|
|
2033
|
+
super(r, g, b, a ?? 255);
|
|
2106
2034
|
this._edited = true;
|
|
2107
2035
|
}
|
|
2108
2036
|
get r() {
|
|
@@ -2231,8 +2159,14 @@ main {
|
|
|
2231
2159
|
Object.assign(c, { displayMode, renderQuality, displayScale });
|
|
2232
2160
|
$._adjustDisplay();
|
|
2233
2161
|
};
|
|
2162
|
+
|
|
2163
|
+
$.fullscreen = (v) => {
|
|
2164
|
+
if (v === undefined) return document.fullscreenElement;
|
|
2165
|
+
if (v) document.body.requestFullscreen();
|
|
2166
|
+
else document.body.exitFullscreen();
|
|
2167
|
+
};
|
|
2234
2168
|
};
|
|
2235
|
-
Q5.modules.input = ($,
|
|
2169
|
+
Q5.modules.input = ($, q) => {
|
|
2236
2170
|
if ($._scope == 'graphics') return;
|
|
2237
2171
|
|
|
2238
2172
|
$.mouseX = 0;
|
|
@@ -2276,17 +2210,26 @@ Q5.modules.input = ($, p) => {
|
|
|
2276
2210
|
|
|
2277
2211
|
$._updateMouse = (e) => {
|
|
2278
2212
|
if (e.changedTouches) return;
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2213
|
+
if (c) {
|
|
2214
|
+
let rect = c.getBoundingClientRect();
|
|
2215
|
+
let sx = c.scrollWidth / $.width || 1;
|
|
2216
|
+
let sy = c.scrollHeight / $.height || 1;
|
|
2217
|
+
q.mouseX = (e.clientX - rect.left) / sx;
|
|
2218
|
+
q.mouseY = (e.clientY - rect.top) / sy;
|
|
2219
|
+
if (c.renderer == 'webgpu') {
|
|
2220
|
+
q.mouseX -= c.hw;
|
|
2221
|
+
q.mouseY -= c.hh;
|
|
2222
|
+
}
|
|
2223
|
+
} else {
|
|
2224
|
+
q.mouseX = e.clientX;
|
|
2225
|
+
q.mouseY = e.clientY;
|
|
2226
|
+
}
|
|
2284
2227
|
};
|
|
2285
2228
|
$._onmousedown = (e) => {
|
|
2286
2229
|
$._startAudio();
|
|
2287
2230
|
$._updateMouse(e);
|
|
2288
|
-
|
|
2289
|
-
|
|
2231
|
+
q.mouseIsPressed = true;
|
|
2232
|
+
q.mouseButton = mouseBtns[e.button];
|
|
2290
2233
|
$.mousePressed(e);
|
|
2291
2234
|
};
|
|
2292
2235
|
$._onmousemove = (e) => {
|
|
@@ -2296,18 +2239,15 @@ Q5.modules.input = ($, p) => {
|
|
|
2296
2239
|
};
|
|
2297
2240
|
$._onmouseup = (e) => {
|
|
2298
2241
|
$._updateMouse(e);
|
|
2299
|
-
|
|
2242
|
+
q.mouseIsPressed = false;
|
|
2300
2243
|
$.mouseReleased(e);
|
|
2301
2244
|
};
|
|
2302
2245
|
$._onclick = (e) => {
|
|
2303
2246
|
$._updateMouse(e);
|
|
2304
|
-
|
|
2247
|
+
q.mouseIsPressed = true;
|
|
2305
2248
|
$.mouseClicked(e);
|
|
2306
|
-
|
|
2249
|
+
q.mouseIsPressed = false;
|
|
2307
2250
|
};
|
|
2308
|
-
c.addEventListener('mousedown', (e) => $._onmousedown(e));
|
|
2309
|
-
c.addEventListener('mouseup', (e) => $._onmouseup(e));
|
|
2310
|
-
c.addEventListener('click', (e) => $._onclick(e));
|
|
2311
2251
|
|
|
2312
2252
|
$.cursor = (name, x, y) => {
|
|
2313
2253
|
let pfx = '';
|
|
@@ -2329,17 +2269,17 @@ Q5.modules.input = ($, p) => {
|
|
|
2329
2269
|
$._onkeydown = (e) => {
|
|
2330
2270
|
if (e.repeat) return;
|
|
2331
2271
|
$._startAudio();
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2272
|
+
q.keyIsPressed = true;
|
|
2273
|
+
q.key = e.key;
|
|
2274
|
+
q.keyCode = e.keyCode;
|
|
2335
2275
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = true;
|
|
2336
2276
|
$.keyPressed(e);
|
|
2337
2277
|
if (e.key.length == 1) $.keyTyped(e);
|
|
2338
2278
|
};
|
|
2339
2279
|
$._onkeyup = (e) => {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2280
|
+
q.keyIsPressed = false;
|
|
2281
|
+
q.key = e.key;
|
|
2282
|
+
q.keyCode = e.keyCode;
|
|
2343
2283
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
|
|
2344
2284
|
$.keyReleased(e);
|
|
2345
2285
|
};
|
|
@@ -2357,37 +2297,43 @@ Q5.modules.input = ($, p) => {
|
|
|
2357
2297
|
}
|
|
2358
2298
|
$._ontouchstart = (e) => {
|
|
2359
2299
|
$._startAudio();
|
|
2360
|
-
|
|
2300
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2361
2301
|
if (!$._isTouchAware) {
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2302
|
+
q.mouseX = $.touches[0].x;
|
|
2303
|
+
q.mouseY = $.touches[0].y;
|
|
2304
|
+
q.mouseIsPressed = true;
|
|
2305
|
+
q.mouseButton = $.LEFT;
|
|
2366
2306
|
if (!$.mousePressed(e)) e.preventDefault();
|
|
2367
2307
|
}
|
|
2368
2308
|
if (!$.touchStarted(e)) e.preventDefault();
|
|
2369
2309
|
};
|
|
2370
2310
|
$._ontouchmove = (e) => {
|
|
2371
|
-
|
|
2311
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2372
2312
|
if (!$._isTouchAware) {
|
|
2373
|
-
|
|
2374
|
-
|
|
2313
|
+
q.mouseX = $.touches[0].x;
|
|
2314
|
+
q.mouseY = $.touches[0].y;
|
|
2375
2315
|
if (!$.mouseDragged(e)) e.preventDefault();
|
|
2376
2316
|
}
|
|
2377
2317
|
if (!$.touchMoved(e)) e.preventDefault();
|
|
2378
2318
|
};
|
|
2379
2319
|
$._ontouchend = (e) => {
|
|
2380
|
-
|
|
2320
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2381
2321
|
if (!$._isTouchAware && !$.touches.length) {
|
|
2382
|
-
|
|
2322
|
+
q.mouseIsPressed = false;
|
|
2383
2323
|
if (!$.mouseReleased(e)) e.preventDefault();
|
|
2384
2324
|
}
|
|
2385
2325
|
if (!$.touchEnded(e)) e.preventDefault();
|
|
2386
2326
|
};
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2327
|
+
|
|
2328
|
+
if (c) {
|
|
2329
|
+
c.addEventListener('mousedown', (e) => $._onmousedown(e));
|
|
2330
|
+
c.addEventListener('mouseup', (e) => $._onmouseup(e));
|
|
2331
|
+
c.addEventListener('click', (e) => $._onclick(e));
|
|
2332
|
+
c.addEventListener('touchstart', (e) => $._ontouchstart(e));
|
|
2333
|
+
c.addEventListener('touchmove', (e) => $._ontouchmove(e));
|
|
2334
|
+
c.addEventListener('touchcancel', (e) => $._ontouchend(e));
|
|
2335
|
+
c.addEventListener('touchend', (e) => $._ontouchend(e));
|
|
2336
|
+
}
|
|
2391
2337
|
|
|
2392
2338
|
if (window) {
|
|
2393
2339
|
let l = window.addEventListener;
|
|
@@ -2396,7 +2342,7 @@ Q5.modules.input = ($, p) => {
|
|
|
2396
2342
|
l('keyup', (e) => $._onkeyup(e), false);
|
|
2397
2343
|
}
|
|
2398
2344
|
};
|
|
2399
|
-
Q5.modules.math = ($,
|
|
2345
|
+
Q5.modules.math = ($, q) => {
|
|
2400
2346
|
$.DEGREES = 'degrees';
|
|
2401
2347
|
$.RADIANS = 'radians';
|
|
2402
2348
|
|
|
@@ -2425,7 +2371,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2425
2371
|
$.degrees = (x) => x * $._RADTODEG;
|
|
2426
2372
|
$.radians = (x) => x * $._DEGTORAD;
|
|
2427
2373
|
|
|
2428
|
-
$.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2374
|
+
$.map = Q5.prototype.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2429
2375
|
let val = ostart + (ostop - ostart) * (((value - istart) * 1.0) / (istop - istart));
|
|
2430
2376
|
if (!clamp) {
|
|
2431
2377
|
return val;
|
|
@@ -2683,7 +2629,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2683
2629
|
let _noise;
|
|
2684
2630
|
|
|
2685
2631
|
$.noiseMode = (mode) => {
|
|
2686
|
-
|
|
2632
|
+
q.Noise = Q5[mode[0].toUpperCase() + mode.slice(1) + 'Noise'];
|
|
2687
2633
|
_noise = null;
|
|
2688
2634
|
};
|
|
2689
2635
|
$.noiseSeed = (seed) => {
|
|
@@ -2816,14 +2762,15 @@ Q5.PerlinNoise = class extends Q5.Noise {
|
|
|
2816
2762
|
return (total / maxAmp + 1) / 2;
|
|
2817
2763
|
}
|
|
2818
2764
|
};
|
|
2819
|
-
Q5.modules.sound = ($,
|
|
2765
|
+
Q5.modules.sound = ($, q) => {
|
|
2820
2766
|
$.Sound = Q5.Sound;
|
|
2821
2767
|
$.loadSound = (path, cb) => {
|
|
2822
|
-
|
|
2768
|
+
q._preloadCount++;
|
|
2823
2769
|
Q5.aud ??= new window.AudioContext();
|
|
2824
2770
|
let a = new Q5.Sound(path, cb);
|
|
2825
2771
|
a.addEventListener('canplaythrough', () => {
|
|
2826
|
-
|
|
2772
|
+
q._preloadCount--;
|
|
2773
|
+
a.loaded = true;
|
|
2827
2774
|
if (cb) cb(a);
|
|
2828
2775
|
});
|
|
2829
2776
|
return a;
|
|
@@ -2855,10 +2802,16 @@ Q5.Sound = class extends Audio {
|
|
|
2855
2802
|
setPan(value) {
|
|
2856
2803
|
this.pan = value;
|
|
2857
2804
|
}
|
|
2805
|
+
isLoaded() {
|
|
2806
|
+
return this.loaded;
|
|
2807
|
+
}
|
|
2808
|
+
isPlaying() {
|
|
2809
|
+
return !this.paused;
|
|
2810
|
+
}
|
|
2858
2811
|
};
|
|
2859
|
-
Q5.modules.util = ($,
|
|
2812
|
+
Q5.modules.util = ($, q) => {
|
|
2860
2813
|
$._loadFile = (path, cb, type) => {
|
|
2861
|
-
|
|
2814
|
+
q._preloadCount++;
|
|
2862
2815
|
let ret = {};
|
|
2863
2816
|
fetch(path)
|
|
2864
2817
|
.then((r) => {
|
|
@@ -2866,7 +2819,7 @@ Q5.modules.util = ($, p) => {
|
|
|
2866
2819
|
if (type == 'text') return r.text();
|
|
2867
2820
|
})
|
|
2868
2821
|
.then((r) => {
|
|
2869
|
-
|
|
2822
|
+
q._preloadCount--;
|
|
2870
2823
|
Object.assign(ret, r);
|
|
2871
2824
|
if (cb) cb(r);
|
|
2872
2825
|
});
|
|
@@ -2911,7 +2864,7 @@ Q5.Vector = class {
|
|
|
2911
2864
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
2912
2865
|
}
|
|
2913
2866
|
_arg2v(x, y, z) {
|
|
2914
|
-
if (x
|
|
2867
|
+
if (x?.x !== undefined) return x;
|
|
2915
2868
|
if (y !== undefined) {
|
|
2916
2869
|
return { x, y, z: z || 0 };
|
|
2917
2870
|
}
|