q5 2.0.15 → 2.1.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 +20 -5
- package/package.json +5 -4
- package/q5-webgpu.js +3308 -0
- package/q5-webgpu.min.js +8 -0
- package/q5.js +532 -565
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +50 -174
- package/src/q5-2d-drawing.js +8 -39
- 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 +56 -30
- package/src/q5-core.js +42 -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 +119 -0
- package/src/q5-webgpu-drawing.js +281 -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.1
|
|
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,43 +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 ($.ctx) $.
|
|
88
|
+
if ($._beginRender) $._beginRender();
|
|
89
|
+
if ($.ctx) $.resetMatrix();
|
|
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
|
};
|
|
109
|
+
$.isLooping = () => $._loop;
|
|
105
110
|
$.redraw = (n = 1) => {
|
|
106
111
|
$._redraw = true;
|
|
107
112
|
for (let i = 0; i < n; i++) {
|
|
108
|
-
_draw();
|
|
113
|
+
$._draw();
|
|
109
114
|
}
|
|
110
115
|
$._redraw = false;
|
|
111
116
|
};
|
|
@@ -135,7 +140,12 @@ function Q5(scope, parent) {
|
|
|
135
140
|
$.describe = () => {};
|
|
136
141
|
|
|
137
142
|
for (let m in Q5.modules) {
|
|
138
|
-
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);
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
// INIT
|
|
@@ -146,12 +156,14 @@ function Q5(scope, parent) {
|
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
|
|
159
|
+
if (scope == 'graphics') return;
|
|
160
|
+
|
|
149
161
|
if (scope == 'global') {
|
|
150
162
|
Object.assign(Q5, $);
|
|
151
163
|
delete Q5.Q5;
|
|
152
164
|
}
|
|
153
165
|
|
|
154
|
-
for (let m of Q5.
|
|
166
|
+
for (let m of Q5.methods.init) {
|
|
155
167
|
m.call($);
|
|
156
168
|
}
|
|
157
169
|
|
|
@@ -168,8 +180,6 @@ function Q5(scope, parent) {
|
|
|
168
180
|
|
|
169
181
|
if (typeof scope == 'function') scope($);
|
|
170
182
|
|
|
171
|
-
if (scope == 'graphics') return;
|
|
172
|
-
|
|
173
183
|
Q5._instanceCount++;
|
|
174
184
|
|
|
175
185
|
let raf =
|
|
@@ -217,8 +227,6 @@ function Q5(scope, parent) {
|
|
|
217
227
|
|
|
218
228
|
if (!($.setup || $.draw)) return;
|
|
219
229
|
|
|
220
|
-
$._startDone = false;
|
|
221
|
-
|
|
222
230
|
async function _start() {
|
|
223
231
|
$._startDone = true;
|
|
224
232
|
if ($._preloadCount > 0) return raf(_start);
|
|
@@ -228,7 +236,7 @@ function Q5(scope, parent) {
|
|
|
228
236
|
if ($.ctx === null) $.createCanvas(100, 100);
|
|
229
237
|
$._setupDone = true;
|
|
230
238
|
if ($.ctx) $.resetMatrix();
|
|
231
|
-
raf(_draw);
|
|
239
|
+
raf($._draw);
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
if ((arguments.length && scope != 'namespace') || preloadDefined) {
|
|
@@ -242,6 +250,7 @@ function Q5(scope, parent) {
|
|
|
242
250
|
}
|
|
243
251
|
}
|
|
244
252
|
|
|
253
|
+
Q5.renderers = {};
|
|
245
254
|
Q5.modules = {};
|
|
246
255
|
|
|
247
256
|
Q5._nodejs = typeof process == 'object';
|
|
@@ -252,13 +261,13 @@ Q5._friendlyError = (msg, func) => {
|
|
|
252
261
|
};
|
|
253
262
|
Q5._validateParameters = () => true;
|
|
254
263
|
|
|
255
|
-
Q5.
|
|
264
|
+
Q5.methods = {
|
|
256
265
|
init: [],
|
|
257
266
|
pre: [],
|
|
258
267
|
post: [],
|
|
259
268
|
remove: []
|
|
260
269
|
};
|
|
261
|
-
Q5.prototype.registerMethod = (m, fn) => Q5.
|
|
270
|
+
Q5.prototype.registerMethod = (m, fn) => Q5.methods[m].push(fn);
|
|
262
271
|
Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.prototype[n] = fn[n]);
|
|
263
272
|
|
|
264
273
|
if (Q5._nodejs) global.p5 ??= global.Q5 = Q5;
|
|
@@ -270,7 +279,7 @@ if (typeof document == 'object') {
|
|
|
270
279
|
if (!Q5._hasGlobal) new Q5('auto');
|
|
271
280
|
});
|
|
272
281
|
}
|
|
273
|
-
Q5.modules.
|
|
282
|
+
Q5.modules.canvas = ($, q) => {
|
|
274
283
|
$._OffscreenCanvas =
|
|
275
284
|
window.OffscreenCanvas ||
|
|
276
285
|
function () {
|
|
@@ -279,59 +288,28 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
279
288
|
|
|
280
289
|
if (Q5._nodejs) {
|
|
281
290
|
if (Q5._createNodeJSCanvas) {
|
|
282
|
-
|
|
291
|
+
q.canvas = Q5._createNodeJSCanvas(100, 100);
|
|
283
292
|
}
|
|
284
293
|
} else if ($._scope == 'image' || $._scope == 'graphics') {
|
|
285
|
-
|
|
294
|
+
q.canvas = new $._OffscreenCanvas(100, 100);
|
|
286
295
|
}
|
|
296
|
+
|
|
287
297
|
if (!$.canvas) {
|
|
288
298
|
if (typeof document == 'object') {
|
|
289
|
-
|
|
299
|
+
q.canvas = document.createElement('canvas');
|
|
290
300
|
$.canvas.id = 'q5Canvas' + Q5._instanceCount;
|
|
291
301
|
$.canvas.classList.add('q5Canvas');
|
|
292
302
|
} else $.noCanvas();
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
let c = $.canvas;
|
|
296
|
-
|
|
297
306
|
c.width = $.width = 100;
|
|
298
307
|
c.height = $.height = 100;
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
$.
|
|
302
|
-
let parent = $._parent;
|
|
303
|
-
if (parent && typeof parent == 'string') {
|
|
304
|
-
parent = document.getElementById(parent);
|
|
305
|
-
}
|
|
306
|
-
c.parent = (el) => {
|
|
307
|
-
if (typeof el == 'string') el = document.getElementById(el);
|
|
308
|
-
el.append(c);
|
|
309
|
-
|
|
310
|
-
function parentResized() {
|
|
311
|
-
if ($.frameCount > 1) {
|
|
312
|
-
$._shouldResize = true;
|
|
313
|
-
$._adjustDisplay();
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (typeof ResizeObserver == 'function') {
|
|
317
|
-
if ($._ro) $._ro.disconnect();
|
|
318
|
-
$._ro = new ResizeObserver(parentResized);
|
|
319
|
-
$._ro.observe(parent);
|
|
320
|
-
} else if (!$.frameCount) {
|
|
321
|
-
window.addEventListener('resize', parentResized);
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
function appendCanvas() {
|
|
325
|
-
parent ??= document.getElementsByTagName('main')[0];
|
|
326
|
-
if (!parent) {
|
|
327
|
-
parent = document.createElement('main');
|
|
328
|
-
document.body.append(parent);
|
|
329
|
-
}
|
|
330
|
-
c.parent(parent);
|
|
331
|
-
}
|
|
332
|
-
if (document.body) appendCanvas();
|
|
333
|
-
else document.addEventListener('DOMContentLoaded', appendCanvas);
|
|
308
|
+
if ($._scope != 'image') {
|
|
309
|
+
c.renderer = $._renderer;
|
|
310
|
+
c[$._renderer] = true;
|
|
334
311
|
}
|
|
312
|
+
$._pixelDensity = 1;
|
|
335
313
|
|
|
336
314
|
$._adjustDisplay = () => {
|
|
337
315
|
if (c.style) {
|
|
@@ -340,25 +318,12 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
340
318
|
}
|
|
341
319
|
};
|
|
342
320
|
|
|
343
|
-
$.createCanvas = function (w, h,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
p.width = c.width = c.w = w || window.innerWidth;
|
|
347
|
-
p.height = c.height = c.h = h || window.innerHeight;
|
|
348
|
-
c.hw = w / 2;
|
|
349
|
-
c.hh = h / 2;
|
|
350
|
-
c.renderer = '2d';
|
|
321
|
+
$.createCanvas = function (w, h, options) {
|
|
322
|
+
options ??= arguments[3];
|
|
323
|
+
|
|
351
324
|
let opt = Object.assign({}, Q5.canvasOptions);
|
|
352
|
-
if (options) Object.assign(opt, options);
|
|
325
|
+
if (typeof options == 'object') Object.assign(opt, options);
|
|
353
326
|
|
|
354
|
-
p.ctx = p.drawingContext = c.getContext('2d', opt);
|
|
355
|
-
Object.assign(c, opt);
|
|
356
|
-
if ($._colorMode == 'rgb') $.colorMode('rgb');
|
|
357
|
-
if ($._scope != 'image') {
|
|
358
|
-
$._defaultStyle();
|
|
359
|
-
$._da = 0;
|
|
360
|
-
}
|
|
361
|
-
$.ctx.save();
|
|
362
327
|
if ($._scope != 'image') {
|
|
363
328
|
let pd = $.displayDensity();
|
|
364
329
|
if ($._scope == 'graphics') pd = this._pixelDensity;
|
|
@@ -367,96 +332,225 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
367
332
|
c.visible = e[0].isIntersecting;
|
|
368
333
|
}).observe(c);
|
|
369
334
|
}
|
|
370
|
-
$.
|
|
371
|
-
}
|
|
335
|
+
$._pixelDensity = Math.ceil(pd);
|
|
336
|
+
}
|
|
372
337
|
|
|
373
|
-
|
|
374
|
-
else $._adjustDisplay();
|
|
375
|
-
return c;
|
|
376
|
-
};
|
|
377
|
-
$._createCanvas = $.createCanvas;
|
|
338
|
+
$._setCanvasSize(w, h);
|
|
378
339
|
|
|
379
|
-
|
|
340
|
+
Object.assign(c, opt);
|
|
341
|
+
let rend = $._createCanvas(c.w, c.h, opt);
|
|
380
342
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
$.ctx.lineJoin = 'miter';
|
|
386
|
-
$.ctx.textAlign = 'left';
|
|
343
|
+
if ($._hooks) {
|
|
344
|
+
for (let m of $._hooks.postCanvas) m();
|
|
345
|
+
}
|
|
346
|
+
return rend;
|
|
387
347
|
};
|
|
388
348
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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);
|
|
393
371
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
+
};
|
|
397
390
|
|
|
398
|
-
|
|
391
|
+
$._setCanvasSize = (w, h) => {
|
|
399
392
|
w ??= window.innerWidth;
|
|
400
393
|
h ??= window.innerHeight;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
let o;
|
|
404
|
-
if ($.frameCount) {
|
|
405
|
-
o = new $._OffscreenCanvas(c.width, c.height);
|
|
406
|
-
o.w = c.w;
|
|
407
|
-
o.h = c.h;
|
|
408
|
-
let oCtx = o.getContext('2d');
|
|
409
|
-
oCtx.drawImage(c, 0, 0);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
c.width = Math.ceil(w * $._pixelDensity);
|
|
413
|
-
c.height = Math.ceil(h * $._pixelDensity);
|
|
414
|
-
c.w = w;
|
|
415
|
-
c.h = h;
|
|
394
|
+
c.w = w = Math.ceil(w);
|
|
395
|
+
c.h = h = Math.ceil(h);
|
|
416
396
|
c.hw = w / 2;
|
|
417
397
|
c.hh = h / 2;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
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);
|
|
422
400
|
|
|
423
401
|
if (!$._da) {
|
|
424
|
-
|
|
425
|
-
|
|
402
|
+
q.width = w;
|
|
403
|
+
q.height = h;
|
|
426
404
|
} else $.flexibleCanvas($._dau);
|
|
427
405
|
|
|
428
|
-
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);
|
|
429
445
|
}
|
|
430
446
|
|
|
431
447
|
$.resizeCanvas = (w, h) => {
|
|
448
|
+
if (!$.ctx) return $.createCanvas(w, h);
|
|
432
449
|
if (w == c.w && h == c.h) return;
|
|
433
|
-
|
|
450
|
+
|
|
451
|
+
$._resizeCanvas(w, h);
|
|
434
452
|
};
|
|
435
453
|
|
|
436
|
-
$.
|
|
454
|
+
$.canvas.resize = $.resizeCanvas;
|
|
455
|
+
$.canvas.save = $.saveCanvas = $.save;
|
|
456
|
+
|
|
437
457
|
$.displayDensity = () => window.devicePixelRatio;
|
|
438
458
|
$.pixelDensity = (v) => {
|
|
439
459
|
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
440
460
|
$._pixelDensity = v;
|
|
441
|
-
|
|
461
|
+
$._setCanvasSize(c.w, c.h);
|
|
442
462
|
return v;
|
|
443
463
|
};
|
|
444
464
|
|
|
445
|
-
if ($._scope == 'image') return;
|
|
446
|
-
|
|
447
|
-
$.fullscreen = (v) => {
|
|
448
|
-
if (v === undefined) return document.fullscreenElement;
|
|
449
|
-
if (v) document.body.requestFullscreen();
|
|
450
|
-
else document.body.exitFullscreen();
|
|
451
|
-
};
|
|
452
|
-
|
|
453
465
|
$.flexibleCanvas = (unit = 400) => {
|
|
454
466
|
if (unit) {
|
|
455
467
|
$._da = c.width / (unit * $._pixelDensity);
|
|
456
|
-
|
|
457
|
-
|
|
468
|
+
q.width = $._dau = unit;
|
|
469
|
+
q.height = (c.h / c.w) * unit;
|
|
458
470
|
} else $._da = 0;
|
|
459
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
|
+
$.strokeWeight = (n) => {
|
|
528
|
+
if (!n) $._doStroke = false;
|
|
529
|
+
if ($._da) n *= $._da;
|
|
530
|
+
$.ctx.lineWidth = n || 0.0001;
|
|
531
|
+
};
|
|
532
|
+
$.stroke = function (c) {
|
|
533
|
+
$._doStroke = true;
|
|
534
|
+
$._strokeSet = true;
|
|
535
|
+
if (Q5.Color) {
|
|
536
|
+
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
537
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
538
|
+
if (c.a <= 0) return ($._doStroke = false);
|
|
539
|
+
}
|
|
540
|
+
$.ctx.strokeStyle = c.toString();
|
|
541
|
+
};
|
|
542
|
+
$.noStroke = () => ($._doStroke = false);
|
|
543
|
+
$.fill = function (c) {
|
|
544
|
+
$._doFill = true;
|
|
545
|
+
$._fillSet = true;
|
|
546
|
+
if (Q5.Color) {
|
|
547
|
+
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
548
|
+
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
549
|
+
if (c.a <= 0) return ($._doFill = false);
|
|
550
|
+
}
|
|
551
|
+
$.ctx.fillStyle = c.toString();
|
|
552
|
+
};
|
|
553
|
+
$.noFill = () => ($._doFill = false);
|
|
460
554
|
|
|
461
555
|
// DRAWING MATRIX
|
|
462
556
|
|
|
@@ -481,7 +575,7 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
481
575
|
$.shearY = (ang) => $.ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
|
|
482
576
|
$.resetMatrix = () => {
|
|
483
577
|
$.ctx.resetTransform();
|
|
484
|
-
$.
|
|
578
|
+
$.scale($._pixelDensity);
|
|
485
579
|
};
|
|
486
580
|
|
|
487
581
|
$._styleNames = [
|
|
@@ -535,29 +629,20 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
535
629
|
opt ??= {};
|
|
536
630
|
opt.alpha ??= true;
|
|
537
631
|
opt.colorSpace ??= $.canvas.colorSpace;
|
|
538
|
-
g.
|
|
632
|
+
g.createCanvas.call($, w, h, opt);
|
|
539
633
|
return g;
|
|
540
634
|
};
|
|
541
635
|
|
|
542
636
|
if (window && $._scope != 'graphics') {
|
|
543
637
|
window.addEventListener('resize', () => {
|
|
544
638
|
$._shouldResize = true;
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
639
|
+
q.windowWidth = window.innerWidth;
|
|
640
|
+
q.windowHeight = window.innerHeight;
|
|
641
|
+
q.deviceOrientation = window.screen?.orientation?.type;
|
|
548
642
|
});
|
|
549
643
|
}
|
|
550
644
|
};
|
|
551
|
-
|
|
552
|
-
Q5.canvasOptions = {
|
|
553
|
-
alpha: false,
|
|
554
|
-
colorSpace: 'display-p3'
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
if (!window.matchMedia || !matchMedia('(dynamic-range: high) and (color-gamut: p3)').matches) {
|
|
558
|
-
Q5.canvasOptions.colorSpace = 'srgb';
|
|
559
|
-
} else Q5.supportsHDR = true;
|
|
560
|
-
Q5.modules.q2d_drawing = ($) => {
|
|
645
|
+
Q5.renderers.q2d.drawing = ($) => {
|
|
561
646
|
$.CHORD = 0;
|
|
562
647
|
$.PIE = 1;
|
|
563
648
|
$.OPEN = 2;
|
|
@@ -619,33 +704,6 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
619
704
|
|
|
620
705
|
// DRAWING SETTINGS
|
|
621
706
|
|
|
622
|
-
$.strokeWeight = (n) => {
|
|
623
|
-
if (!n) $._doStroke = false;
|
|
624
|
-
if ($._da) n *= $._da;
|
|
625
|
-
$.ctx.lineWidth = n || 0.0001;
|
|
626
|
-
};
|
|
627
|
-
$.stroke = function (c) {
|
|
628
|
-
$._doStroke = true;
|
|
629
|
-
$._strokeSet = true;
|
|
630
|
-
if (Q5.Color) {
|
|
631
|
-
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
632
|
-
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
633
|
-
if (c.a <= 0) return ($._doStroke = false);
|
|
634
|
-
}
|
|
635
|
-
$.ctx.strokeStyle = c.toString();
|
|
636
|
-
};
|
|
637
|
-
$.noStroke = () => ($._doStroke = false);
|
|
638
|
-
$.fill = function (c) {
|
|
639
|
-
$._doFill = true;
|
|
640
|
-
$._fillSet = true;
|
|
641
|
-
if (Q5.Color) {
|
|
642
|
-
if (!c._q5Color && typeof c != 'string') c = $.color(...arguments);
|
|
643
|
-
else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
|
|
644
|
-
if (c.a <= 0) return ($._doFill = false);
|
|
645
|
-
}
|
|
646
|
-
$.ctx.fillStyle = c.toString();
|
|
647
|
-
};
|
|
648
|
-
$.noFill = () => ($._doFill = false);
|
|
649
707
|
$.blendMode = (x) => ($.ctx.globalCompositeOperation = x);
|
|
650
708
|
$.strokeCap = (x) => ($.ctx.lineCap = x);
|
|
651
709
|
$.strokeJoin = (x) => ($.ctx.lineJoin = x);
|
|
@@ -848,22 +906,18 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
848
906
|
return $.rect(x, y, s, s, tl, tr, br, bl);
|
|
849
907
|
};
|
|
850
908
|
|
|
851
|
-
function clearBuff() {
|
|
852
|
-
curveBuff = [];
|
|
853
|
-
}
|
|
854
|
-
|
|
855
909
|
$.beginShape = () => {
|
|
856
|
-
|
|
910
|
+
curveBuff = [];
|
|
857
911
|
$.ctx.beginPath();
|
|
858
912
|
firstVertex = true;
|
|
859
913
|
};
|
|
860
914
|
$.beginContour = () => {
|
|
861
915
|
$.ctx.closePath();
|
|
862
|
-
|
|
916
|
+
curveBuff = [];
|
|
863
917
|
firstVertex = true;
|
|
864
918
|
};
|
|
865
919
|
$.endContour = () => {
|
|
866
|
-
|
|
920
|
+
curveBuff = [];
|
|
867
921
|
firstVertex = true;
|
|
868
922
|
};
|
|
869
923
|
$.vertex = (x, y) => {
|
|
@@ -871,7 +925,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
871
925
|
x *= $._da;
|
|
872
926
|
y *= $._da;
|
|
873
927
|
}
|
|
874
|
-
|
|
928
|
+
curveBuff = [];
|
|
875
929
|
if (firstVertex) {
|
|
876
930
|
$.ctx.moveTo(x, y);
|
|
877
931
|
} else {
|
|
@@ -888,7 +942,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
888
942
|
x *= $._da;
|
|
889
943
|
y *= $._da;
|
|
890
944
|
}
|
|
891
|
-
|
|
945
|
+
curveBuff = [];
|
|
892
946
|
$.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
893
947
|
};
|
|
894
948
|
$.quadraticVertex = (cp1x, cp1y, x, y) => {
|
|
@@ -898,7 +952,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
898
952
|
x *= $._da;
|
|
899
953
|
y *= $._da;
|
|
900
954
|
}
|
|
901
|
-
|
|
955
|
+
curveBuff = [];
|
|
902
956
|
$.ctx.quadraticCurveTo(cp1x, cp1y, x, y);
|
|
903
957
|
};
|
|
904
958
|
$.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
@@ -923,7 +977,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
923
977
|
$.endShape($.CLOSE);
|
|
924
978
|
};
|
|
925
979
|
$.endShape = (close) => {
|
|
926
|
-
|
|
980
|
+
curveBuff = [];
|
|
927
981
|
if (close) $.ctx.closePath();
|
|
928
982
|
ink();
|
|
929
983
|
};
|
|
@@ -1075,7 +1129,32 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
1075
1129
|
return $.ctx.isPointInStroke(x * pd, y * pd);
|
|
1076
1130
|
};
|
|
1077
1131
|
};
|
|
1078
|
-
Q5.
|
|
1132
|
+
Q5.renderers.q2d.image = ($, q) => {
|
|
1133
|
+
class Q5Image {
|
|
1134
|
+
constructor(w, h, opt) {
|
|
1135
|
+
let $ = this;
|
|
1136
|
+
$._scope = 'image';
|
|
1137
|
+
$.canvas = $.ctx = $.drawingContext = null;
|
|
1138
|
+
$.pixels = [];
|
|
1139
|
+
Q5.modules.canvas($, $);
|
|
1140
|
+
let r = Q5.renderers.q2d;
|
|
1141
|
+
for (let m of ['canvas', 'image', 'soft_filters']) {
|
|
1142
|
+
if (r[m]) r[m]($, $);
|
|
1143
|
+
}
|
|
1144
|
+
$.createCanvas(w, h, opt);
|
|
1145
|
+
delete $.createCanvas;
|
|
1146
|
+
$._loop = false;
|
|
1147
|
+
}
|
|
1148
|
+
get w() {
|
|
1149
|
+
return this.width;
|
|
1150
|
+
}
|
|
1151
|
+
get h() {
|
|
1152
|
+
return this.height;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
Q5.Image ??= Q5Image;
|
|
1157
|
+
|
|
1079
1158
|
$.createImage = (w, h, opt) => {
|
|
1080
1159
|
opt ??= {};
|
|
1081
1160
|
opt.alpha ??= true;
|
|
@@ -1083,115 +1162,145 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1083
1162
|
return new Q5.Image(w, h, opt);
|
|
1084
1163
|
};
|
|
1085
1164
|
|
|
1086
|
-
$.
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
function makeTmpCtx(w, h) {
|
|
1092
|
-
h ??= w || $.canvas.height;
|
|
1093
|
-
w ??= $.canvas.width;
|
|
1094
|
-
if (tmpCtx == null) {
|
|
1095
|
-
tmpCtx = new $._OffscreenCanvas(w, h).getContext('2d', {
|
|
1096
|
-
colorSpace: $.canvas.colorSpace
|
|
1097
|
-
});
|
|
1165
|
+
$.loadImage = function (url, cb, opt) {
|
|
1166
|
+
if (url.canvas) return url;
|
|
1167
|
+
if (url.slice(-3).toLowerCase() == 'gif') {
|
|
1168
|
+
throw new Error(`q5 doesn't support GIFs due to their impact on performance. Use a video or animation instead.`);
|
|
1098
1169
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1170
|
+
q._preloadCount++;
|
|
1171
|
+
let last = [...arguments].at(-1);
|
|
1172
|
+
opt = typeof last == 'object' ? last : null;
|
|
1173
|
+
|
|
1174
|
+
let g = $.createImage(1, 1, opt);
|
|
1175
|
+
|
|
1176
|
+
function loaded(img) {
|
|
1177
|
+
g.resize(img.naturalWidth || img.width, img.naturalHeight || img.height);
|
|
1178
|
+
g.ctx.drawImage(img, 0, 0);
|
|
1179
|
+
q._preloadCount--;
|
|
1180
|
+
if (cb) cb(g);
|
|
1102
1181
|
}
|
|
1103
|
-
}
|
|
1104
1182
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1183
|
+
if (Q5._nodejs && global.CairoCanvas) {
|
|
1184
|
+
global.CairoCanvas.loadImage(url)
|
|
1185
|
+
.then(loaded)
|
|
1186
|
+
.catch((e) => {
|
|
1187
|
+
q._preloadCount--;
|
|
1188
|
+
throw e;
|
|
1189
|
+
});
|
|
1190
|
+
} else {
|
|
1191
|
+
let img = new window.Image();
|
|
1192
|
+
img.src = url;
|
|
1193
|
+
img.crossOrigin = 'Anonymous';
|
|
1194
|
+
img._pixelDensity = 1;
|
|
1195
|
+
img.onload = () => loaded(img);
|
|
1196
|
+
img.onerror = (e) => {
|
|
1197
|
+
q._preloadCount--;
|
|
1198
|
+
throw e;
|
|
1199
|
+
};
|
|
1112
1200
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1201
|
+
return g;
|
|
1202
|
+
};
|
|
1203
|
+
|
|
1204
|
+
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1205
|
+
$.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
|
|
1206
|
+
let drawable = img.canvas || img;
|
|
1207
|
+
if (Q5._createNodeJSCanvas) {
|
|
1208
|
+
drawable = drawable.context.canvas;
|
|
1116
1209
|
}
|
|
1117
|
-
|
|
1210
|
+
dw ??= img.width || img.videoWidth;
|
|
1211
|
+
dh ??= img.height || img.videoHeight;
|
|
1212
|
+
if ($._imageMode == 'center') {
|
|
1213
|
+
dx -= dw * 0.5;
|
|
1214
|
+
dy -= dh * 0.5;
|
|
1215
|
+
}
|
|
1216
|
+
if ($._da) {
|
|
1217
|
+
dx *= $._da;
|
|
1218
|
+
dy *= $._da;
|
|
1219
|
+
dw *= $._da;
|
|
1220
|
+
dh *= $._da;
|
|
1221
|
+
sx *= $._da;
|
|
1222
|
+
sy *= $._da;
|
|
1223
|
+
sw *= $._da;
|
|
1224
|
+
sh *= $._da;
|
|
1225
|
+
}
|
|
1226
|
+
let pd = img._pixelDensity || 1;
|
|
1227
|
+
if (!sw) {
|
|
1228
|
+
sw = drawable.width || drawable.videoWidth;
|
|
1229
|
+
} else sw *= pd;
|
|
1230
|
+
if (!sh) {
|
|
1231
|
+
sh = drawable.height || drawable.videoHeight;
|
|
1232
|
+
} else sh *= pd;
|
|
1233
|
+
$.ctx.drawImage(drawable, sx * pd, sy * pd, sw, sh, dx, dy, dw, dh);
|
|
1234
|
+
|
|
1235
|
+
if ($._tint) {
|
|
1236
|
+
$.ctx.globalCompositeOperation = 'multiply';
|
|
1237
|
+
$.ctx.fillStyle = $._tint.toString();
|
|
1238
|
+
$.ctx.fillRect(dx, dy, dw, dh);
|
|
1239
|
+
$.ctx.globalCompositeOperation = 'source-over';
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
$._tint = null;
|
|
1244
|
+
let imgData = null;
|
|
1118
1245
|
|
|
1119
1246
|
$._softFilter = () => {
|
|
1120
|
-
throw 'Load q5-2d-soft-filters.js to use software filters.';
|
|
1247
|
+
throw new Error('Load q5-2d-soft-filters.js to use software filters.');
|
|
1121
1248
|
};
|
|
1122
1249
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
tmpCtx.filter = filt;
|
|
1126
|
-
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1127
|
-
$.ctx.save();
|
|
1128
|
-
$.ctx.resetTransform();
|
|
1129
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1130
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1131
|
-
$.ctx.restore();
|
|
1132
|
-
}
|
|
1250
|
+
$.filter = (type, x) => {
|
|
1251
|
+
if (!$.ctx.filter) return $._softFilter(type, x);
|
|
1133
1252
|
|
|
1134
|
-
|
|
1135
|
-
if (
|
|
1136
|
-
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1253
|
+
if (typeof type == 'string') f = type;
|
|
1254
|
+
else if (type == Q5.GRAY) f = `saturate(0%)`;
|
|
1255
|
+
else if (type == Q5.INVERT) f = `invert(100%)`;
|
|
1256
|
+
else if (type == Q5.BLUR) {
|
|
1257
|
+
let r = Math.ceil(x * $._pixelDensity) || 1;
|
|
1258
|
+
f = `blur(${r}px)`;
|
|
1259
|
+
} else if (type == Q5.THRESHOLD) {
|
|
1140
1260
|
x ??= 0.5;
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
} else if (typ == Q5.GRAY) {
|
|
1145
|
-
nativeFilter(`saturate(0%)`);
|
|
1146
|
-
} else if (typ == Q5.OPAQUE) {
|
|
1147
|
-
tmpCtx.fillStyle = 'black';
|
|
1148
|
-
tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1149
|
-
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1150
|
-
$.ctx.save();
|
|
1151
|
-
$.ctx.resetTransform();
|
|
1152
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1153
|
-
$.ctx.restore();
|
|
1154
|
-
} else if (typ == Q5.INVERT) {
|
|
1155
|
-
nativeFilter(`invert(100%)`);
|
|
1156
|
-
} else if (typ == Q5.BLUR) {
|
|
1157
|
-
nativeFilter(`blur(${Math.ceil((x * $._pixelDensity) / 1) || 1}px)`);
|
|
1158
|
-
} else {
|
|
1159
|
-
$._softFilter(typ, x);
|
|
1160
|
-
}
|
|
1161
|
-
};
|
|
1261
|
+
let b = Math.floor((0.5 / Math.max(x, 0.00001)) * 100);
|
|
1262
|
+
f = `saturate(0%) brightness(${b}%) contrast(1000000%)`;
|
|
1263
|
+
} else return $._softFilter(type, x);
|
|
1162
1264
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
p.width = w;
|
|
1167
|
-
p.height = h;
|
|
1168
|
-
$.canvas.width = w * $._pixelDensity;
|
|
1169
|
-
$.canvas.height = h * $._pixelDensity;
|
|
1170
|
-
$.ctx.save();
|
|
1171
|
-
$.ctx.resetTransform();
|
|
1172
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1173
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0, $.canvas.width, $.canvas.height);
|
|
1174
|
-
$.ctx.restore();
|
|
1265
|
+
$.ctx.filter = f;
|
|
1266
|
+
$.ctx.drawImage($.canvas, 0, 0, $.canvas.w, $.canvas.h);
|
|
1267
|
+
$.ctx.filter = 'none';
|
|
1175
1268
|
};
|
|
1176
1269
|
|
|
1270
|
+
if ($._scope == 'image') {
|
|
1271
|
+
$.resize = (w, h) => {
|
|
1272
|
+
let o = new $._OffscreenCanvas($.canvas.width, $.canvas.height);
|
|
1273
|
+
let tmpCtx = o.getContext('2d', {
|
|
1274
|
+
colorSpace: $.canvas.colorSpace
|
|
1275
|
+
});
|
|
1276
|
+
tmpCtx.drawImage($.canvas, 0, 0);
|
|
1277
|
+
$._setCanvasSize(w, h);
|
|
1278
|
+
|
|
1279
|
+
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
1280
|
+
$.ctx.drawImage(o, 0, 0, $.canvas.width, $.canvas.height);
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1177
1284
|
$.trim = () => {
|
|
1178
1285
|
let pd = $._pixelDensity || 1;
|
|
1179
|
-
let
|
|
1180
|
-
let
|
|
1181
|
-
let
|
|
1286
|
+
let w = $.canvas.width;
|
|
1287
|
+
let h = $.canvas.height;
|
|
1288
|
+
let data = $.ctx.getImageData(0, 0, w, h).data;
|
|
1289
|
+
let left = w,
|
|
1182
1290
|
right = 0,
|
|
1183
|
-
top =
|
|
1291
|
+
top = h,
|
|
1184
1292
|
bottom = 0;
|
|
1185
1293
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
if (data[
|
|
1294
|
+
let i = 3;
|
|
1295
|
+
for (let y = 0; y < h; y++) {
|
|
1296
|
+
for (let x = 0; x < w; x++) {
|
|
1297
|
+
if (data[i] !== 0) {
|
|
1190
1298
|
if (x < left) left = x;
|
|
1191
1299
|
if (x > right) right = x;
|
|
1192
1300
|
if (y < top) top = y;
|
|
1193
1301
|
if (y > bottom) bottom = y;
|
|
1194
1302
|
}
|
|
1303
|
+
i += 4;
|
|
1195
1304
|
}
|
|
1196
1305
|
}
|
|
1197
1306
|
top = Math.floor(top / pd);
|
|
@@ -1212,48 +1321,6 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1212
1321
|
$.ctx.restore();
|
|
1213
1322
|
};
|
|
1214
1323
|
|
|
1215
|
-
$._save = async (data, name, ext) => {
|
|
1216
|
-
name = name || 'untitled';
|
|
1217
|
-
ext = ext || 'png';
|
|
1218
|
-
if (ext == 'jpg' || ext == 'png' || ext == 'webp') {
|
|
1219
|
-
if (data instanceof OffscreenCanvas) {
|
|
1220
|
-
const blob = await data.convertToBlob({ type: 'image/' + ext });
|
|
1221
|
-
data = await new Promise((resolve) => {
|
|
1222
|
-
const reader = new FileReader();
|
|
1223
|
-
reader.onloadend = () => resolve(reader.result);
|
|
1224
|
-
reader.readAsDataURL(blob);
|
|
1225
|
-
});
|
|
1226
|
-
} else {
|
|
1227
|
-
data = data.toDataURL('image/' + ext);
|
|
1228
|
-
}
|
|
1229
|
-
} else {
|
|
1230
|
-
let type = 'text/plain';
|
|
1231
|
-
if (ext == 'json') {
|
|
1232
|
-
if (typeof data != 'string') data = JSON.stringify(data);
|
|
1233
|
-
type = 'text/json';
|
|
1234
|
-
}
|
|
1235
|
-
data = new Blob([data], { type });
|
|
1236
|
-
data = URL.createObjectURL(data);
|
|
1237
|
-
}
|
|
1238
|
-
let a = document.createElement('a');
|
|
1239
|
-
a.href = data;
|
|
1240
|
-
a.download = name + '.' + ext;
|
|
1241
|
-
a.click();
|
|
1242
|
-
URL.revokeObjectURL(a.href);
|
|
1243
|
-
};
|
|
1244
|
-
$.save = (a, b, c) => {
|
|
1245
|
-
if (!a || (typeof a == 'string' && (!b || (!c && b.length < 5)))) {
|
|
1246
|
-
c = b;
|
|
1247
|
-
b = a;
|
|
1248
|
-
a = $.canvas;
|
|
1249
|
-
}
|
|
1250
|
-
if (c) return $._save(a, b, c);
|
|
1251
|
-
if (b) {
|
|
1252
|
-
b = b.split('.');
|
|
1253
|
-
$._save(a, b[0], b.at(-1));
|
|
1254
|
-
} else $._save(a);
|
|
1255
|
-
};
|
|
1256
|
-
|
|
1257
1324
|
$.get = (x, y, w, h) => {
|
|
1258
1325
|
let pd = $._pixelDensity || 1;
|
|
1259
1326
|
if (x !== undefined && w === undefined) {
|
|
@@ -1298,174 +1365,23 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1298
1365
|
|
|
1299
1366
|
$.loadPixels = () => {
|
|
1300
1367
|
imgData = $.ctx.getImageData(0, 0, $.canvas.width, $.canvas.height);
|
|
1301
|
-
|
|
1368
|
+
q.pixels = imgData.data;
|
|
1302
1369
|
};
|
|
1303
1370
|
$.updatePixels = () => {
|
|
1304
1371
|
if (imgData != null) $.ctx.putImageData(imgData, 0, 0);
|
|
1305
1372
|
};
|
|
1306
1373
|
|
|
1307
|
-
$._tinted = function (col) {
|
|
1308
|
-
let alpha = col.a;
|
|
1309
|
-
col.a = 255;
|
|
1310
|
-
makeTmpCtx();
|
|
1311
|
-
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1312
|
-
tmpCtx.fillStyle = col.toString();
|
|
1313
|
-
tmpCtx.fillRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1314
|
-
tmpCtx.globalCompositeOperation = 'multiply';
|
|
1315
|
-
tmpCtx.drawImage($.ctx.canvas, 0, 0);
|
|
1316
|
-
tmpCtx.globalCompositeOperation = 'source-over';
|
|
1317
|
-
|
|
1318
|
-
$.ctx.save();
|
|
1319
|
-
$.ctx.resetTransform();
|
|
1320
|
-
let old = $.ctx.globalCompositeOperation;
|
|
1321
|
-
$.ctx.globalCompositeOperation = 'source-in';
|
|
1322
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1323
|
-
$.ctx.globalCompositeOperation = old;
|
|
1324
|
-
$.ctx.restore();
|
|
1325
|
-
|
|
1326
|
-
tmpCtx.globalAlpha = alpha / 255;
|
|
1327
|
-
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
|
1328
|
-
tmpCtx.drawImage($.ctx.canvas, 0, 0);
|
|
1329
|
-
tmpCtx.globalAlpha = 1;
|
|
1330
|
-
|
|
1331
|
-
$.ctx.save();
|
|
1332
|
-
$.ctx.resetTransform();
|
|
1333
|
-
$.ctx.clearRect(0, 0, $.ctx.canvas.width, $.ctx.canvas.height);
|
|
1334
|
-
$.ctx.drawImage(tmpCtx.canvas, 0, 0);
|
|
1335
|
-
$.ctx.restore();
|
|
1336
|
-
};
|
|
1337
|
-
|
|
1338
1374
|
$.smooth = () => ($.ctx.imageSmoothingEnabled = true);
|
|
1339
1375
|
$.noSmooth = () => ($.ctx.imageSmoothingEnabled = false);
|
|
1340
1376
|
|
|
1341
1377
|
if ($._scope == 'image') return;
|
|
1342
1378
|
|
|
1343
|
-
$.saveCanvas = $.canvas.save = $.save;
|
|
1344
|
-
|
|
1345
1379
|
$.tint = function (c) {
|
|
1346
1380
|
$._tint = c._q5Color ? c : $.color(...arguments);
|
|
1347
1381
|
};
|
|
1348
1382
|
$.noTint = () => ($._tint = null);
|
|
1349
|
-
|
|
1350
|
-
// IMAGING
|
|
1351
|
-
|
|
1352
|
-
$.imageMode = (mode) => ($._imageMode = mode);
|
|
1353
|
-
$.image = (img, dx, dy, dWidth, dHeight, sx = 0, sy = 0, sWidth, sHeight) => {
|
|
1354
|
-
if ($._da) {
|
|
1355
|
-
dx *= $._da;
|
|
1356
|
-
dy *= $._da;
|
|
1357
|
-
dWidth *= $._da;
|
|
1358
|
-
dHeight *= $._da;
|
|
1359
|
-
sx *= $._da;
|
|
1360
|
-
sy *= $._da;
|
|
1361
|
-
sWidth *= $._da;
|
|
1362
|
-
sHeight *= $._da;
|
|
1363
|
-
}
|
|
1364
|
-
let drawable = img.canvas || img;
|
|
1365
|
-
if (Q5._createNodeJSCanvas) {
|
|
1366
|
-
drawable = drawable.context.canvas;
|
|
1367
|
-
}
|
|
1368
|
-
function reset() {
|
|
1369
|
-
if (!img._q5 || !$._tint) return;
|
|
1370
|
-
let c = img.ctx;
|
|
1371
|
-
c.save();
|
|
1372
|
-
c.resetTransform();
|
|
1373
|
-
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
|
|
1374
|
-
c.drawImage(tmpCt2.canvas, 0, 0);
|
|
1375
|
-
c.restore();
|
|
1376
|
-
}
|
|
1377
|
-
if (img.canvas && $._tint != null) {
|
|
1378
|
-
makeTmpCt2(img.canvas.width, img.canvas.height);
|
|
1379
|
-
tmpCt2.drawImage(img.canvas, 0, 0);
|
|
1380
|
-
img._tinted($._tint);
|
|
1381
|
-
}
|
|
1382
|
-
dWidth ??= img.width || img.videoWidth;
|
|
1383
|
-
dHeight ??= img.height || img.videoHeight;
|
|
1384
|
-
if ($._imageMode == 'center') {
|
|
1385
|
-
dx -= dWidth * 0.5;
|
|
1386
|
-
dy -= dHeight * 0.5;
|
|
1387
|
-
}
|
|
1388
|
-
let pd = img._pixelDensity || 1;
|
|
1389
|
-
if (!sWidth) {
|
|
1390
|
-
sWidth = drawable.width || drawable.videoWidth;
|
|
1391
|
-
} else sWidth *= pd;
|
|
1392
|
-
if (!sHeight) {
|
|
1393
|
-
sHeight = drawable.height || drawable.videoHeight;
|
|
1394
|
-
} else sHeight *= pd;
|
|
1395
|
-
$.ctx.drawImage(drawable, sx * pd, sy * pd, sWidth, sHeight, dx, dy, dWidth, dHeight);
|
|
1396
|
-
reset();
|
|
1397
|
-
};
|
|
1398
|
-
|
|
1399
|
-
$.loadImage = function (url, cb, opt) {
|
|
1400
|
-
if (url.canvas) return url;
|
|
1401
|
-
if (url.slice(-3).toLowerCase() == 'gif') {
|
|
1402
|
-
throw new Error(`q5 doesn't support GIFs due to their impact on performance. Use a video or animation instead.`);
|
|
1403
|
-
}
|
|
1404
|
-
p._preloadCount++;
|
|
1405
|
-
let last = [...arguments].at(-1);
|
|
1406
|
-
opt = typeof last == 'object' ? last : null;
|
|
1407
|
-
|
|
1408
|
-
let g = $.createImage(1, 1, opt);
|
|
1409
|
-
|
|
1410
|
-
function loaded(img) {
|
|
1411
|
-
let c = g.ctx;
|
|
1412
|
-
g.width = c.canvas.width = img.naturalWidth || img.width;
|
|
1413
|
-
g.height = c.canvas.height = img.naturalHeight || img.height;
|
|
1414
|
-
c.drawImage(img, 0, 0);
|
|
1415
|
-
p._preloadCount--;
|
|
1416
|
-
if (cb) cb(g);
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
if (Q5._nodejs && global.CairoCanvas) {
|
|
1420
|
-
global.CairoCanvas.loadImage(url)
|
|
1421
|
-
.then(loaded)
|
|
1422
|
-
.catch((e) => {
|
|
1423
|
-
p._preloadCount--;
|
|
1424
|
-
throw e;
|
|
1425
|
-
});
|
|
1426
|
-
} else {
|
|
1427
|
-
let img = new window.Image();
|
|
1428
|
-
img.src = url;
|
|
1429
|
-
img.crossOrigin = 'Anonymous';
|
|
1430
|
-
img._pixelDensity = 1;
|
|
1431
|
-
img.onload = () => loaded(img);
|
|
1432
|
-
img.onerror = (e) => {
|
|
1433
|
-
p._preloadCount--;
|
|
1434
|
-
throw e;
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
return g;
|
|
1438
|
-
};
|
|
1439
1383
|
};
|
|
1440
1384
|
|
|
1441
|
-
// IMAGE CLASS
|
|
1442
|
-
|
|
1443
|
-
Q5.imageModules = ['q2d_canvas', 'q2d_image'];
|
|
1444
|
-
|
|
1445
|
-
class _Q5Image {
|
|
1446
|
-
constructor(w, h, opt) {
|
|
1447
|
-
let $ = this;
|
|
1448
|
-
$._scope = 'image';
|
|
1449
|
-
$.canvas = $.ctx = $.drawingContext = null;
|
|
1450
|
-
$.pixels = [];
|
|
1451
|
-
for (let m of Q5.imageModules) {
|
|
1452
|
-
Q5.modules[m]($, $);
|
|
1453
|
-
}
|
|
1454
|
-
delete this.createCanvas;
|
|
1455
|
-
|
|
1456
|
-
this._createCanvas(w, h, '2d', opt);
|
|
1457
|
-
this._loop = false;
|
|
1458
|
-
}
|
|
1459
|
-
get w() {
|
|
1460
|
-
return this.width;
|
|
1461
|
-
}
|
|
1462
|
-
get h() {
|
|
1463
|
-
return this.height;
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
Q5.Image ??= _Q5Image;
|
|
1468
|
-
|
|
1469
1385
|
Q5.THRESHOLD = 1;
|
|
1470
1386
|
Q5.GRAY = 2;
|
|
1471
1387
|
Q5.OPAQUE = 3;
|
|
@@ -1475,12 +1391,12 @@ Q5.DILATE = 6;
|
|
|
1475
1391
|
Q5.ERODE = 7;
|
|
1476
1392
|
Q5.BLUR = 8;
|
|
1477
1393
|
/* software implementation of image filters */
|
|
1478
|
-
Q5.
|
|
1394
|
+
Q5.renderers.q2d.soft_filters = ($) => {
|
|
1479
1395
|
let tmpBuf = null;
|
|
1480
1396
|
|
|
1481
|
-
function
|
|
1397
|
+
function ensureTmpBuf() {
|
|
1482
1398
|
let l = $.canvas.width * $.canvas.height * 4;
|
|
1483
|
-
if (!tmpBuf ||
|
|
1399
|
+
if (!tmpBuf || tmpBuf.length != l) {
|
|
1484
1400
|
tmpBuf = new Uint8ClampedArray(l);
|
|
1485
1401
|
}
|
|
1486
1402
|
}
|
|
@@ -1522,7 +1438,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1522
1438
|
}
|
|
1523
1439
|
};
|
|
1524
1440
|
$._filters[Q5.DILATE] = (data) => {
|
|
1525
|
-
|
|
1441
|
+
ensureTmpBuf();
|
|
1526
1442
|
tmpBuf.set(data);
|
|
1527
1443
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1528
1444
|
for (let i = 0; i < h; i++) {
|
|
@@ -1549,7 +1465,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1549
1465
|
}
|
|
1550
1466
|
};
|
|
1551
1467
|
$._filters[Q5.ERODE] = (data) => {
|
|
1552
|
-
|
|
1468
|
+
ensureTmpBuf();
|
|
1553
1469
|
tmpBuf.set(data);
|
|
1554
1470
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1555
1471
|
for (let i = 0; i < h; i++) {
|
|
@@ -1578,7 +1494,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1578
1494
|
$._filters[Q5.BLUR] = (data, rad) => {
|
|
1579
1495
|
rad = rad || 1;
|
|
1580
1496
|
rad = Math.floor(rad * $._pixelDensity);
|
|
1581
|
-
|
|
1497
|
+
ensureTmpBuf();
|
|
1582
1498
|
tmpBuf.set(data);
|
|
1583
1499
|
|
|
1584
1500
|
let ksize = rad * 2 + 1;
|
|
@@ -1650,7 +1566,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1650
1566
|
$.ctx.putImageData(imgData, 0, 0);
|
|
1651
1567
|
};
|
|
1652
1568
|
};
|
|
1653
|
-
Q5.
|
|
1569
|
+
Q5.renderers.q2d.text = ($, q) => {
|
|
1654
1570
|
$.NORMAL = 'normal';
|
|
1655
1571
|
$.ITALIC = 'italic';
|
|
1656
1572
|
$.BOLD = 'bold';
|
|
@@ -1670,12 +1586,12 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1670
1586
|
$._textStyle = 'normal';
|
|
1671
1587
|
|
|
1672
1588
|
$.loadFont = (url, cb) => {
|
|
1673
|
-
|
|
1589
|
+
q._preloadCount++;
|
|
1674
1590
|
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
1675
1591
|
let f = new FontFace(name, `url(${url})`);
|
|
1676
1592
|
document.fonts.add(f);
|
|
1677
1593
|
f.load().then(() => {
|
|
1678
|
-
|
|
1594
|
+
q._preloadCount--;
|
|
1679
1595
|
if (cb) cb(name);
|
|
1680
1596
|
});
|
|
1681
1597
|
return name;
|
|
@@ -1864,8 +1780,8 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1864
1780
|
};
|
|
1865
1781
|
};
|
|
1866
1782
|
Q5.modules.ai = ($) => {
|
|
1867
|
-
$.askAI = (
|
|
1868
|
-
throw Error('Ask AI ✨ ' +
|
|
1783
|
+
$.askAI = (question = '') => {
|
|
1784
|
+
throw Error('Ask AI ✨ ' + question);
|
|
1869
1785
|
};
|
|
1870
1786
|
|
|
1871
1787
|
$._aiErrorAssistance = async (e) => {
|
|
@@ -1928,22 +1844,24 @@ Q5.modules.ai = ($) => {
|
|
|
1928
1844
|
} catch (err) {}
|
|
1929
1845
|
};
|
|
1930
1846
|
};
|
|
1931
|
-
Q5.modules.color = ($,
|
|
1847
|
+
Q5.modules.color = ($, q) => {
|
|
1932
1848
|
$.RGB = $.RGBA = $._colorMode = 'rgb';
|
|
1933
1849
|
$.OKLCH = 'oklch';
|
|
1934
1850
|
|
|
1935
|
-
|
|
1936
|
-
else $.Color = Q5.ColorRGBA;
|
|
1937
|
-
|
|
1938
|
-
$.colorMode = (mode) => {
|
|
1851
|
+
$.colorMode = (mode, format) => {
|
|
1939
1852
|
$._colorMode = mode;
|
|
1853
|
+
let srgb = $.canvas.colorSpace == 'srgb' || mode == 'srgb';
|
|
1854
|
+
format ??= srgb ? 'integer' : 'float';
|
|
1855
|
+
$._colorFormat = format;
|
|
1940
1856
|
if (mode == 'oklch') {
|
|
1941
|
-
|
|
1942
|
-
} else
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1857
|
+
q.Color = Q5.ColorOKLCH;
|
|
1858
|
+
} else {
|
|
1859
|
+
let srgb = $.canvas.colorSpace == 'srgb';
|
|
1860
|
+
if ($._colorFormat == 'integer') {
|
|
1861
|
+
q.Color = srgb ? Q5.ColorRGBA_8 : Q5.ColorRGBA_P3_8;
|
|
1862
|
+
} else {
|
|
1863
|
+
q.Color = srgb ? Q5.ColorRGBA : Q5.ColorRGBA_P3;
|
|
1864
|
+
}
|
|
1947
1865
|
$._colorMode = 'rgb';
|
|
1948
1866
|
}
|
|
1949
1867
|
};
|
|
@@ -1988,27 +1906,33 @@ Q5.modules.color = ($, p) => {
|
|
|
1988
1906
|
let args = arguments;
|
|
1989
1907
|
if (args.length == 1) {
|
|
1990
1908
|
if (typeof c0 == 'string') {
|
|
1909
|
+
let r, g, b, a;
|
|
1991
1910
|
if (c0[0] == '#') {
|
|
1992
1911
|
if (c0.length <= 5) {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
);
|
|
1912
|
+
r = parseInt(c0[1] + c0[1], 16);
|
|
1913
|
+
g = parseInt(c0[2] + c0[2], 16);
|
|
1914
|
+
b = parseInt(c0[3] + c0[3], 16);
|
|
1915
|
+
a = c0.length == 4 ? null : parseInt(c0[4] + c0[4], 16);
|
|
1916
|
+
} else {
|
|
1917
|
+
r = parseInt(c0.slice(1, 3), 16);
|
|
1918
|
+
g = parseInt(c0.slice(3, 5), 16);
|
|
1919
|
+
b = parseInt(c0.slice(5, 7), 16);
|
|
1920
|
+
a = c0.length == 7 ? null : parseInt(c0.slice(7, 9), 16);
|
|
1999
1921
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
c0.length == 7 ? null : parseInt(c0.slice(7, 9), 16)
|
|
1922
|
+
} else if ($._namedColors[c0]) [r, g, b] = $._namedColors[c0];
|
|
1923
|
+
else {
|
|
1924
|
+
console.error(
|
|
1925
|
+
"q5 can't parse color: " + c0 + '\nOnly numeric input, hex, and common named colors are supported.'
|
|
2005
1926
|
);
|
|
1927
|
+
return new C(0, 0, 0);
|
|
2006
1928
|
}
|
|
2007
|
-
if ($.
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
1929
|
+
if ($._colorFormat != 'integer') {
|
|
1930
|
+
r /= 255;
|
|
1931
|
+
g /= 255;
|
|
1932
|
+
b /= 255;
|
|
1933
|
+
if (a != null) a /= 255;
|
|
1934
|
+
}
|
|
1935
|
+
return new C(r, g, b, a);
|
|
2012
1936
|
}
|
|
2013
1937
|
if (Array.isArray(c0)) return new C(...c0);
|
|
2014
1938
|
}
|
|
@@ -2078,7 +2002,24 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2078
2002
|
this.r = r;
|
|
2079
2003
|
this.g = g;
|
|
2080
2004
|
this.b = b;
|
|
2081
|
-
this.a = a ??
|
|
2005
|
+
this.a = a ?? 1;
|
|
2006
|
+
}
|
|
2007
|
+
get levels() {
|
|
2008
|
+
return [this.r, this.g, this.b, this.a];
|
|
2009
|
+
}
|
|
2010
|
+
toString() {
|
|
2011
|
+
return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
|
|
2015
|
+
toString() {
|
|
2016
|
+
return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
// legacy 8-bit (0-255) integer color format
|
|
2020
|
+
Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
|
|
2021
|
+
constructor(r, g, b, a) {
|
|
2022
|
+
super(r, g, b, a ?? 255);
|
|
2082
2023
|
}
|
|
2083
2024
|
setRed(v) {
|
|
2084
2025
|
this.r = v;
|
|
@@ -2099,9 +2040,10 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2099
2040
|
return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
|
|
2100
2041
|
}
|
|
2101
2042
|
};
|
|
2102
|
-
|
|
2043
|
+
// p3 10-bit color in integer color format, for backwards compatibility
|
|
2044
|
+
Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
|
|
2103
2045
|
constructor(r, g, b, a) {
|
|
2104
|
-
super(r, g, b, a);
|
|
2046
|
+
super(r, g, b, a ?? 255);
|
|
2105
2047
|
this._edited = true;
|
|
2106
2048
|
}
|
|
2107
2049
|
get r() {
|
|
@@ -2230,8 +2172,14 @@ main {
|
|
|
2230
2172
|
Object.assign(c, { displayMode, renderQuality, displayScale });
|
|
2231
2173
|
$._adjustDisplay();
|
|
2232
2174
|
};
|
|
2175
|
+
|
|
2176
|
+
$.fullscreen = (v) => {
|
|
2177
|
+
if (v === undefined) return document.fullscreenElement;
|
|
2178
|
+
if (v) document.body.requestFullscreen();
|
|
2179
|
+
else document.body.exitFullscreen();
|
|
2180
|
+
};
|
|
2233
2181
|
};
|
|
2234
|
-
Q5.modules.input = ($,
|
|
2182
|
+
Q5.modules.input = ($, q) => {
|
|
2235
2183
|
if ($._scope == 'graphics') return;
|
|
2236
2184
|
|
|
2237
2185
|
$.mouseX = 0;
|
|
@@ -2275,17 +2223,26 @@ Q5.modules.input = ($, p) => {
|
|
|
2275
2223
|
|
|
2276
2224
|
$._updateMouse = (e) => {
|
|
2277
2225
|
if (e.changedTouches) return;
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2226
|
+
if (c) {
|
|
2227
|
+
let rect = c.getBoundingClientRect();
|
|
2228
|
+
let sx = c.scrollWidth / $.width || 1;
|
|
2229
|
+
let sy = c.scrollHeight / $.height || 1;
|
|
2230
|
+
q.mouseX = (e.clientX - rect.left) / sx;
|
|
2231
|
+
q.mouseY = (e.clientY - rect.top) / sy;
|
|
2232
|
+
if (c.renderer == 'webgpu') {
|
|
2233
|
+
q.mouseX -= c.hw;
|
|
2234
|
+
q.mouseY -= c.hh;
|
|
2235
|
+
}
|
|
2236
|
+
} else {
|
|
2237
|
+
q.mouseX = e.clientX;
|
|
2238
|
+
q.mouseY = e.clientY;
|
|
2239
|
+
}
|
|
2283
2240
|
};
|
|
2284
2241
|
$._onmousedown = (e) => {
|
|
2285
2242
|
$._startAudio();
|
|
2286
2243
|
$._updateMouse(e);
|
|
2287
|
-
|
|
2288
|
-
|
|
2244
|
+
q.mouseIsPressed = true;
|
|
2245
|
+
q.mouseButton = mouseBtns[e.button];
|
|
2289
2246
|
$.mousePressed(e);
|
|
2290
2247
|
};
|
|
2291
2248
|
$._onmousemove = (e) => {
|
|
@@ -2295,18 +2252,15 @@ Q5.modules.input = ($, p) => {
|
|
|
2295
2252
|
};
|
|
2296
2253
|
$._onmouseup = (e) => {
|
|
2297
2254
|
$._updateMouse(e);
|
|
2298
|
-
|
|
2255
|
+
q.mouseIsPressed = false;
|
|
2299
2256
|
$.mouseReleased(e);
|
|
2300
2257
|
};
|
|
2301
2258
|
$._onclick = (e) => {
|
|
2302
2259
|
$._updateMouse(e);
|
|
2303
|
-
|
|
2260
|
+
q.mouseIsPressed = true;
|
|
2304
2261
|
$.mouseClicked(e);
|
|
2305
|
-
|
|
2262
|
+
q.mouseIsPressed = false;
|
|
2306
2263
|
};
|
|
2307
|
-
c.addEventListener('mousedown', (e) => $._onmousedown(e));
|
|
2308
|
-
c.addEventListener('mouseup', (e) => $._onmouseup(e));
|
|
2309
|
-
c.addEventListener('click', (e) => $._onclick(e));
|
|
2310
2264
|
|
|
2311
2265
|
$.cursor = (name, x, y) => {
|
|
2312
2266
|
let pfx = '';
|
|
@@ -2328,17 +2282,17 @@ Q5.modules.input = ($, p) => {
|
|
|
2328
2282
|
$._onkeydown = (e) => {
|
|
2329
2283
|
if (e.repeat) return;
|
|
2330
2284
|
$._startAudio();
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2285
|
+
q.keyIsPressed = true;
|
|
2286
|
+
q.key = e.key;
|
|
2287
|
+
q.keyCode = e.keyCode;
|
|
2334
2288
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = true;
|
|
2335
2289
|
$.keyPressed(e);
|
|
2336
2290
|
if (e.key.length == 1) $.keyTyped(e);
|
|
2337
2291
|
};
|
|
2338
2292
|
$._onkeyup = (e) => {
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2293
|
+
q.keyIsPressed = false;
|
|
2294
|
+
q.key = e.key;
|
|
2295
|
+
q.keyCode = e.keyCode;
|
|
2342
2296
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
|
|
2343
2297
|
$.keyReleased(e);
|
|
2344
2298
|
};
|
|
@@ -2356,37 +2310,43 @@ Q5.modules.input = ($, p) => {
|
|
|
2356
2310
|
}
|
|
2357
2311
|
$._ontouchstart = (e) => {
|
|
2358
2312
|
$._startAudio();
|
|
2359
|
-
|
|
2313
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2360
2314
|
if (!$._isTouchAware) {
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2315
|
+
q.mouseX = $.touches[0].x;
|
|
2316
|
+
q.mouseY = $.touches[0].y;
|
|
2317
|
+
q.mouseIsPressed = true;
|
|
2318
|
+
q.mouseButton = $.LEFT;
|
|
2365
2319
|
if (!$.mousePressed(e)) e.preventDefault();
|
|
2366
2320
|
}
|
|
2367
2321
|
if (!$.touchStarted(e)) e.preventDefault();
|
|
2368
2322
|
};
|
|
2369
2323
|
$._ontouchmove = (e) => {
|
|
2370
|
-
|
|
2324
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2371
2325
|
if (!$._isTouchAware) {
|
|
2372
|
-
|
|
2373
|
-
|
|
2326
|
+
q.mouseX = $.touches[0].x;
|
|
2327
|
+
q.mouseY = $.touches[0].y;
|
|
2374
2328
|
if (!$.mouseDragged(e)) e.preventDefault();
|
|
2375
2329
|
}
|
|
2376
2330
|
if (!$.touchMoved(e)) e.preventDefault();
|
|
2377
2331
|
};
|
|
2378
2332
|
$._ontouchend = (e) => {
|
|
2379
|
-
|
|
2333
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2380
2334
|
if (!$._isTouchAware && !$.touches.length) {
|
|
2381
|
-
|
|
2335
|
+
q.mouseIsPressed = false;
|
|
2382
2336
|
if (!$.mouseReleased(e)) e.preventDefault();
|
|
2383
2337
|
}
|
|
2384
2338
|
if (!$.touchEnded(e)) e.preventDefault();
|
|
2385
2339
|
};
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2340
|
+
|
|
2341
|
+
if (c) {
|
|
2342
|
+
c.addEventListener('mousedown', (e) => $._onmousedown(e));
|
|
2343
|
+
c.addEventListener('mouseup', (e) => $._onmouseup(e));
|
|
2344
|
+
c.addEventListener('click', (e) => $._onclick(e));
|
|
2345
|
+
c.addEventListener('touchstart', (e) => $._ontouchstart(e));
|
|
2346
|
+
c.addEventListener('touchmove', (e) => $._ontouchmove(e));
|
|
2347
|
+
c.addEventListener('touchcancel', (e) => $._ontouchend(e));
|
|
2348
|
+
c.addEventListener('touchend', (e) => $._ontouchend(e));
|
|
2349
|
+
}
|
|
2390
2350
|
|
|
2391
2351
|
if (window) {
|
|
2392
2352
|
let l = window.addEventListener;
|
|
@@ -2395,7 +2355,7 @@ Q5.modules.input = ($, p) => {
|
|
|
2395
2355
|
l('keyup', (e) => $._onkeyup(e), false);
|
|
2396
2356
|
}
|
|
2397
2357
|
};
|
|
2398
|
-
Q5.modules.math = ($,
|
|
2358
|
+
Q5.modules.math = ($, q) => {
|
|
2399
2359
|
$.DEGREES = 'degrees';
|
|
2400
2360
|
$.RADIANS = 'radians';
|
|
2401
2361
|
|
|
@@ -2424,7 +2384,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2424
2384
|
$.degrees = (x) => x * $._RADTODEG;
|
|
2425
2385
|
$.radians = (x) => x * $._DEGTORAD;
|
|
2426
2386
|
|
|
2427
|
-
$.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2387
|
+
$.map = Q5.prototype.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2428
2388
|
let val = ostart + (ostop - ostart) * (((value - istart) * 1.0) / (istop - istart));
|
|
2429
2389
|
if (!clamp) {
|
|
2430
2390
|
return val;
|
|
@@ -2682,7 +2642,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2682
2642
|
let _noise;
|
|
2683
2643
|
|
|
2684
2644
|
$.noiseMode = (mode) => {
|
|
2685
|
-
|
|
2645
|
+
q.Noise = Q5[mode[0].toUpperCase() + mode.slice(1) + 'Noise'];
|
|
2686
2646
|
_noise = null;
|
|
2687
2647
|
};
|
|
2688
2648
|
$.noiseSeed = (seed) => {
|
|
@@ -2815,14 +2775,15 @@ Q5.PerlinNoise = class extends Q5.Noise {
|
|
|
2815
2775
|
return (total / maxAmp + 1) / 2;
|
|
2816
2776
|
}
|
|
2817
2777
|
};
|
|
2818
|
-
Q5.modules.sound = ($,
|
|
2778
|
+
Q5.modules.sound = ($, q) => {
|
|
2819
2779
|
$.Sound = Q5.Sound;
|
|
2820
2780
|
$.loadSound = (path, cb) => {
|
|
2821
|
-
|
|
2781
|
+
q._preloadCount++;
|
|
2822
2782
|
Q5.aud ??= new window.AudioContext();
|
|
2823
2783
|
let a = new Q5.Sound(path, cb);
|
|
2824
2784
|
a.addEventListener('canplaythrough', () => {
|
|
2825
|
-
|
|
2785
|
+
q._preloadCount--;
|
|
2786
|
+
a.loaded = true;
|
|
2826
2787
|
if (cb) cb(a);
|
|
2827
2788
|
});
|
|
2828
2789
|
return a;
|
|
@@ -2854,10 +2815,16 @@ Q5.Sound = class extends Audio {
|
|
|
2854
2815
|
setPan(value) {
|
|
2855
2816
|
this.pan = value;
|
|
2856
2817
|
}
|
|
2818
|
+
isLoaded() {
|
|
2819
|
+
return this.loaded;
|
|
2820
|
+
}
|
|
2821
|
+
isPlaying() {
|
|
2822
|
+
return !this.paused;
|
|
2823
|
+
}
|
|
2857
2824
|
};
|
|
2858
|
-
Q5.modules.util = ($,
|
|
2825
|
+
Q5.modules.util = ($, q) => {
|
|
2859
2826
|
$._loadFile = (path, cb, type) => {
|
|
2860
|
-
|
|
2827
|
+
q._preloadCount++;
|
|
2861
2828
|
let ret = {};
|
|
2862
2829
|
fetch(path)
|
|
2863
2830
|
.then((r) => {
|
|
@@ -2865,7 +2832,7 @@ Q5.modules.util = ($, p) => {
|
|
|
2865
2832
|
if (type == 'text') return r.text();
|
|
2866
2833
|
})
|
|
2867
2834
|
.then((r) => {
|
|
2868
|
-
|
|
2835
|
+
q._preloadCount--;
|
|
2869
2836
|
Object.assign(ret, r);
|
|
2870
2837
|
if (cb) cb(r);
|
|
2871
2838
|
});
|
|
@@ -2910,7 +2877,7 @@ Q5.Vector = class {
|
|
|
2910
2877
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
2911
2878
|
}
|
|
2912
2879
|
_arg2v(x, y, z) {
|
|
2913
|
-
if (x
|
|
2880
|
+
if (x?.x !== undefined) return x;
|
|
2914
2881
|
if (y !== undefined) {
|
|
2915
2882
|
return { x, y, z: z || 0 };
|
|
2916
2883
|
}
|