q5 2.0.17 → 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 +8 -5
- package/package.json +5 -4
- package/q5-webgpu.js +3308 -0
- package/q5-webgpu.min.js +8 -0
- package/q5.js +531 -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 +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 +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,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 ($.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
|
};
|
|
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,225 @@ 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
|
+
$.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);
|
|
461
554
|
|
|
462
555
|
// DRAWING MATRIX
|
|
463
556
|
|
|
@@ -482,7 +575,7 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
482
575
|
$.shearY = (ang) => $.ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
|
|
483
576
|
$.resetMatrix = () => {
|
|
484
577
|
$.ctx.resetTransform();
|
|
485
|
-
$.
|
|
578
|
+
$.scale($._pixelDensity);
|
|
486
579
|
};
|
|
487
580
|
|
|
488
581
|
$._styleNames = [
|
|
@@ -536,29 +629,20 @@ Q5.modules.q2d_canvas = ($, p) => {
|
|
|
536
629
|
opt ??= {};
|
|
537
630
|
opt.alpha ??= true;
|
|
538
631
|
opt.colorSpace ??= $.canvas.colorSpace;
|
|
539
|
-
g.
|
|
632
|
+
g.createCanvas.call($, w, h, opt);
|
|
540
633
|
return g;
|
|
541
634
|
};
|
|
542
635
|
|
|
543
636
|
if (window && $._scope != 'graphics') {
|
|
544
637
|
window.addEventListener('resize', () => {
|
|
545
638
|
$._shouldResize = true;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
639
|
+
q.windowWidth = window.innerWidth;
|
|
640
|
+
q.windowHeight = window.innerHeight;
|
|
641
|
+
q.deviceOrientation = window.screen?.orientation?.type;
|
|
549
642
|
});
|
|
550
643
|
}
|
|
551
644
|
};
|
|
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 = ($) => {
|
|
645
|
+
Q5.renderers.q2d.drawing = ($) => {
|
|
562
646
|
$.CHORD = 0;
|
|
563
647
|
$.PIE = 1;
|
|
564
648
|
$.OPEN = 2;
|
|
@@ -620,33 +704,6 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
620
704
|
|
|
621
705
|
// DRAWING SETTINGS
|
|
622
706
|
|
|
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
707
|
$.blendMode = (x) => ($.ctx.globalCompositeOperation = x);
|
|
651
708
|
$.strokeCap = (x) => ($.ctx.lineCap = x);
|
|
652
709
|
$.strokeJoin = (x) => ($.ctx.lineJoin = x);
|
|
@@ -849,22 +906,18 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
849
906
|
return $.rect(x, y, s, s, tl, tr, br, bl);
|
|
850
907
|
};
|
|
851
908
|
|
|
852
|
-
function clearBuff() {
|
|
853
|
-
curveBuff = [];
|
|
854
|
-
}
|
|
855
|
-
|
|
856
909
|
$.beginShape = () => {
|
|
857
|
-
|
|
910
|
+
curveBuff = [];
|
|
858
911
|
$.ctx.beginPath();
|
|
859
912
|
firstVertex = true;
|
|
860
913
|
};
|
|
861
914
|
$.beginContour = () => {
|
|
862
915
|
$.ctx.closePath();
|
|
863
|
-
|
|
916
|
+
curveBuff = [];
|
|
864
917
|
firstVertex = true;
|
|
865
918
|
};
|
|
866
919
|
$.endContour = () => {
|
|
867
|
-
|
|
920
|
+
curveBuff = [];
|
|
868
921
|
firstVertex = true;
|
|
869
922
|
};
|
|
870
923
|
$.vertex = (x, y) => {
|
|
@@ -872,7 +925,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
872
925
|
x *= $._da;
|
|
873
926
|
y *= $._da;
|
|
874
927
|
}
|
|
875
|
-
|
|
928
|
+
curveBuff = [];
|
|
876
929
|
if (firstVertex) {
|
|
877
930
|
$.ctx.moveTo(x, y);
|
|
878
931
|
} else {
|
|
@@ -889,7 +942,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
889
942
|
x *= $._da;
|
|
890
943
|
y *= $._da;
|
|
891
944
|
}
|
|
892
|
-
|
|
945
|
+
curveBuff = [];
|
|
893
946
|
$.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
894
947
|
};
|
|
895
948
|
$.quadraticVertex = (cp1x, cp1y, x, y) => {
|
|
@@ -899,7 +952,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
899
952
|
x *= $._da;
|
|
900
953
|
y *= $._da;
|
|
901
954
|
}
|
|
902
|
-
|
|
955
|
+
curveBuff = [];
|
|
903
956
|
$.ctx.quadraticCurveTo(cp1x, cp1y, x, y);
|
|
904
957
|
};
|
|
905
958
|
$.bezier = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
@@ -924,7 +977,7 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
924
977
|
$.endShape($.CLOSE);
|
|
925
978
|
};
|
|
926
979
|
$.endShape = (close) => {
|
|
927
|
-
|
|
980
|
+
curveBuff = [];
|
|
928
981
|
if (close) $.ctx.closePath();
|
|
929
982
|
ink();
|
|
930
983
|
};
|
|
@@ -1076,7 +1129,32 @@ Q5.modules.q2d_drawing = ($) => {
|
|
|
1076
1129
|
return $.ctx.isPointInStroke(x * pd, y * pd);
|
|
1077
1130
|
};
|
|
1078
1131
|
};
|
|
1079
|
-
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
|
+
|
|
1080
1158
|
$.createImage = (w, h, opt) => {
|
|
1081
1159
|
opt ??= {};
|
|
1082
1160
|
opt.alpha ??= true;
|
|
@@ -1084,115 +1162,145 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1084
1162
|
return new Q5.Image(w, h, opt);
|
|
1085
1163
|
};
|
|
1086
1164
|
|
|
1087
|
-
$.
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
function makeTmpCtx(w, h) {
|
|
1093
|
-
h ??= w || $.canvas.height;
|
|
1094
|
-
w ??= $.canvas.width;
|
|
1095
|
-
if (tmpCtx == null) {
|
|
1096
|
-
tmpCtx = new $._OffscreenCanvas(w, h).getContext('2d', {
|
|
1097
|
-
colorSpace: $.canvas.colorSpace
|
|
1098
|
-
});
|
|
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.`);
|
|
1099
1169
|
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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);
|
|
1103
1181
|
}
|
|
1104
|
-
}
|
|
1105
1182
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
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
|
+
};
|
|
1113
1200
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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;
|
|
1117
1209
|
}
|
|
1118
|
-
|
|
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;
|
|
1119
1245
|
|
|
1120
1246
|
$._softFilter = () => {
|
|
1121
|
-
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.');
|
|
1122
1248
|
};
|
|
1123
1249
|
|
|
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
|
-
}
|
|
1250
|
+
$.filter = (type, x) => {
|
|
1251
|
+
if (!$.ctx.filter) return $._softFilter(type, x);
|
|
1134
1252
|
|
|
1135
|
-
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1140
|
-
|
|
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) {
|
|
1141
1260
|
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
|
-
};
|
|
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);
|
|
1163
1264
|
|
|
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();
|
|
1265
|
+
$.ctx.filter = f;
|
|
1266
|
+
$.ctx.drawImage($.canvas, 0, 0, $.canvas.w, $.canvas.h);
|
|
1267
|
+
$.ctx.filter = 'none';
|
|
1176
1268
|
};
|
|
1177
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
|
+
|
|
1178
1284
|
$.trim = () => {
|
|
1179
1285
|
let pd = $._pixelDensity || 1;
|
|
1180
|
-
let
|
|
1181
|
-
let
|
|
1182
|
-
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,
|
|
1183
1290
|
right = 0,
|
|
1184
|
-
top =
|
|
1291
|
+
top = h,
|
|
1185
1292
|
bottom = 0;
|
|
1186
1293
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
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) {
|
|
1191
1298
|
if (x < left) left = x;
|
|
1192
1299
|
if (x > right) right = x;
|
|
1193
1300
|
if (y < top) top = y;
|
|
1194
1301
|
if (y > bottom) bottom = y;
|
|
1195
1302
|
}
|
|
1303
|
+
i += 4;
|
|
1196
1304
|
}
|
|
1197
1305
|
}
|
|
1198
1306
|
top = Math.floor(top / pd);
|
|
@@ -1213,48 +1321,6 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1213
1321
|
$.ctx.restore();
|
|
1214
1322
|
};
|
|
1215
1323
|
|
|
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
1324
|
$.get = (x, y, w, h) => {
|
|
1259
1325
|
let pd = $._pixelDensity || 1;
|
|
1260
1326
|
if (x !== undefined && w === undefined) {
|
|
@@ -1299,174 +1365,23 @@ Q5.modules.q2d_image = ($, p) => {
|
|
|
1299
1365
|
|
|
1300
1366
|
$.loadPixels = () => {
|
|
1301
1367
|
imgData = $.ctx.getImageData(0, 0, $.canvas.width, $.canvas.height);
|
|
1302
|
-
|
|
1368
|
+
q.pixels = imgData.data;
|
|
1303
1369
|
};
|
|
1304
1370
|
$.updatePixels = () => {
|
|
1305
1371
|
if (imgData != null) $.ctx.putImageData(imgData, 0, 0);
|
|
1306
1372
|
};
|
|
1307
1373
|
|
|
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
1374
|
$.smooth = () => ($.ctx.imageSmoothingEnabled = true);
|
|
1340
1375
|
$.noSmooth = () => ($.ctx.imageSmoothingEnabled = false);
|
|
1341
1376
|
|
|
1342
1377
|
if ($._scope == 'image') return;
|
|
1343
1378
|
|
|
1344
|
-
$.saveCanvas = $.canvas.save = $.save;
|
|
1345
|
-
|
|
1346
1379
|
$.tint = function (c) {
|
|
1347
1380
|
$._tint = c._q5Color ? c : $.color(...arguments);
|
|
1348
1381
|
};
|
|
1349
1382
|
$.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
1383
|
};
|
|
1441
1384
|
|
|
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
1385
|
Q5.THRESHOLD = 1;
|
|
1471
1386
|
Q5.GRAY = 2;
|
|
1472
1387
|
Q5.OPAQUE = 3;
|
|
@@ -1476,12 +1391,12 @@ Q5.DILATE = 6;
|
|
|
1476
1391
|
Q5.ERODE = 7;
|
|
1477
1392
|
Q5.BLUR = 8;
|
|
1478
1393
|
/* software implementation of image filters */
|
|
1479
|
-
Q5.
|
|
1394
|
+
Q5.renderers.q2d.soft_filters = ($) => {
|
|
1480
1395
|
let tmpBuf = null;
|
|
1481
1396
|
|
|
1482
|
-
function
|
|
1397
|
+
function ensureTmpBuf() {
|
|
1483
1398
|
let l = $.canvas.width * $.canvas.height * 4;
|
|
1484
|
-
if (!tmpBuf ||
|
|
1399
|
+
if (!tmpBuf || tmpBuf.length != l) {
|
|
1485
1400
|
tmpBuf = new Uint8ClampedArray(l);
|
|
1486
1401
|
}
|
|
1487
1402
|
}
|
|
@@ -1523,7 +1438,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1523
1438
|
}
|
|
1524
1439
|
};
|
|
1525
1440
|
$._filters[Q5.DILATE] = (data) => {
|
|
1526
|
-
|
|
1441
|
+
ensureTmpBuf();
|
|
1527
1442
|
tmpBuf.set(data);
|
|
1528
1443
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1529
1444
|
for (let i = 0; i < h; i++) {
|
|
@@ -1550,7 +1465,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1550
1465
|
}
|
|
1551
1466
|
};
|
|
1552
1467
|
$._filters[Q5.ERODE] = (data) => {
|
|
1553
|
-
|
|
1468
|
+
ensureTmpBuf();
|
|
1554
1469
|
tmpBuf.set(data);
|
|
1555
1470
|
let [w, h] = [$.canvas.width, $.canvas.height];
|
|
1556
1471
|
for (let i = 0; i < h; i++) {
|
|
@@ -1579,7 +1494,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1579
1494
|
$._filters[Q5.BLUR] = (data, rad) => {
|
|
1580
1495
|
rad = rad || 1;
|
|
1581
1496
|
rad = Math.floor(rad * $._pixelDensity);
|
|
1582
|
-
|
|
1497
|
+
ensureTmpBuf();
|
|
1583
1498
|
tmpBuf.set(data);
|
|
1584
1499
|
|
|
1585
1500
|
let ksize = rad * 2 + 1;
|
|
@@ -1651,7 +1566,7 @@ Q5.modules.q2d_soft_filters = ($) => {
|
|
|
1651
1566
|
$.ctx.putImageData(imgData, 0, 0);
|
|
1652
1567
|
};
|
|
1653
1568
|
};
|
|
1654
|
-
Q5.
|
|
1569
|
+
Q5.renderers.q2d.text = ($, q) => {
|
|
1655
1570
|
$.NORMAL = 'normal';
|
|
1656
1571
|
$.ITALIC = 'italic';
|
|
1657
1572
|
$.BOLD = 'bold';
|
|
@@ -1671,12 +1586,12 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1671
1586
|
$._textStyle = 'normal';
|
|
1672
1587
|
|
|
1673
1588
|
$.loadFont = (url, cb) => {
|
|
1674
|
-
|
|
1589
|
+
q._preloadCount++;
|
|
1675
1590
|
let name = url.split('/').pop().split('.')[0].replace(' ', '');
|
|
1676
1591
|
let f = new FontFace(name, `url(${url})`);
|
|
1677
1592
|
document.fonts.add(f);
|
|
1678
1593
|
f.load().then(() => {
|
|
1679
|
-
|
|
1594
|
+
q._preloadCount--;
|
|
1680
1595
|
if (cb) cb(name);
|
|
1681
1596
|
});
|
|
1682
1597
|
return name;
|
|
@@ -1865,8 +1780,8 @@ Q5.modules.q2d_text = ($, p) => {
|
|
|
1865
1780
|
};
|
|
1866
1781
|
};
|
|
1867
1782
|
Q5.modules.ai = ($) => {
|
|
1868
|
-
$.askAI = (
|
|
1869
|
-
throw Error('Ask AI ✨ ' +
|
|
1783
|
+
$.askAI = (question = '') => {
|
|
1784
|
+
throw Error('Ask AI ✨ ' + question);
|
|
1870
1785
|
};
|
|
1871
1786
|
|
|
1872
1787
|
$._aiErrorAssistance = async (e) => {
|
|
@@ -1929,22 +1844,24 @@ Q5.modules.ai = ($) => {
|
|
|
1929
1844
|
} catch (err) {}
|
|
1930
1845
|
};
|
|
1931
1846
|
};
|
|
1932
|
-
Q5.modules.color = ($,
|
|
1847
|
+
Q5.modules.color = ($, q) => {
|
|
1933
1848
|
$.RGB = $.RGBA = $._colorMode = 'rgb';
|
|
1934
1849
|
$.OKLCH = 'oklch';
|
|
1935
1850
|
|
|
1936
|
-
|
|
1937
|
-
else $.Color = Q5.ColorRGBA;
|
|
1938
|
-
|
|
1939
|
-
$.colorMode = (mode) => {
|
|
1851
|
+
$.colorMode = (mode, format) => {
|
|
1940
1852
|
$._colorMode = mode;
|
|
1853
|
+
let srgb = $.canvas.colorSpace == 'srgb' || mode == 'srgb';
|
|
1854
|
+
format ??= srgb ? 'integer' : 'float';
|
|
1855
|
+
$._colorFormat = format;
|
|
1941
1856
|
if (mode == 'oklch') {
|
|
1942
|
-
|
|
1943
|
-
} else
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
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
|
+
}
|
|
1948
1865
|
$._colorMode = 'rgb';
|
|
1949
1866
|
}
|
|
1950
1867
|
};
|
|
@@ -1989,27 +1906,33 @@ Q5.modules.color = ($, p) => {
|
|
|
1989
1906
|
let args = arguments;
|
|
1990
1907
|
if (args.length == 1) {
|
|
1991
1908
|
if (typeof c0 == 'string') {
|
|
1909
|
+
let r, g, b, a;
|
|
1992
1910
|
if (c0[0] == '#') {
|
|
1993
1911
|
if (c0.length <= 5) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
);
|
|
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);
|
|
2000
1921
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
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.'
|
|
2006
1926
|
);
|
|
1927
|
+
return new C(0, 0, 0);
|
|
2007
1928
|
}
|
|
2008
|
-
if ($.
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
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);
|
|
2013
1936
|
}
|
|
2014
1937
|
if (Array.isArray(c0)) return new C(...c0);
|
|
2015
1938
|
}
|
|
@@ -2079,7 +2002,24 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2079
2002
|
this.r = r;
|
|
2080
2003
|
this.g = g;
|
|
2081
2004
|
this.b = b;
|
|
2082
|
-
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);
|
|
2083
2023
|
}
|
|
2084
2024
|
setRed(v) {
|
|
2085
2025
|
this.r = v;
|
|
@@ -2100,9 +2040,10 @@ Q5.ColorRGBA = class extends Q5.Color {
|
|
|
2100
2040
|
return `rgb(${this.r} ${this.g} ${this.b} / ${this.a / 255})`;
|
|
2101
2041
|
}
|
|
2102
2042
|
};
|
|
2103
|
-
|
|
2043
|
+
// p3 10-bit color in integer color format, for backwards compatibility
|
|
2044
|
+
Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
|
|
2104
2045
|
constructor(r, g, b, a) {
|
|
2105
|
-
super(r, g, b, a);
|
|
2046
|
+
super(r, g, b, a ?? 255);
|
|
2106
2047
|
this._edited = true;
|
|
2107
2048
|
}
|
|
2108
2049
|
get r() {
|
|
@@ -2231,8 +2172,14 @@ main {
|
|
|
2231
2172
|
Object.assign(c, { displayMode, renderQuality, displayScale });
|
|
2232
2173
|
$._adjustDisplay();
|
|
2233
2174
|
};
|
|
2175
|
+
|
|
2176
|
+
$.fullscreen = (v) => {
|
|
2177
|
+
if (v === undefined) return document.fullscreenElement;
|
|
2178
|
+
if (v) document.body.requestFullscreen();
|
|
2179
|
+
else document.body.exitFullscreen();
|
|
2180
|
+
};
|
|
2234
2181
|
};
|
|
2235
|
-
Q5.modules.input = ($,
|
|
2182
|
+
Q5.modules.input = ($, q) => {
|
|
2236
2183
|
if ($._scope == 'graphics') return;
|
|
2237
2184
|
|
|
2238
2185
|
$.mouseX = 0;
|
|
@@ -2276,17 +2223,26 @@ Q5.modules.input = ($, p) => {
|
|
|
2276
2223
|
|
|
2277
2224
|
$._updateMouse = (e) => {
|
|
2278
2225
|
if (e.changedTouches) return;
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
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
|
+
}
|
|
2284
2240
|
};
|
|
2285
2241
|
$._onmousedown = (e) => {
|
|
2286
2242
|
$._startAudio();
|
|
2287
2243
|
$._updateMouse(e);
|
|
2288
|
-
|
|
2289
|
-
|
|
2244
|
+
q.mouseIsPressed = true;
|
|
2245
|
+
q.mouseButton = mouseBtns[e.button];
|
|
2290
2246
|
$.mousePressed(e);
|
|
2291
2247
|
};
|
|
2292
2248
|
$._onmousemove = (e) => {
|
|
@@ -2296,18 +2252,15 @@ Q5.modules.input = ($, p) => {
|
|
|
2296
2252
|
};
|
|
2297
2253
|
$._onmouseup = (e) => {
|
|
2298
2254
|
$._updateMouse(e);
|
|
2299
|
-
|
|
2255
|
+
q.mouseIsPressed = false;
|
|
2300
2256
|
$.mouseReleased(e);
|
|
2301
2257
|
};
|
|
2302
2258
|
$._onclick = (e) => {
|
|
2303
2259
|
$._updateMouse(e);
|
|
2304
|
-
|
|
2260
|
+
q.mouseIsPressed = true;
|
|
2305
2261
|
$.mouseClicked(e);
|
|
2306
|
-
|
|
2262
|
+
q.mouseIsPressed = false;
|
|
2307
2263
|
};
|
|
2308
|
-
c.addEventListener('mousedown', (e) => $._onmousedown(e));
|
|
2309
|
-
c.addEventListener('mouseup', (e) => $._onmouseup(e));
|
|
2310
|
-
c.addEventListener('click', (e) => $._onclick(e));
|
|
2311
2264
|
|
|
2312
2265
|
$.cursor = (name, x, y) => {
|
|
2313
2266
|
let pfx = '';
|
|
@@ -2329,17 +2282,17 @@ Q5.modules.input = ($, p) => {
|
|
|
2329
2282
|
$._onkeydown = (e) => {
|
|
2330
2283
|
if (e.repeat) return;
|
|
2331
2284
|
$._startAudio();
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2285
|
+
q.keyIsPressed = true;
|
|
2286
|
+
q.key = e.key;
|
|
2287
|
+
q.keyCode = e.keyCode;
|
|
2335
2288
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = true;
|
|
2336
2289
|
$.keyPressed(e);
|
|
2337
2290
|
if (e.key.length == 1) $.keyTyped(e);
|
|
2338
2291
|
};
|
|
2339
2292
|
$._onkeyup = (e) => {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2293
|
+
q.keyIsPressed = false;
|
|
2294
|
+
q.key = e.key;
|
|
2295
|
+
q.keyCode = e.keyCode;
|
|
2343
2296
|
keysHeld[$.keyCode] = keysHeld[$.key.toLowerCase()] = false;
|
|
2344
2297
|
$.keyReleased(e);
|
|
2345
2298
|
};
|
|
@@ -2357,37 +2310,43 @@ Q5.modules.input = ($, p) => {
|
|
|
2357
2310
|
}
|
|
2358
2311
|
$._ontouchstart = (e) => {
|
|
2359
2312
|
$._startAudio();
|
|
2360
|
-
|
|
2313
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2361
2314
|
if (!$._isTouchAware) {
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2315
|
+
q.mouseX = $.touches[0].x;
|
|
2316
|
+
q.mouseY = $.touches[0].y;
|
|
2317
|
+
q.mouseIsPressed = true;
|
|
2318
|
+
q.mouseButton = $.LEFT;
|
|
2366
2319
|
if (!$.mousePressed(e)) e.preventDefault();
|
|
2367
2320
|
}
|
|
2368
2321
|
if (!$.touchStarted(e)) e.preventDefault();
|
|
2369
2322
|
};
|
|
2370
2323
|
$._ontouchmove = (e) => {
|
|
2371
|
-
|
|
2324
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2372
2325
|
if (!$._isTouchAware) {
|
|
2373
|
-
|
|
2374
|
-
|
|
2326
|
+
q.mouseX = $.touches[0].x;
|
|
2327
|
+
q.mouseY = $.touches[0].y;
|
|
2375
2328
|
if (!$.mouseDragged(e)) e.preventDefault();
|
|
2376
2329
|
}
|
|
2377
2330
|
if (!$.touchMoved(e)) e.preventDefault();
|
|
2378
2331
|
};
|
|
2379
2332
|
$._ontouchend = (e) => {
|
|
2380
|
-
|
|
2333
|
+
q.touches = [...e.touches].map(getTouchInfo);
|
|
2381
2334
|
if (!$._isTouchAware && !$.touches.length) {
|
|
2382
|
-
|
|
2335
|
+
q.mouseIsPressed = false;
|
|
2383
2336
|
if (!$.mouseReleased(e)) e.preventDefault();
|
|
2384
2337
|
}
|
|
2385
2338
|
if (!$.touchEnded(e)) e.preventDefault();
|
|
2386
2339
|
};
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
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
|
+
}
|
|
2391
2350
|
|
|
2392
2351
|
if (window) {
|
|
2393
2352
|
let l = window.addEventListener;
|
|
@@ -2396,7 +2355,7 @@ Q5.modules.input = ($, p) => {
|
|
|
2396
2355
|
l('keyup', (e) => $._onkeyup(e), false);
|
|
2397
2356
|
}
|
|
2398
2357
|
};
|
|
2399
|
-
Q5.modules.math = ($,
|
|
2358
|
+
Q5.modules.math = ($, q) => {
|
|
2400
2359
|
$.DEGREES = 'degrees';
|
|
2401
2360
|
$.RADIANS = 'radians';
|
|
2402
2361
|
|
|
@@ -2425,7 +2384,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2425
2384
|
$.degrees = (x) => x * $._RADTODEG;
|
|
2426
2385
|
$.radians = (x) => x * $._DEGTORAD;
|
|
2427
2386
|
|
|
2428
|
-
$.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2387
|
+
$.map = Q5.prototype.map = (value, istart, istop, ostart, ostop, clamp) => {
|
|
2429
2388
|
let val = ostart + (ostop - ostart) * (((value - istart) * 1.0) / (istop - istart));
|
|
2430
2389
|
if (!clamp) {
|
|
2431
2390
|
return val;
|
|
@@ -2683,7 +2642,7 @@ Q5.modules.math = ($, p) => {
|
|
|
2683
2642
|
let _noise;
|
|
2684
2643
|
|
|
2685
2644
|
$.noiseMode = (mode) => {
|
|
2686
|
-
|
|
2645
|
+
q.Noise = Q5[mode[0].toUpperCase() + mode.slice(1) + 'Noise'];
|
|
2687
2646
|
_noise = null;
|
|
2688
2647
|
};
|
|
2689
2648
|
$.noiseSeed = (seed) => {
|
|
@@ -2816,14 +2775,15 @@ Q5.PerlinNoise = class extends Q5.Noise {
|
|
|
2816
2775
|
return (total / maxAmp + 1) / 2;
|
|
2817
2776
|
}
|
|
2818
2777
|
};
|
|
2819
|
-
Q5.modules.sound = ($,
|
|
2778
|
+
Q5.modules.sound = ($, q) => {
|
|
2820
2779
|
$.Sound = Q5.Sound;
|
|
2821
2780
|
$.loadSound = (path, cb) => {
|
|
2822
|
-
|
|
2781
|
+
q._preloadCount++;
|
|
2823
2782
|
Q5.aud ??= new window.AudioContext();
|
|
2824
2783
|
let a = new Q5.Sound(path, cb);
|
|
2825
2784
|
a.addEventListener('canplaythrough', () => {
|
|
2826
|
-
|
|
2785
|
+
q._preloadCount--;
|
|
2786
|
+
a.loaded = true;
|
|
2827
2787
|
if (cb) cb(a);
|
|
2828
2788
|
});
|
|
2829
2789
|
return a;
|
|
@@ -2855,10 +2815,16 @@ Q5.Sound = class extends Audio {
|
|
|
2855
2815
|
setPan(value) {
|
|
2856
2816
|
this.pan = value;
|
|
2857
2817
|
}
|
|
2818
|
+
isLoaded() {
|
|
2819
|
+
return this.loaded;
|
|
2820
|
+
}
|
|
2821
|
+
isPlaying() {
|
|
2822
|
+
return !this.paused;
|
|
2823
|
+
}
|
|
2858
2824
|
};
|
|
2859
|
-
Q5.modules.util = ($,
|
|
2825
|
+
Q5.modules.util = ($, q) => {
|
|
2860
2826
|
$._loadFile = (path, cb, type) => {
|
|
2861
|
-
|
|
2827
|
+
q._preloadCount++;
|
|
2862
2828
|
let ret = {};
|
|
2863
2829
|
fetch(path)
|
|
2864
2830
|
.then((r) => {
|
|
@@ -2866,7 +2832,7 @@ Q5.modules.util = ($, p) => {
|
|
|
2866
2832
|
if (type == 'text') return r.text();
|
|
2867
2833
|
})
|
|
2868
2834
|
.then((r) => {
|
|
2869
|
-
|
|
2835
|
+
q._preloadCount--;
|
|
2870
2836
|
Object.assign(ret, r);
|
|
2871
2837
|
if (cb) cb(r);
|
|
2872
2838
|
});
|
|
@@ -2911,7 +2877,7 @@ Q5.Vector = class {
|
|
|
2911
2877
|
return new Q5.Vector(this.x, this.y, this.z);
|
|
2912
2878
|
}
|
|
2913
2879
|
_arg2v(x, y, z) {
|
|
2914
|
-
if (x
|
|
2880
|
+
if (x?.x !== undefined) return x;
|
|
2915
2881
|
if (y !== undefined) {
|
|
2916
2882
|
return { x, y, z: z || 0 };
|
|
2917
2883
|
}
|