q5 2.5.5 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/q5.js +600 -518
- package/q5.min.js +2 -2
- package/src/q5-2d-canvas.js +2 -4
- package/src/q5-core.js +2 -2
- package/src/q5-webgpu-canvas.js +176 -44
- package/src/q5-webgpu-drawing.js +298 -291
- package/src/q5-webgpu-image.js +25 -38
- package/src/q5-webgpu-text.js +95 -137
- package/src/readme.md +8 -18
package/q5.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* q5.js
|
|
3
|
-
* @version 2.
|
|
3
|
+
* @version 2.6
|
|
4
4
|
* @author quinton-ashley, Tezumie, and LingDong-
|
|
5
5
|
* @license LGPL-3.0
|
|
6
6
|
* @class Q5
|
|
@@ -259,7 +259,7 @@ Q5._nodejs = typeof process == 'object';
|
|
|
259
259
|
|
|
260
260
|
Q5._instanceCount = 0;
|
|
261
261
|
Q5._friendlyError = (msg, func) => {
|
|
262
|
-
|
|
262
|
+
console.error(func + ': ' + msg);
|
|
263
263
|
};
|
|
264
264
|
Q5._validateParameters = () => true;
|
|
265
265
|
|
|
@@ -635,8 +635,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
635
635
|
};
|
|
636
636
|
|
|
637
637
|
$.fill = function (c) {
|
|
638
|
-
$._doFill = true;
|
|
639
|
-
$._fillSet = true;
|
|
638
|
+
$._doFill = $._fillSet = true;
|
|
640
639
|
if (Q5.Color) {
|
|
641
640
|
if (!c._q5Color) {
|
|
642
641
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -648,8 +647,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
648
647
|
};
|
|
649
648
|
$.noFill = () => ($._doFill = false);
|
|
650
649
|
$.stroke = function (c) {
|
|
651
|
-
$._doStroke = true;
|
|
652
|
-
$._strokeSet = true;
|
|
650
|
+
$._doStroke = $._strokeSet = true;
|
|
653
651
|
if (Q5.Color) {
|
|
654
652
|
if (!c._q5Color) {
|
|
655
653
|
if (typeof c != 'string') c = $.color(...arguments);
|
|
@@ -3075,18 +3073,30 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3075
3073
|
c.width = $.width = 500;
|
|
3076
3074
|
c.height = $.height = 500;
|
|
3077
3075
|
|
|
3078
|
-
if ($.colorMode) $.colorMode('rgb',
|
|
3076
|
+
if ($.colorMode) $.colorMode('rgb', 1);
|
|
3079
3077
|
|
|
3080
|
-
let pass
|
|
3078
|
+
let pass,
|
|
3079
|
+
mainView,
|
|
3080
|
+
colorsLayout,
|
|
3081
|
+
colorIndex = 1,
|
|
3082
|
+
colorStackIndex = 8;
|
|
3081
3083
|
|
|
3082
|
-
$.
|
|
3084
|
+
$._pipelineConfigs = [];
|
|
3085
|
+
$._pipelines = [];
|
|
3083
3086
|
|
|
3084
3087
|
// local variables used for slightly better performance
|
|
3085
3088
|
// stores pipeline shifts and vertex counts/image indices
|
|
3086
3089
|
let drawStack = ($.drawStack = []);
|
|
3087
3090
|
|
|
3088
3091
|
// colors used for each draw call
|
|
3089
|
-
|
|
3092
|
+
|
|
3093
|
+
let colorStack = ($.colorStack = new Float32Array(1e6));
|
|
3094
|
+
|
|
3095
|
+
// prettier-ignore
|
|
3096
|
+
colorStack.set([
|
|
3097
|
+
0, 0, 0, 1, // black
|
|
3098
|
+
1, 1, 1, 1 // white
|
|
3099
|
+
]);
|
|
3090
3100
|
|
|
3091
3101
|
$._transformLayout = Q5.device.createBindGroupLayout({
|
|
3092
3102
|
label: 'transformLayout',
|
|
@@ -3110,32 +3120,70 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3110
3120
|
]
|
|
3111
3121
|
});
|
|
3112
3122
|
|
|
3113
|
-
|
|
3123
|
+
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3124
|
+
label: 'colorsLayout',
|
|
3125
|
+
entries: [
|
|
3126
|
+
{
|
|
3127
|
+
binding: 0,
|
|
3128
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
3129
|
+
buffer: {
|
|
3130
|
+
type: 'read-only-storage',
|
|
3131
|
+
hasDynamicOffset: false
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
]
|
|
3135
|
+
});
|
|
3136
|
+
|
|
3137
|
+
$.bindGroupLayouts = [$._transformLayout, colorsLayout];
|
|
3114
3138
|
|
|
3115
3139
|
let uniformBuffer = Q5.device.createBuffer({
|
|
3116
3140
|
size: 8, // Size of two floats
|
|
3117
3141
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
3118
3142
|
});
|
|
3119
3143
|
|
|
3144
|
+
let createMainView = () => {
|
|
3145
|
+
mainView = Q5.device
|
|
3146
|
+
.createTexture({
|
|
3147
|
+
size: [$.canvas.width, $.canvas.height],
|
|
3148
|
+
sampleCount: 4,
|
|
3149
|
+
format: 'bgra8unorm',
|
|
3150
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT
|
|
3151
|
+
})
|
|
3152
|
+
.createView();
|
|
3153
|
+
};
|
|
3154
|
+
|
|
3120
3155
|
$._createCanvas = (w, h, opt) => {
|
|
3121
3156
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
3122
3157
|
|
|
3123
3158
|
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
3124
3159
|
opt.device ??= Q5.device;
|
|
3125
3160
|
|
|
3161
|
+
// needed for other blend modes but couldn't get it working
|
|
3162
|
+
// opt.alphaMode = 'premultiplied';
|
|
3163
|
+
|
|
3126
3164
|
$.ctx.configure(opt);
|
|
3127
3165
|
|
|
3128
3166
|
Q5.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([$.canvas.hw, $.canvas.hh]));
|
|
3129
3167
|
|
|
3168
|
+
createMainView();
|
|
3169
|
+
|
|
3130
3170
|
return c;
|
|
3131
3171
|
};
|
|
3132
3172
|
|
|
3133
3173
|
$._resizeCanvas = (w, h) => {
|
|
3134
3174
|
$._setCanvasSize(w, h);
|
|
3175
|
+
createMainView();
|
|
3176
|
+
};
|
|
3177
|
+
|
|
3178
|
+
$.pixelDensity = (v) => {
|
|
3179
|
+
if (!v || v == $._pixelDensity) return $._pixelDensity;
|
|
3180
|
+
$._pixelDensity = v;
|
|
3181
|
+
$._setCanvasSize(c.w, c.h);
|
|
3182
|
+
createMainView();
|
|
3183
|
+
return v;
|
|
3135
3184
|
};
|
|
3136
3185
|
|
|
3137
3186
|
// current color index, used to associate a vertex with a color
|
|
3138
|
-
let colorIndex = 0;
|
|
3139
3187
|
let addColor = (r, g, b, a = 1) => {
|
|
3140
3188
|
if (typeof r == 'string') r = $.color(r);
|
|
3141
3189
|
else if (b == undefined) {
|
|
@@ -3143,21 +3191,35 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3143
3191
|
a = g ?? 1;
|
|
3144
3192
|
g = b = r;
|
|
3145
3193
|
}
|
|
3146
|
-
if (r._q5Color)
|
|
3147
|
-
|
|
3194
|
+
if (r._q5Color) {
|
|
3195
|
+
a = r.a;
|
|
3196
|
+
b = r.b;
|
|
3197
|
+
g = r.g;
|
|
3198
|
+
r = r.r;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
let cs = colorStack,
|
|
3202
|
+
i = colorStackIndex;
|
|
3203
|
+
cs[i++] = r;
|
|
3204
|
+
cs[i++] = g;
|
|
3205
|
+
cs[i++] = b;
|
|
3206
|
+
cs[i++] = a;
|
|
3207
|
+
colorStackIndex = i;
|
|
3208
|
+
|
|
3148
3209
|
colorIndex++;
|
|
3149
3210
|
};
|
|
3150
3211
|
|
|
3151
|
-
$._fillIndex = $._strokeIndex =
|
|
3212
|
+
$._fillIndex = $._strokeIndex = 0;
|
|
3213
|
+
$._doFill = $._doStroke = true;
|
|
3152
3214
|
|
|
3153
3215
|
$.fill = (r, g, b, a) => {
|
|
3154
3216
|
addColor(r, g, b, a);
|
|
3155
|
-
$._doFill = true;
|
|
3217
|
+
$._doFill = $._fillSet = true;
|
|
3156
3218
|
$._fillIndex = colorIndex;
|
|
3157
3219
|
};
|
|
3158
3220
|
$.stroke = (r, g, b, a) => {
|
|
3159
3221
|
addColor(r, g, b, a);
|
|
3160
|
-
$._doStroke = true;
|
|
3222
|
+
$._doStroke = $._strokeSet = true;
|
|
3161
3223
|
$._strokeIndex = colorIndex;
|
|
3162
3224
|
};
|
|
3163
3225
|
|
|
@@ -3359,6 +3421,68 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3359
3421
|
return [l, r, t, b];
|
|
3360
3422
|
};
|
|
3361
3423
|
|
|
3424
|
+
// prettier-ignore
|
|
3425
|
+
let blendFactors = [
|
|
3426
|
+
'zero', // 0
|
|
3427
|
+
'one', // 1
|
|
3428
|
+
'src-alpha', // 2
|
|
3429
|
+
'one-minus-src-alpha', // 3
|
|
3430
|
+
'dst', // 4
|
|
3431
|
+
'dst-alpha', // 5
|
|
3432
|
+
'one-minus-dst-alpha', // 6
|
|
3433
|
+
'one-minus-src' // 7
|
|
3434
|
+
];
|
|
3435
|
+
let blendOps = [
|
|
3436
|
+
'add', // 0
|
|
3437
|
+
'subtract', // 1
|
|
3438
|
+
'reverse-subtract', // 2
|
|
3439
|
+
'min', // 3
|
|
3440
|
+
'max' // 4
|
|
3441
|
+
];
|
|
3442
|
+
|
|
3443
|
+
const blendModes = {
|
|
3444
|
+
normal: [2, 3, 0, 2, 3, 0],
|
|
3445
|
+
// destination_over: [6, 1, 0, 6, 1, 0],
|
|
3446
|
+
additive: [1, 1, 0, 1, 1, 0]
|
|
3447
|
+
// source_in: [5, 0, 0, 5, 0, 0],
|
|
3448
|
+
// destination_in: [0, 2, 0, 0, 2, 0],
|
|
3449
|
+
// source_out: [6, 0, 0, 6, 0, 0],
|
|
3450
|
+
// destination_out: [0, 3, 0, 0, 3, 0],
|
|
3451
|
+
// source_atop: [5, 3, 0, 5, 3, 0],
|
|
3452
|
+
// destination_atop: [6, 2, 0, 6, 2, 0]
|
|
3453
|
+
};
|
|
3454
|
+
|
|
3455
|
+
$.blendConfigs = {};
|
|
3456
|
+
|
|
3457
|
+
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3458
|
+
$.blendConfigs[name] = {
|
|
3459
|
+
color: {
|
|
3460
|
+
srcFactor: blendFactors[mode[0]],
|
|
3461
|
+
dstFactor: blendFactors[mode[1]],
|
|
3462
|
+
operation: blendOps[mode[2]]
|
|
3463
|
+
},
|
|
3464
|
+
alpha: {
|
|
3465
|
+
srcFactor: blendFactors[mode[3]],
|
|
3466
|
+
dstFactor: blendFactors[mode[4]],
|
|
3467
|
+
operation: blendOps[mode[5]]
|
|
3468
|
+
}
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
$._blendMode = 'normal';
|
|
3473
|
+
$.blendMode = (mode) => {
|
|
3474
|
+
if (mode == $._blendMode) return;
|
|
3475
|
+
if (mode == 'source-over') mode = 'normal';
|
|
3476
|
+
if (mode == 'lighter') mode = 'additive';
|
|
3477
|
+
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3478
|
+
$._blendMode = mode;
|
|
3479
|
+
|
|
3480
|
+
for (let i = 0; i < $._pipelines.length; i++) {
|
|
3481
|
+
$._pipelineConfigs[i].fragment.targets[0].blend = $.blendConfigs[mode];
|
|
3482
|
+
$._pipelines[i] = Q5.device.createRenderPipeline($._pipelineConfigs[i]);
|
|
3483
|
+
}
|
|
3484
|
+
};
|
|
3485
|
+
|
|
3362
3486
|
$.clear = () => {};
|
|
3363
3487
|
|
|
3364
3488
|
$._beginRender = () => {
|
|
@@ -3368,7 +3492,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3368
3492
|
label: 'q5-webgpu',
|
|
3369
3493
|
colorAttachments: [
|
|
3370
3494
|
{
|
|
3371
|
-
view:
|
|
3495
|
+
view: mainView,
|
|
3496
|
+
resolveTarget: $.ctx.getCurrentTexture().createView(),
|
|
3372
3497
|
loadOp: 'clear',
|
|
3373
3498
|
storeOp: 'store'
|
|
3374
3499
|
}
|
|
@@ -3379,58 +3504,62 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3379
3504
|
$._render = () => {
|
|
3380
3505
|
if (transformStates.length > 1 || !$._transformBindGroup) {
|
|
3381
3506
|
let transformBuffer = Q5.device.createBuffer({
|
|
3382
|
-
size: transformStates.length * 64, //
|
|
3383
|
-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
3507
|
+
size: transformStates.length * 64, // 64 is the size of 16 floats
|
|
3508
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3509
|
+
mappedAtCreation: true
|
|
3384
3510
|
});
|
|
3385
3511
|
|
|
3386
|
-
|
|
3512
|
+
new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
|
|
3513
|
+
transformBuffer.unmap();
|
|
3387
3514
|
|
|
3388
3515
|
$._transformBindGroup = Q5.device.createBindGroup({
|
|
3389
3516
|
layout: $._transformLayout,
|
|
3390
3517
|
entries: [
|
|
3391
|
-
{
|
|
3392
|
-
|
|
3393
|
-
resource: {
|
|
3394
|
-
buffer: uniformBuffer
|
|
3395
|
-
}
|
|
3396
|
-
},
|
|
3397
|
-
{
|
|
3398
|
-
binding: 1,
|
|
3399
|
-
resource: {
|
|
3400
|
-
buffer: transformBuffer
|
|
3401
|
-
}
|
|
3402
|
-
}
|
|
3518
|
+
{ binding: 0, resource: { buffer: uniformBuffer } },
|
|
3519
|
+
{ binding: 1, resource: { buffer: transformBuffer } }
|
|
3403
3520
|
]
|
|
3404
3521
|
});
|
|
3405
3522
|
}
|
|
3406
3523
|
|
|
3407
3524
|
pass.setBindGroup(0, $._transformBindGroup);
|
|
3408
3525
|
|
|
3526
|
+
let colorsBuffer = Q5.device.createBuffer({
|
|
3527
|
+
size: colorStackIndex * 4,
|
|
3528
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
3529
|
+
mappedAtCreation: true
|
|
3530
|
+
});
|
|
3531
|
+
|
|
3532
|
+
new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
|
|
3533
|
+
colorsBuffer.unmap();
|
|
3534
|
+
|
|
3535
|
+
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3536
|
+
layout: colorsLayout,
|
|
3537
|
+
entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
|
|
3538
|
+
});
|
|
3539
|
+
|
|
3540
|
+
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
3541
|
+
|
|
3409
3542
|
for (let m of $._hooks.preRender) m();
|
|
3410
3543
|
|
|
3411
|
-
let drawVertOffset = 0
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3544
|
+
let drawVertOffset = 0,
|
|
3545
|
+
imageVertOffset = 0,
|
|
3546
|
+
textCharOffset = 0,
|
|
3547
|
+
curPipelineIndex = -1,
|
|
3548
|
+
curTextureIndex = -1;
|
|
3416
3549
|
|
|
3417
|
-
for (let i = 0; i < drawStack.length; i +=
|
|
3550
|
+
for (let i = 0; i < drawStack.length; i += 3) {
|
|
3418
3551
|
let v = drawStack[i + 1];
|
|
3419
|
-
|
|
3420
|
-
if (drawStack[i] == -1) {
|
|
3421
|
-
v();
|
|
3422
|
-
continue;
|
|
3423
|
-
}
|
|
3552
|
+
let o = drawStack[i + 2];
|
|
3424
3553
|
|
|
3425
3554
|
if (curPipelineIndex != drawStack[i]) {
|
|
3426
3555
|
curPipelineIndex = drawStack[i];
|
|
3427
|
-
pass.setPipeline($.
|
|
3556
|
+
pass.setPipeline($._pipelines[curPipelineIndex]);
|
|
3428
3557
|
}
|
|
3429
3558
|
|
|
3430
3559
|
if (curPipelineIndex == 0) {
|
|
3431
3560
|
// v is the number of vertices
|
|
3432
|
-
pass.
|
|
3433
|
-
drawVertOffset +=
|
|
3561
|
+
pass.drawIndexed(v, 1, 0, drawVertOffset);
|
|
3562
|
+
drawVertOffset += o;
|
|
3434
3563
|
} else if (curPipelineIndex == 1) {
|
|
3435
3564
|
if (curTextureIndex != v) {
|
|
3436
3565
|
// v is the texture index
|
|
@@ -3439,7 +3568,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3439
3568
|
pass.draw(6, 1, imageVertOffset);
|
|
3440
3569
|
imageVertOffset += 6;
|
|
3441
3570
|
} else if (curPipelineIndex == 2) {
|
|
3442
|
-
pass.setBindGroup(2, $.
|
|
3571
|
+
pass.setBindGroup(2, $._fonts[o].bindGroup);
|
|
3443
3572
|
pass.setBindGroup(3, $._textBindGroup);
|
|
3444
3573
|
|
|
3445
3574
|
// v is the number of characters in the text
|
|
@@ -3455,12 +3584,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3455
3584
|
pass.end();
|
|
3456
3585
|
let commandBuffer = $.encoder.finish();
|
|
3457
3586
|
Q5.device.queue.submit([commandBuffer]);
|
|
3587
|
+
|
|
3458
3588
|
q.pass = $.encoder = null;
|
|
3459
3589
|
|
|
3460
3590
|
// clear the stacks for the next frame
|
|
3461
3591
|
$.drawStack.length = 0;
|
|
3462
|
-
|
|
3463
|
-
|
|
3592
|
+
colorIndex = 1;
|
|
3593
|
+
colorStackIndex = 8;
|
|
3464
3594
|
rotation = 0;
|
|
3465
3595
|
transformStates.length = 1;
|
|
3466
3596
|
$._transformIndexStack.length = 0;
|
|
@@ -3491,41 +3621,47 @@ Q5.webgpu = async function (scope, parent) {
|
|
|
3491
3621
|
return new Q5(scope, parent, 'webgpu');
|
|
3492
3622
|
};
|
|
3493
3623
|
Q5.renderers.webgpu.drawing = ($, q) => {
|
|
3494
|
-
let c = $.canvas
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3624
|
+
let c = $.canvas,
|
|
3625
|
+
drawStack = $.drawStack,
|
|
3626
|
+
vertexStack = new Float32Array(1e7),
|
|
3627
|
+
indexStack = new Uint32Array(1e6),
|
|
3628
|
+
vertIndex = 0,
|
|
3629
|
+
vertCount = 0,
|
|
3630
|
+
idxBufferIndex = 0,
|
|
3631
|
+
colorIndex;
|
|
3502
3632
|
|
|
3503
3633
|
let vertexShader = Q5.device.createShaderModule({
|
|
3504
3634
|
label: 'drawingVertexShader',
|
|
3505
3635
|
code: `
|
|
3636
|
+
struct VertexInput {
|
|
3637
|
+
@location(0) pos: vec2f,
|
|
3638
|
+
@location(1) colorIndex: f32,
|
|
3639
|
+
@location(2) transformIndex: f32
|
|
3640
|
+
}
|
|
3506
3641
|
struct VertexOutput {
|
|
3507
3642
|
@builtin(position) position: vec4f,
|
|
3508
|
-
@location(0)
|
|
3509
|
-
}
|
|
3510
|
-
|
|
3643
|
+
@location(0) color: vec4f
|
|
3644
|
+
}
|
|
3511
3645
|
struct Uniforms {
|
|
3512
3646
|
halfWidth: f32,
|
|
3513
3647
|
halfHeight: f32
|
|
3514
|
-
}
|
|
3648
|
+
}
|
|
3515
3649
|
|
|
3516
3650
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3517
|
-
@group(0) @binding(1) var<storage
|
|
3651
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3652
|
+
|
|
3653
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
3518
3654
|
|
|
3519
3655
|
@vertex
|
|
3520
|
-
fn vertexMain(
|
|
3521
|
-
var vert = vec4f(pos, 0.0, 1.0);
|
|
3522
|
-
vert = transforms[i32(transformIndex)] * vert;
|
|
3656
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
3657
|
+
var vert = vec4f(input.pos, 0.0, 1.0);
|
|
3658
|
+
vert = transforms[i32(input.transformIndex)] * vert;
|
|
3523
3659
|
vert.x /= uniforms.halfWidth;
|
|
3524
3660
|
vert.y /= uniforms.halfHeight;
|
|
3525
3661
|
|
|
3526
3662
|
var output: VertexOutput;
|
|
3527
3663
|
output.position = vert;
|
|
3528
|
-
output.
|
|
3664
|
+
output.color = colors[i32(input.colorIndex)];
|
|
3529
3665
|
return output;
|
|
3530
3666
|
}
|
|
3531
3667
|
`
|
|
@@ -3534,32 +3670,13 @@ fn vertexMain(@location(0) pos: vec2f, @location(1) colorIndex: f32, @location(2
|
|
|
3534
3670
|
let fragmentShader = Q5.device.createShaderModule({
|
|
3535
3671
|
label: 'drawingFragmentShader',
|
|
3536
3672
|
code: `
|
|
3537
|
-
@group(1) @binding(0) var<storage, read> colors : array<vec4f>;
|
|
3538
|
-
|
|
3539
3673
|
@fragment
|
|
3540
|
-
fn fragmentMain(@location(0)
|
|
3541
|
-
|
|
3542
|
-
return mix(colors[index], colors[index + 1], fract(colorIndex));
|
|
3674
|
+
fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
|
|
3675
|
+
return color;
|
|
3543
3676
|
}
|
|
3544
3677
|
`
|
|
3545
3678
|
});
|
|
3546
3679
|
|
|
3547
|
-
colorsLayout = Q5.device.createBindGroupLayout({
|
|
3548
|
-
label: 'colorsLayout',
|
|
3549
|
-
entries: [
|
|
3550
|
-
{
|
|
3551
|
-
binding: 0,
|
|
3552
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
3553
|
-
buffer: {
|
|
3554
|
-
type: 'read-only-storage',
|
|
3555
|
-
hasDynamicOffset: false
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
]
|
|
3559
|
-
});
|
|
3560
|
-
|
|
3561
|
-
$.bindGroupLayouts.push(colorsLayout);
|
|
3562
|
-
|
|
3563
3680
|
let vertexBufferLayout = {
|
|
3564
3681
|
arrayStride: 16, // 2 coordinates + 1 color index + 1 transform index * 4 bytes each
|
|
3565
3682
|
attributes: [
|
|
@@ -3569,193 +3686,212 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3569
3686
|
]
|
|
3570
3687
|
};
|
|
3571
3688
|
|
|
3572
|
-
// prettier-ignore
|
|
3573
|
-
let blendFactors = [
|
|
3574
|
-
'zero', // 0
|
|
3575
|
-
'one', // 1
|
|
3576
|
-
'src-alpha', // 2
|
|
3577
|
-
'one-minus-src-alpha', // 3
|
|
3578
|
-
'dst', // 4
|
|
3579
|
-
'dst-alpha', // 5
|
|
3580
|
-
'one-minus-dst-alpha', // 6
|
|
3581
|
-
'one-minus-src' // 7
|
|
3582
|
-
];
|
|
3583
|
-
let blendOps = [
|
|
3584
|
-
'add', // 0
|
|
3585
|
-
'subtract', // 1
|
|
3586
|
-
'reverse-subtract', // 2
|
|
3587
|
-
'min', // 3
|
|
3588
|
-
'max' // 4
|
|
3589
|
-
];
|
|
3590
|
-
|
|
3591
|
-
const blendModes = {
|
|
3592
|
-
normal: [2, 3, 0, 2, 3, 0],
|
|
3593
|
-
lighter: [2, 1, 0, 2, 1, 0],
|
|
3594
|
-
subtract: [2, 1, 2, 2, 1, 2],
|
|
3595
|
-
multiply: [4, 0, 0, 5, 0, 0],
|
|
3596
|
-
screen: [1, 3, 0, 1, 3, 0],
|
|
3597
|
-
darken: [1, 3, 3, 1, 3, 3],
|
|
3598
|
-
lighten: [1, 3, 4, 1, 3, 4],
|
|
3599
|
-
overlay: [2, 3, 0, 2, 3, 0],
|
|
3600
|
-
hard_light: [2, 3, 0, 2, 3, 0],
|
|
3601
|
-
soft_light: [2, 3, 0, 2, 3, 0],
|
|
3602
|
-
difference: [2, 3, 2, 2, 3, 2],
|
|
3603
|
-
exclusion: [2, 3, 0, 2, 3, 0],
|
|
3604
|
-
color_dodge: [1, 7, 0, 1, 7, 0],
|
|
3605
|
-
color_burn: [6, 1, 0, 6, 1, 0],
|
|
3606
|
-
linear_dodge: [2, 1, 0, 2, 1, 0],
|
|
3607
|
-
linear_burn: [2, 7, 1, 2, 7, 1],
|
|
3608
|
-
vivid_light: [2, 7, 0, 2, 7, 0],
|
|
3609
|
-
pin_light: [2, 7, 0, 2, 7, 0],
|
|
3610
|
-
hard_mix: [2, 7, 0, 2, 7, 0]
|
|
3611
|
-
};
|
|
3612
|
-
|
|
3613
|
-
$.blendConfigs = {};
|
|
3614
|
-
|
|
3615
|
-
for (const [name, mode] of Object.entries(blendModes)) {
|
|
3616
|
-
$.blendConfigs[name] = {
|
|
3617
|
-
color: {
|
|
3618
|
-
srcFactor: blendFactors[mode[0]],
|
|
3619
|
-
dstFactor: blendFactors[mode[1]],
|
|
3620
|
-
operation: blendOps[mode[2]]
|
|
3621
|
-
},
|
|
3622
|
-
alpha: {
|
|
3623
|
-
srcFactor: blendFactors[mode[3]],
|
|
3624
|
-
dstFactor: blendFactors[mode[4]],
|
|
3625
|
-
operation: blendOps[mode[5]]
|
|
3626
|
-
}
|
|
3627
|
-
};
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
$._blendMode = 'normal';
|
|
3631
|
-
$.blendMode = (mode) => {
|
|
3632
|
-
if (mode == $._blendMode) return;
|
|
3633
|
-
if (mode == 'source-over') mode = 'normal';
|
|
3634
|
-
mode = mode.toLowerCase().replace(/[ -]/g, '_');
|
|
3635
|
-
$._blendMode = mode;
|
|
3636
|
-
$.pipelines[0] = $._createPipeline($.blendConfigs[mode]);
|
|
3637
|
-
};
|
|
3638
|
-
|
|
3639
3689
|
let pipelineLayout = Q5.device.createPipelineLayout({
|
|
3640
3690
|
label: 'drawingPipelineLayout',
|
|
3641
3691
|
bindGroupLayouts: $.bindGroupLayouts
|
|
3642
3692
|
});
|
|
3643
3693
|
|
|
3644
|
-
$.
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3694
|
+
$._pipelineConfigs[0] = {
|
|
3695
|
+
label: 'drawingPipeline',
|
|
3696
|
+
layout: pipelineLayout,
|
|
3697
|
+
vertex: {
|
|
3698
|
+
module: vertexShader,
|
|
3699
|
+
entryPoint: 'vertexMain',
|
|
3700
|
+
buffers: [vertexBufferLayout]
|
|
3701
|
+
},
|
|
3702
|
+
fragment: {
|
|
3703
|
+
module: fragmentShader,
|
|
3704
|
+
entryPoint: 'fragmentMain',
|
|
3705
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
3706
|
+
},
|
|
3707
|
+
primitive: { topology: 'triangle-list' },
|
|
3708
|
+
multisample: {
|
|
3709
|
+
count: 4
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
|
|
3713
|
+
$._pipelines[0] = Q5.device.createRenderPipeline($._pipelineConfigs[0]);
|
|
3714
|
+
|
|
3715
|
+
const addVert = (x, y, ci, ti) => {
|
|
3716
|
+
let v = vertexStack,
|
|
3717
|
+
i = vertIndex;
|
|
3718
|
+
v[i++] = x;
|
|
3719
|
+
v[i++] = y;
|
|
3720
|
+
v[i++] = ci;
|
|
3721
|
+
v[i++] = ti;
|
|
3722
|
+
vertIndex = i;
|
|
3723
|
+
vertCount++;
|
|
3724
|
+
};
|
|
3725
|
+
|
|
3726
|
+
const addIndex = (i1, i2, i3) => {
|
|
3727
|
+
let is = indexStack,
|
|
3728
|
+
ii = idxBufferIndex;
|
|
3729
|
+
is[ii++] = i1;
|
|
3730
|
+
is[ii++] = i2;
|
|
3731
|
+
is[ii++] = i3;
|
|
3732
|
+
idxBufferIndex = ii;
|
|
3733
|
+
};
|
|
3734
|
+
|
|
3735
|
+
const addQuad = (x1, y1, x2, y2, x3, y3, x4, y4, ci, ti) => {
|
|
3736
|
+
let v = vertexStack,
|
|
3737
|
+
i = vertIndex;
|
|
3738
|
+
|
|
3739
|
+
let i1 = vertCount++;
|
|
3740
|
+
v[i++] = x1;
|
|
3741
|
+
v[i++] = y1;
|
|
3742
|
+
v[i++] = ci;
|
|
3743
|
+
v[i++] = ti;
|
|
3744
|
+
|
|
3745
|
+
let i2 = vertCount++;
|
|
3746
|
+
v[i++] = x2;
|
|
3747
|
+
v[i++] = y2;
|
|
3748
|
+
v[i++] = ci;
|
|
3749
|
+
v[i++] = ti;
|
|
3750
|
+
|
|
3751
|
+
let i3 = vertCount++;
|
|
3752
|
+
v[i++] = x3;
|
|
3753
|
+
v[i++] = y3;
|
|
3754
|
+
v[i++] = ci;
|
|
3755
|
+
v[i++] = ti;
|
|
3756
|
+
|
|
3757
|
+
let i4 = vertCount++;
|
|
3758
|
+
v[i++] = x4;
|
|
3759
|
+
v[i++] = y4;
|
|
3760
|
+
v[i++] = ci;
|
|
3761
|
+
v[i++] = ti;
|
|
3762
|
+
|
|
3763
|
+
vertIndex = i;
|
|
3764
|
+
|
|
3765
|
+
let is = indexStack,
|
|
3766
|
+
ii = idxBufferIndex;
|
|
3767
|
+
is[ii++] = i1;
|
|
3768
|
+
is[ii++] = i2;
|
|
3769
|
+
is[ii++] = i3;
|
|
3770
|
+
is[ii++] = i1;
|
|
3771
|
+
is[ii++] = i3;
|
|
3772
|
+
is[ii++] = i4;
|
|
3773
|
+
idxBufferIndex = ii;
|
|
3774
|
+
|
|
3775
|
+
drawStack.push(0, 6, 4);
|
|
3776
|
+
};
|
|
3777
|
+
|
|
3778
|
+
const addEllipse = (x, y, a, b, n, ci, ti) => {
|
|
3779
|
+
let t = 0,
|
|
3780
|
+
angleIncrement = $.TAU / n,
|
|
3781
|
+
indicesStart = vertIndex / 4;
|
|
3782
|
+
addVert(x, y, ci, ti); // Center vertex
|
|
3783
|
+
for (let i = 0; i <= n; i++) {
|
|
3784
|
+
let vx = x + a * Math.cos(t),
|
|
3785
|
+
vy = y + b * Math.sin(t);
|
|
3786
|
+
addVert(vx, vy, ci, ti);
|
|
3787
|
+
if (i > 0) {
|
|
3788
|
+
addIndex(indicesStart, indicesStart + i, indicesStart + i + 1);
|
|
3789
|
+
}
|
|
3790
|
+
t += angleIncrement;
|
|
3791
|
+
}
|
|
3792
|
+
drawStack.push(0, n * 3, n + 2);
|
|
3660
3793
|
};
|
|
3661
3794
|
|
|
3662
|
-
$.
|
|
3795
|
+
$.rectMode = (x) => ($._rectMode = x);
|
|
3663
3796
|
|
|
3664
|
-
|
|
3797
|
+
$.rect = (x, y, w, h) => {
|
|
3798
|
+
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3799
|
+
let ci, ti;
|
|
3800
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3801
|
+
ti = $._transformIndex;
|
|
3665
3802
|
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
};
|
|
3803
|
+
if ($._doStroke) {
|
|
3804
|
+
ci = $._strokeIndex;
|
|
3669
3805
|
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3806
|
+
// outer rectangle coordinates
|
|
3807
|
+
let sw = $._strokeWeight / 2;
|
|
3808
|
+
let to = t + sw,
|
|
3809
|
+
bo = b - sw,
|
|
3810
|
+
lo = l - sw,
|
|
3811
|
+
ro = r + sw;
|
|
3674
3812
|
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
}
|
|
3684
|
-
if (close) {
|
|
3685
|
-
// Close the shape by adding the first vertex at the end
|
|
3686
|
-
v.push(v[0], v[1], v[2], v[3]);
|
|
3687
|
-
}
|
|
3688
|
-
// Convert the shape to triangles
|
|
3689
|
-
let triangles = [];
|
|
3690
|
-
for (let i = 4; i < v.length; i += 4) {
|
|
3691
|
-
triangles.push(
|
|
3692
|
-
v[0], // First vertex
|
|
3693
|
-
v[1],
|
|
3694
|
-
v[2],
|
|
3695
|
-
v[3],
|
|
3696
|
-
v[i - 4], // Previous vertex
|
|
3697
|
-
v[i - 3],
|
|
3698
|
-
v[i - 2],
|
|
3699
|
-
v[i - 1],
|
|
3700
|
-
v[i], // Current vertex
|
|
3701
|
-
v[i + 1],
|
|
3702
|
-
v[i + 2],
|
|
3703
|
-
v[i + 3]
|
|
3704
|
-
);
|
|
3813
|
+
// stroke is simply a bigger rectangle drawn first
|
|
3814
|
+
addQuad(lo, to, ro, to, ro, bo, lo, bo, ci, ti);
|
|
3815
|
+
|
|
3816
|
+
// inner rectangle coordinates
|
|
3817
|
+
t -= sw;
|
|
3818
|
+
b += sw;
|
|
3819
|
+
l += sw;
|
|
3820
|
+
r -= sw;
|
|
3705
3821
|
}
|
|
3706
|
-
shapeVertices = [];
|
|
3707
3822
|
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
};
|
|
3823
|
+
if ($._doFill) {
|
|
3824
|
+
ci = colorIndex ?? $._fillIndex;
|
|
3711
3825
|
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
$.vertex(x2, y2);
|
|
3716
|
-
$.vertex(x3, y3);
|
|
3717
|
-
$.endShape(1);
|
|
3826
|
+
// two triangles make a rectangle
|
|
3827
|
+
addQuad(l, t, r, t, r, b, l, b, ci, ti);
|
|
3828
|
+
}
|
|
3718
3829
|
};
|
|
3719
3830
|
|
|
3720
|
-
$.
|
|
3721
|
-
$.beginShape();
|
|
3722
|
-
$.vertex(x1, y1);
|
|
3723
|
-
$.vertex(x2, y2);
|
|
3724
|
-
$.vertex(x3, y3);
|
|
3725
|
-
$.vertex(x4, y4);
|
|
3726
|
-
$.endShape(1);
|
|
3727
|
-
};
|
|
3831
|
+
$.square = (x, y, s) => $.rect(x, y, s, s);
|
|
3728
3832
|
|
|
3729
|
-
|
|
3833
|
+
// prettier-ignore
|
|
3834
|
+
const getArcSegments = (d) =>
|
|
3835
|
+
d < 4 ? 6 :
|
|
3836
|
+
d < 6 ? 8 :
|
|
3837
|
+
d < 10 ? 10 :
|
|
3838
|
+
d < 16 ? 12 :
|
|
3839
|
+
d < 20 ? 14 :
|
|
3840
|
+
d < 22 ? 16 :
|
|
3841
|
+
d < 24 ? 18 :
|
|
3842
|
+
d < 28 ? 20 :
|
|
3843
|
+
d < 34 ? 22 :
|
|
3844
|
+
d < 42 ? 24 :
|
|
3845
|
+
d < 48 ? 26 :
|
|
3846
|
+
d < 56 ? 28 :
|
|
3847
|
+
d < 64 ? 30 :
|
|
3848
|
+
d < 72 ? 32 :
|
|
3849
|
+
d < 84 ? 34 :
|
|
3850
|
+
d < 96 ? 36 :
|
|
3851
|
+
d < 98 ? 38 :
|
|
3852
|
+
d < 113 ? 40 :
|
|
3853
|
+
d < 149 ? 44 :
|
|
3854
|
+
d < 199 ? 48 :
|
|
3855
|
+
d < 261 ? 52 :
|
|
3856
|
+
d < 353 ? 56 :
|
|
3857
|
+
d < 461 ? 60 :
|
|
3858
|
+
d < 585 ? 64 :
|
|
3859
|
+
d < 1200 ? 70 :
|
|
3860
|
+
d < 1800 ? 80 :
|
|
3861
|
+
d < 2400 ? 90 :
|
|
3862
|
+
100;
|
|
3730
3863
|
|
|
3731
|
-
$.
|
|
3732
|
-
let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
|
|
3864
|
+
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3733
3865
|
|
|
3734
|
-
|
|
3866
|
+
$.ellipse = (x, y, w, h) => {
|
|
3867
|
+
let n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3868
|
+
let a = Math.max(w, 1) / 2;
|
|
3869
|
+
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3870
|
+
let ci;
|
|
3735
3871
|
if ($._matrixDirty) $._saveMatrix();
|
|
3736
3872
|
let ti = $._transformIndex;
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
);
|
|
3747
|
-
drawStack.push(0, 6);
|
|
3873
|
+
if ($._doStroke) {
|
|
3874
|
+
let sw = $._strokeWeight / 2;
|
|
3875
|
+
addEllipse(x, y, a + sw, b + sw, n, $._strokeIndex, ti);
|
|
3876
|
+
a -= sw;
|
|
3877
|
+
b -= sw;
|
|
3878
|
+
}
|
|
3879
|
+
if ($._doFill) {
|
|
3880
|
+
addEllipse(x, y, a, b, n, colorIndex ?? $._fillIndex, ti);
|
|
3881
|
+
}
|
|
3748
3882
|
};
|
|
3749
3883
|
|
|
3750
|
-
$.
|
|
3884
|
+
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3751
3885
|
|
|
3752
3886
|
$.point = (x, y) => {
|
|
3753
3887
|
colorIndex = $._strokeIndex;
|
|
3888
|
+
$._doStroke = false;
|
|
3754
3889
|
let sw = $._strokeWeight;
|
|
3755
3890
|
if (sw < 2) {
|
|
3756
3891
|
sw = Math.round(sw);
|
|
3757
3892
|
$.rect(x, y, sw, sw);
|
|
3758
3893
|
} else $.ellipse(x, y, sw, sw);
|
|
3894
|
+
$._doStroke = true;
|
|
3759
3895
|
colorIndex = null;
|
|
3760
3896
|
};
|
|
3761
3897
|
|
|
@@ -3763,19 +3899,97 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3763
3899
|
colorIndex = $._strokeIndex;
|
|
3764
3900
|
|
|
3765
3901
|
$.push();
|
|
3766
|
-
$.
|
|
3902
|
+
$._doStroke = false;
|
|
3903
|
+
$.translate(x1, -y1);
|
|
3767
3904
|
$.rotate($.atan2(y2 - y1, x2 - x1));
|
|
3768
3905
|
let length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
3769
|
-
let sw = $._strokeWeight
|
|
3770
|
-
|
|
3906
|
+
let sw = $._strokeWeight,
|
|
3907
|
+
hsw = sw / 2;
|
|
3908
|
+
$._rectMode = 'corner';
|
|
3909
|
+
if (sw < 4) {
|
|
3910
|
+
$.rect(-hsw, -hsw, length + hsw, sw);
|
|
3911
|
+
} else {
|
|
3912
|
+
$._ellipseMode = 'center';
|
|
3913
|
+
$.ellipse(0, 0, sw, sw);
|
|
3914
|
+
$.ellipse(length, 0, sw, sw);
|
|
3915
|
+
$.rect(0, -hsw, length, sw);
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3771
3918
|
$.pop();
|
|
3772
3919
|
|
|
3773
3920
|
colorIndex = null;
|
|
3774
3921
|
};
|
|
3775
3922
|
|
|
3923
|
+
let shapeVertCount;
|
|
3924
|
+
|
|
3925
|
+
$.beginShape = () => {
|
|
3926
|
+
shapeVertCount = 0;
|
|
3927
|
+
};
|
|
3928
|
+
|
|
3929
|
+
$.vertex = (x, y) => {
|
|
3930
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3931
|
+
addVert(x, -y, $._fillIndex, $._transformIndex);
|
|
3932
|
+
shapeVertCount++;
|
|
3933
|
+
};
|
|
3934
|
+
|
|
3935
|
+
$.endShape = (close) => {
|
|
3936
|
+
if (shapeVertCount < 3) {
|
|
3937
|
+
throw new Error('A shape must have at least 3 vertices.');
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
let firstVert = vertCount - shapeVertCount;
|
|
3941
|
+
|
|
3942
|
+
if ($._doFill) {
|
|
3943
|
+
// make a simple triangle fan, starting from the first vertex
|
|
3944
|
+
for (let i = firstVert + 1; i < vertCount - 1; i++) {
|
|
3945
|
+
addIndex(firstVert, i, i + 1);
|
|
3946
|
+
}
|
|
3947
|
+
drawStack.push(0, (shapeVertCount - 2) * 3, shapeVertCount);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
if ($._doStroke) {
|
|
3951
|
+
let first = firstVert * 4,
|
|
3952
|
+
last = vertIndex - 4;
|
|
3953
|
+
for (let i = first; i < last; i += 4) {
|
|
3954
|
+
let x1 = vertexStack[i],
|
|
3955
|
+
y1 = vertexStack[i + 1],
|
|
3956
|
+
x2 = vertexStack[i + 4],
|
|
3957
|
+
y2 = vertexStack[i + 5];
|
|
3958
|
+
$.line(x1, y1, x2, y2);
|
|
3959
|
+
}
|
|
3960
|
+
if (close) {
|
|
3961
|
+
let x1 = vertexStack[last],
|
|
3962
|
+
y1 = vertexStack[last + 1],
|
|
3963
|
+
x2 = vertexStack[first],
|
|
3964
|
+
y2 = vertexStack[first + 1];
|
|
3965
|
+
$.line(x1, y1, x2, y2);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
shapeVertCount = 0;
|
|
3970
|
+
};
|
|
3971
|
+
|
|
3972
|
+
$.triangle = (x1, y1, x2, y2, x3, y3) => {
|
|
3973
|
+
$.beginShape();
|
|
3974
|
+
$.vertex(x1, y1);
|
|
3975
|
+
$.vertex(x2, y2);
|
|
3976
|
+
$.vertex(x3, y3);
|
|
3977
|
+
$.endShape();
|
|
3978
|
+
};
|
|
3979
|
+
|
|
3980
|
+
$.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
3981
|
+
$.beginShape();
|
|
3982
|
+
$.vertex(x1, y1);
|
|
3983
|
+
$.vertex(x2, y2);
|
|
3984
|
+
$.vertex(x3, y3);
|
|
3985
|
+
$.vertex(x4, y4);
|
|
3986
|
+
$.endShape();
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3776
3989
|
$.background = (r, g, b, a) => {
|
|
3777
3990
|
$.push();
|
|
3778
3991
|
$.resetMatrix();
|
|
3992
|
+
$._doStroke = false;
|
|
3779
3993
|
if (r.src) {
|
|
3780
3994
|
let og = $._imageMode;
|
|
3781
3995
|
$._imageMode = 'corner';
|
|
@@ -3789,121 +4003,44 @@ fn fragmentMain(@location(0) colorIndex: f32) -> @location(0) vec4f {
|
|
|
3789
4003
|
$._rectMode = og;
|
|
3790
4004
|
}
|
|
3791
4005
|
$.pop();
|
|
4006
|
+
if (!$._fillSet) $._fillIndex = 1;
|
|
3792
4007
|
};
|
|
3793
4008
|
|
|
3794
|
-
/**
|
|
3795
|
-
* Derived from: ceil(Math.log(d) * 7) * 2 - ceil(28)
|
|
3796
|
-
* This lookup table is used for better performance.
|
|
3797
|
-
* @param {Number} d diameter of the circle
|
|
3798
|
-
* @returns n number of segments
|
|
3799
|
-
*/
|
|
3800
|
-
// prettier-ignore
|
|
3801
|
-
const getArcSegments = (d) =>
|
|
3802
|
-
d < 4 ? 6 :
|
|
3803
|
-
d < 6 ? 8 :
|
|
3804
|
-
d < 10 ? 10 :
|
|
3805
|
-
d < 16 ? 12 :
|
|
3806
|
-
d < 20 ? 14 :
|
|
3807
|
-
d < 22 ? 16 :
|
|
3808
|
-
d < 24 ? 18 :
|
|
3809
|
-
d < 28 ? 20 :
|
|
3810
|
-
d < 34 ? 22 :
|
|
3811
|
-
d < 42 ? 24 :
|
|
3812
|
-
d < 48 ? 26 :
|
|
3813
|
-
d < 56 ? 28 :
|
|
3814
|
-
d < 64 ? 30 :
|
|
3815
|
-
d < 72 ? 32 :
|
|
3816
|
-
d < 84 ? 34 :
|
|
3817
|
-
d < 96 ? 36 :
|
|
3818
|
-
d < 98 ? 38 :
|
|
3819
|
-
d < 113 ? 40 :
|
|
3820
|
-
d < 149 ? 44 :
|
|
3821
|
-
d < 199 ? 48 :
|
|
3822
|
-
d < 261 ? 52 :
|
|
3823
|
-
d < 353 ? 56 :
|
|
3824
|
-
d < 461 ? 60 :
|
|
3825
|
-
d < 585 ? 64 :
|
|
3826
|
-
d < 1200 ? 70 :
|
|
3827
|
-
d < 1800 ? 80 :
|
|
3828
|
-
d < 2400 ? 90 :
|
|
3829
|
-
100;
|
|
3830
|
-
|
|
3831
|
-
$.ellipseMode = (x) => ($._ellipseMode = x);
|
|
3832
|
-
|
|
3833
|
-
$.ellipse = (x, y, w, h) => {
|
|
3834
|
-
const n = getArcSegments(w == h ? w : Math.max(w, h));
|
|
3835
|
-
|
|
3836
|
-
let a = Math.max(w, 1) / 2;
|
|
3837
|
-
let b = w == h ? a : Math.max(h, 1) / 2;
|
|
3838
|
-
|
|
3839
|
-
let t = 0; // theta
|
|
3840
|
-
const angleIncrement = $.TAU / n;
|
|
3841
|
-
const ci = colorIndex ?? $._fillIndex;
|
|
3842
|
-
if ($._matrixDirty) $._saveMatrix();
|
|
3843
|
-
const ti = $._transformIndex;
|
|
3844
|
-
let vx1, vy1, vx2, vy2;
|
|
3845
|
-
for (let i = 0; i <= n; i++) {
|
|
3846
|
-
vx1 = vx2;
|
|
3847
|
-
vy1 = vy2;
|
|
3848
|
-
vx2 = x + a * Math.cos(t);
|
|
3849
|
-
vy2 = y + b * Math.sin(t);
|
|
3850
|
-
t += angleIncrement;
|
|
3851
|
-
|
|
3852
|
-
if (i == 0) continue;
|
|
3853
|
-
|
|
3854
|
-
verticesStack.push(x, y, ci, ti, vx1, vy1, ci, ti, vx2, vy2, ci, ti);
|
|
3855
|
-
}
|
|
3856
|
-
|
|
3857
|
-
drawStack.push(0, n * 3);
|
|
3858
|
-
};
|
|
3859
|
-
|
|
3860
|
-
$.circle = (x, y, d) => $.ellipse(x, y, d, d);
|
|
3861
|
-
|
|
3862
4009
|
$._hooks.preRender.push(() => {
|
|
3863
|
-
$.pass.setPipeline($.
|
|
3864
|
-
|
|
3865
|
-
const vertices = new Float32Array(verticesStack);
|
|
4010
|
+
$.pass.setPipeline($._pipelines[0]);
|
|
3866
4011
|
|
|
3867
|
-
|
|
3868
|
-
size:
|
|
3869
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4012
|
+
let vertexBuffer = Q5.device.createBuffer({
|
|
4013
|
+
size: vertIndex * 4,
|
|
4014
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4015
|
+
mappedAtCreation: true
|
|
3870
4016
|
});
|
|
3871
4017
|
|
|
3872
|
-
|
|
4018
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
|
|
4019
|
+
vertexBuffer.unmap();
|
|
4020
|
+
|
|
3873
4021
|
$.pass.setVertexBuffer(0, vertexBuffer);
|
|
3874
4022
|
|
|
3875
|
-
|
|
3876
|
-
size:
|
|
3877
|
-
usage: GPUBufferUsage.
|
|
4023
|
+
let indexBuffer = Q5.device.createBuffer({
|
|
4024
|
+
size: idxBufferIndex * 4,
|
|
4025
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
4026
|
+
mappedAtCreation: true
|
|
3878
4027
|
});
|
|
3879
4028
|
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
$._colorsBindGroup = Q5.device.createBindGroup({
|
|
3883
|
-
layout: colorsLayout,
|
|
3884
|
-
entries: [
|
|
3885
|
-
{
|
|
3886
|
-
binding: 0,
|
|
3887
|
-
resource: {
|
|
3888
|
-
buffer: colorsBuffer,
|
|
3889
|
-
offset: 0,
|
|
3890
|
-
size: colorsStack.length * 4
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
]
|
|
3894
|
-
});
|
|
4029
|
+
new Uint32Array(indexBuffer.getMappedRange()).set(indexStack.slice(0, idxBufferIndex));
|
|
4030
|
+
indexBuffer.unmap();
|
|
3895
4031
|
|
|
3896
|
-
|
|
3897
|
-
$.pass.setBindGroup(1, $._colorsBindGroup);
|
|
4032
|
+
$.pass.setIndexBuffer(indexBuffer, 'uint32');
|
|
3898
4033
|
});
|
|
3899
4034
|
|
|
3900
4035
|
$._hooks.postRender.push(() => {
|
|
3901
|
-
|
|
4036
|
+
vertIndex = 0;
|
|
4037
|
+
vertCount = 0;
|
|
4038
|
+
idxBufferIndex = 0;
|
|
3902
4039
|
});
|
|
3903
4040
|
};
|
|
3904
4041
|
Q5.renderers.webgpu.image = ($, q) => {
|
|
3905
4042
|
$._textureBindGroups = [];
|
|
3906
|
-
let
|
|
4043
|
+
let vertexStack = [];
|
|
3907
4044
|
|
|
3908
4045
|
let vertexShader = Q5.device.createShaderModule({
|
|
3909
4046
|
label: 'imageVertexShader',
|
|
@@ -3911,15 +4048,14 @@ Q5.renderers.webgpu.image = ($, q) => {
|
|
|
3911
4048
|
struct VertexOutput {
|
|
3912
4049
|
@builtin(position) position: vec4f,
|
|
3913
4050
|
@location(0) texCoord: vec2f
|
|
3914
|
-
}
|
|
3915
|
-
|
|
4051
|
+
}
|
|
3916
4052
|
struct Uniforms {
|
|
3917
4053
|
halfWidth: f32,
|
|
3918
4054
|
halfHeight: f32
|
|
3919
|
-
}
|
|
4055
|
+
}
|
|
3920
4056
|
|
|
3921
4057
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
3922
|
-
@group(0) @binding(1) var<storage
|
|
4058
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
3923
4059
|
|
|
3924
4060
|
@vertex
|
|
3925
4061
|
fn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {
|
|
@@ -3980,7 +4116,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3980
4116
|
bindGroupLayouts: [...$.bindGroupLayouts, textureLayout]
|
|
3981
4117
|
});
|
|
3982
4118
|
|
|
3983
|
-
$.
|
|
4119
|
+
$._pipelineConfigs[1] = {
|
|
3984
4120
|
label: 'imagePipeline',
|
|
3985
4121
|
layout: pipelineLayout,
|
|
3986
4122
|
vertex: {
|
|
@@ -3991,28 +4127,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
3991
4127
|
fragment: {
|
|
3992
4128
|
module: fragmentShader,
|
|
3993
4129
|
entryPoint: 'fragmentMain',
|
|
3994
|
-
targets: [
|
|
3995
|
-
{
|
|
3996
|
-
format: 'bgra8unorm',
|
|
3997
|
-
blend: $.blendConfigs?.normal || {
|
|
3998
|
-
color: {
|
|
3999
|
-
srcFactor: 'src-alpha',
|
|
4000
|
-
dstFactor: 'one-minus-src-alpha',
|
|
4001
|
-
operation: 'add'
|
|
4002
|
-
},
|
|
4003
|
-
alpha: {
|
|
4004
|
-
srcFactor: 'src-alpha',
|
|
4005
|
-
dstFactor: 'one-minus-src-alpha',
|
|
4006
|
-
operation: 'add'
|
|
4007
|
-
}
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
]
|
|
4130
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4011
4131
|
},
|
|
4012
|
-
primitive: {
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4132
|
+
primitive: { topology: 'triangle-list' }
|
|
4133
|
+
};
|
|
4134
|
+
|
|
4135
|
+
$._pipelines[1] = Q5.device.createRenderPipeline($._pipelineConfigs[1]);
|
|
4016
4136
|
|
|
4017
4137
|
let sampler = Q5.device.createSampler({
|
|
4018
4138
|
magFilter: 'linear',
|
|
@@ -4037,7 +4157,11 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4037
4157
|
|
|
4038
4158
|
Q5.device.queue.copyExternalImageToTexture(
|
|
4039
4159
|
{ source: img },
|
|
4040
|
-
{
|
|
4160
|
+
{
|
|
4161
|
+
texture,
|
|
4162
|
+
colorSpace: $.canvas.colorSpace
|
|
4163
|
+
// premultipliedAlpha: true
|
|
4164
|
+
},
|
|
4041
4165
|
textureSize
|
|
4042
4166
|
);
|
|
4043
4167
|
|
|
@@ -4090,7 +4214,7 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4090
4214
|
let [l, r, t, b] = $._calcBox(x, y, w, h, $._imageMode);
|
|
4091
4215
|
|
|
4092
4216
|
// prettier-ignore
|
|
4093
|
-
|
|
4217
|
+
vertexStack.push(
|
|
4094
4218
|
l, t, 0, 0, ti,
|
|
4095
4219
|
r, t, 1, 0, ti,
|
|
4096
4220
|
l, b, 0, 1, ti,
|
|
@@ -4099,29 +4223,29 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
|
|
|
4099
4223
|
r, b, 1, 1, ti
|
|
4100
4224
|
);
|
|
4101
4225
|
|
|
4102
|
-
$.drawStack.push(1, img.textureIndex);
|
|
4226
|
+
$.drawStack.push(1, img.textureIndex, 0);
|
|
4103
4227
|
};
|
|
4104
4228
|
|
|
4105
4229
|
$._hooks.preRender.push(() => {
|
|
4106
4230
|
if (!$._textureBindGroups.length) return;
|
|
4107
4231
|
|
|
4108
4232
|
// Switch to image pipeline
|
|
4109
|
-
$.pass.setPipeline($.
|
|
4110
|
-
|
|
4111
|
-
// Create a vertex buffer for the image quads
|
|
4112
|
-
const vertices = new Float32Array(verticesStack);
|
|
4233
|
+
$.pass.setPipeline($._pipelines[1]);
|
|
4113
4234
|
|
|
4114
4235
|
const vertexBuffer = Q5.device.createBuffer({
|
|
4115
|
-
size:
|
|
4116
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
4236
|
+
size: vertexStack.length * 4,
|
|
4237
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
4238
|
+
mappedAtCreation: true
|
|
4117
4239
|
});
|
|
4118
4240
|
|
|
4119
|
-
|
|
4241
|
+
new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
|
|
4242
|
+
vertexBuffer.unmap();
|
|
4243
|
+
|
|
4120
4244
|
$.pass.setVertexBuffer(1, vertexBuffer);
|
|
4121
4245
|
});
|
|
4122
4246
|
|
|
4123
4247
|
$._hooks.postRender.push(() => {
|
|
4124
|
-
|
|
4248
|
+
vertexStack.length = 0;
|
|
4125
4249
|
});
|
|
4126
4250
|
};
|
|
4127
4251
|
|
|
@@ -4142,35 +4266,35 @@ const pos = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
|
4142
4266
|
|
|
4143
4267
|
struct VertexInput {
|
|
4144
4268
|
@builtin(vertex_index) vertex : u32,
|
|
4145
|
-
@builtin(instance_index) instance : u32
|
|
4146
|
-
}
|
|
4269
|
+
@builtin(instance_index) instance : u32
|
|
4270
|
+
}
|
|
4147
4271
|
struct VertexOutput {
|
|
4148
4272
|
@builtin(position) position : vec4f,
|
|
4149
|
-
@location(0)
|
|
4150
|
-
@location(1)
|
|
4151
|
-
}
|
|
4273
|
+
@location(0) texCoord : vec2f,
|
|
4274
|
+
@location(1) fillColor : vec4f
|
|
4275
|
+
}
|
|
4152
4276
|
struct Char {
|
|
4153
4277
|
texOffset: vec2f,
|
|
4154
4278
|
texExtent: vec2f,
|
|
4155
4279
|
size: vec2f,
|
|
4156
4280
|
offset: vec2f,
|
|
4157
|
-
}
|
|
4281
|
+
}
|
|
4158
4282
|
struct Text {
|
|
4159
4283
|
pos: vec2f,
|
|
4160
4284
|
scale: f32,
|
|
4161
4285
|
transformIndex: f32,
|
|
4162
4286
|
fillIndex: f32,
|
|
4163
4287
|
strokeIndex: f32
|
|
4164
|
-
}
|
|
4288
|
+
}
|
|
4165
4289
|
struct Uniforms {
|
|
4166
4290
|
halfWidth: f32,
|
|
4167
4291
|
halfHeight: f32
|
|
4168
|
-
}
|
|
4292
|
+
}
|
|
4169
4293
|
|
|
4170
4294
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
4171
|
-
@group(0) @binding(1) var<storage
|
|
4295
|
+
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
4172
4296
|
|
|
4173
|
-
@group(1) @binding(0) var<storage
|
|
4297
|
+
@group(1) @binding(0) var<storage> colors : array<vec4f>;
|
|
4174
4298
|
|
|
4175
4299
|
@group(2) @binding(0) var fontTexture: texture_2d<f32>;
|
|
4176
4300
|
@group(2) @binding(1) var fontSampler: sampler;
|
|
@@ -4196,13 +4320,13 @@ fn vertexMain(input : VertexInput) -> VertexOutput {
|
|
|
4196
4320
|
|
|
4197
4321
|
var output : VertexOutput;
|
|
4198
4322
|
output.position = vert;
|
|
4199
|
-
output.
|
|
4200
|
-
output.
|
|
4323
|
+
output.texCoord = (pos[input.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
|
|
4324
|
+
output.fillColor = colors[i32(text.fillIndex)];
|
|
4201
4325
|
return output;
|
|
4202
4326
|
}
|
|
4203
4327
|
|
|
4204
|
-
fn sampleMsdf(
|
|
4205
|
-
let c = textureSample(fontTexture, fontSampler,
|
|
4328
|
+
fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
4329
|
+
let c = textureSample(fontTexture, fontSampler, texCoord);
|
|
4206
4330
|
return max(min(c.r, c.g), min(max(c.r, c.g), c.b));
|
|
4207
4331
|
}
|
|
4208
4332
|
|
|
@@ -4212,25 +4336,83 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4212
4336
|
// uses the default which is 4.
|
|
4213
4337
|
let pxRange = 4.0;
|
|
4214
4338
|
let sz = vec2f(textureDimensions(fontTexture, 0));
|
|
4215
|
-
let dx = sz.x*length(vec2f(dpdxFine(input.
|
|
4216
|
-
let dy = sz.y*length(vec2f(dpdxFine(input.
|
|
4339
|
+
let dx = sz.x*length(vec2f(dpdxFine(input.texCoord.x), dpdyFine(input.texCoord.x)));
|
|
4340
|
+
let dy = sz.y*length(vec2f(dpdxFine(input.texCoord.y), dpdyFine(input.texCoord.y)));
|
|
4217
4341
|
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
|
|
4218
|
-
let sigDist = sampleMsdf(input.
|
|
4342
|
+
let sigDist = sampleMsdf(input.texCoord) - 0.5;
|
|
4219
4343
|
let pxDist = sigDist * toPixels;
|
|
4220
4344
|
let edgeWidth = 0.5;
|
|
4221
4345
|
let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
|
|
4222
4346
|
if (alpha < 0.001) {
|
|
4223
4347
|
discard;
|
|
4224
4348
|
}
|
|
4225
|
-
|
|
4226
|
-
return vec4f(fillColor.rgb, fillColor.a * alpha);
|
|
4349
|
+
return vec4f(input.fillColor.rgb, input.fillColor.a * alpha);
|
|
4227
4350
|
}
|
|
4228
4351
|
`
|
|
4229
4352
|
});
|
|
4230
4353
|
|
|
4354
|
+
let textBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4355
|
+
label: 'MSDF text group layout',
|
|
4356
|
+
entries: [
|
|
4357
|
+
{
|
|
4358
|
+
binding: 0,
|
|
4359
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4360
|
+
buffer: { type: 'read-only-storage' }
|
|
4361
|
+
},
|
|
4362
|
+
{
|
|
4363
|
+
binding: 1,
|
|
4364
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4365
|
+
buffer: { type: 'read-only-storage' }
|
|
4366
|
+
}
|
|
4367
|
+
]
|
|
4368
|
+
});
|
|
4369
|
+
|
|
4370
|
+
let fontSampler = Q5.device.createSampler({
|
|
4371
|
+
minFilter: 'linear',
|
|
4372
|
+
magFilter: 'linear',
|
|
4373
|
+
mipmapFilter: 'linear',
|
|
4374
|
+
maxAnisotropy: 16
|
|
4375
|
+
});
|
|
4376
|
+
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4377
|
+
label: 'MSDF font group layout',
|
|
4378
|
+
entries: [
|
|
4379
|
+
{
|
|
4380
|
+
binding: 0,
|
|
4381
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4382
|
+
texture: {}
|
|
4383
|
+
},
|
|
4384
|
+
{
|
|
4385
|
+
binding: 1,
|
|
4386
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
4387
|
+
sampler: {}
|
|
4388
|
+
},
|
|
4389
|
+
{
|
|
4390
|
+
binding: 2,
|
|
4391
|
+
visibility: GPUShaderStage.VERTEX,
|
|
4392
|
+
buffer: { type: 'read-only-storage' }
|
|
4393
|
+
}
|
|
4394
|
+
]
|
|
4395
|
+
});
|
|
4396
|
+
|
|
4397
|
+
let fontPipelineLayout = Q5.device.createPipelineLayout({
|
|
4398
|
+
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4399
|
+
});
|
|
4400
|
+
|
|
4401
|
+
$._pipelineConfigs[2] = {
|
|
4402
|
+
label: 'msdf font pipeline',
|
|
4403
|
+
layout: fontPipelineLayout,
|
|
4404
|
+
vertex: { module: textShader, entryPoint: 'vertexMain' },
|
|
4405
|
+
fragment: {
|
|
4406
|
+
module: textShader,
|
|
4407
|
+
entryPoint: 'fragmentMain',
|
|
4408
|
+
targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
|
|
4409
|
+
},
|
|
4410
|
+
primitive: { topology: 'triangle-strip', stripIndexFormat: 'uint32' }
|
|
4411
|
+
};
|
|
4412
|
+
$._pipelines[2] = Q5.device.createRenderPipeline($._pipelineConfigs[2]);
|
|
4413
|
+
|
|
4231
4414
|
class MsdfFont {
|
|
4232
|
-
constructor(
|
|
4233
|
-
this.pipeline = pipeline;
|
|
4415
|
+
constructor(bindGroup, lineHeight, chars, kernings) {
|
|
4234
4416
|
this.bindGroup = bindGroup;
|
|
4235
4417
|
this.lineHeight = lineHeight;
|
|
4236
4418
|
this.chars = chars;
|
|
@@ -4256,22 +4438,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4256
4438
|
}
|
|
4257
4439
|
}
|
|
4258
4440
|
|
|
4259
|
-
|
|
4260
|
-
label: 'MSDF text group layout',
|
|
4261
|
-
entries: [
|
|
4262
|
-
{
|
|
4263
|
-
binding: 0,
|
|
4264
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4265
|
-
buffer: { type: 'read-only-storage' }
|
|
4266
|
-
},
|
|
4267
|
-
{
|
|
4268
|
-
binding: 1,
|
|
4269
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
4270
|
-
buffer: { type: 'read-only-storage' }
|
|
4271
|
-
}
|
|
4272
|
-
]
|
|
4273
|
-
});
|
|
4274
|
-
|
|
4441
|
+
$._fonts = [];
|
|
4275
4442
|
let fonts = {};
|
|
4276
4443
|
|
|
4277
4444
|
let createFont = async (fontJsonUrl, fontName, cb) => {
|
|
@@ -4334,74 +4501,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4334
4501
|
}
|
|
4335
4502
|
charsBuffer.unmap();
|
|
4336
4503
|
|
|
4337
|
-
let fontSampler = Q5.device.createSampler({
|
|
4338
|
-
minFilter: 'linear',
|
|
4339
|
-
magFilter: 'linear',
|
|
4340
|
-
mipmapFilter: 'linear',
|
|
4341
|
-
maxAnisotropy: 16
|
|
4342
|
-
});
|
|
4343
|
-
let fontBindGroupLayout = Q5.device.createBindGroupLayout({
|
|
4344
|
-
label: 'MSDF font group layout',
|
|
4345
|
-
entries: [
|
|
4346
|
-
{
|
|
4347
|
-
binding: 0,
|
|
4348
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4349
|
-
texture: {}
|
|
4350
|
-
},
|
|
4351
|
-
{
|
|
4352
|
-
binding: 1,
|
|
4353
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
4354
|
-
sampler: {}
|
|
4355
|
-
},
|
|
4356
|
-
{
|
|
4357
|
-
binding: 2,
|
|
4358
|
-
visibility: GPUShaderStage.VERTEX,
|
|
4359
|
-
buffer: { type: 'read-only-storage' }
|
|
4360
|
-
}
|
|
4361
|
-
]
|
|
4362
|
-
});
|
|
4363
|
-
let fontPipeline = Q5.device.createRenderPipeline({
|
|
4364
|
-
label: 'msdf font pipeline',
|
|
4365
|
-
layout: Q5.device.createPipelineLayout({
|
|
4366
|
-
bindGroupLayouts: [...$.bindGroupLayouts, fontBindGroupLayout, textBindGroupLayout]
|
|
4367
|
-
}),
|
|
4368
|
-
vertex: {
|
|
4369
|
-
module: textShader,
|
|
4370
|
-
entryPoint: 'vertexMain'
|
|
4371
|
-
},
|
|
4372
|
-
fragment: {
|
|
4373
|
-
module: textShader,
|
|
4374
|
-
entryPoint: 'fragmentMain',
|
|
4375
|
-
targets: [
|
|
4376
|
-
{
|
|
4377
|
-
format: 'bgra8unorm',
|
|
4378
|
-
blend: {
|
|
4379
|
-
color: {
|
|
4380
|
-
srcFactor: 'src-alpha',
|
|
4381
|
-
dstFactor: 'one-minus-src-alpha'
|
|
4382
|
-
},
|
|
4383
|
-
alpha: {
|
|
4384
|
-
srcFactor: 'one',
|
|
4385
|
-
dstFactor: 'one'
|
|
4386
|
-
}
|
|
4387
|
-
}
|
|
4388
|
-
}
|
|
4389
|
-
]
|
|
4390
|
-
},
|
|
4391
|
-
primitive: {
|
|
4392
|
-
topology: 'triangle-strip',
|
|
4393
|
-
stripIndexFormat: 'uint32'
|
|
4394
|
-
}
|
|
4395
|
-
});
|
|
4396
|
-
|
|
4397
4504
|
let fontBindGroup = Q5.device.createBindGroup({
|
|
4398
4505
|
label: 'msdf font bind group',
|
|
4399
4506
|
layout: fontBindGroupLayout,
|
|
4400
4507
|
entries: [
|
|
4401
|
-
{
|
|
4402
|
-
binding: 0,
|
|
4403
|
-
resource: texture.createView()
|
|
4404
|
-
},
|
|
4508
|
+
{ binding: 0, resource: texture.createView() },
|
|
4405
4509
|
{ binding: 1, resource: fontSampler },
|
|
4406
4510
|
{ binding: 2, resource: { buffer: charsBuffer } }
|
|
4407
4511
|
]
|
|
@@ -4419,10 +4523,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4419
4523
|
}
|
|
4420
4524
|
}
|
|
4421
4525
|
|
|
4422
|
-
$._font = new MsdfFont(
|
|
4526
|
+
$._font = new MsdfFont(fontBindGroup, atlas.common.lineHeight, chars, kernings);
|
|
4423
4527
|
|
|
4528
|
+
$._font.index = $._fonts.length;
|
|
4529
|
+
$._fonts.push($._font);
|
|
4424
4530
|
fonts[fontName] = $._font;
|
|
4425
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4426
4531
|
|
|
4427
4532
|
q._preloadCount--;
|
|
4428
4533
|
|
|
@@ -4451,12 +4556,6 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4451
4556
|
|
|
4452
4557
|
$.textFont = (fontName) => {
|
|
4453
4558
|
$._font = fonts[fontName];
|
|
4454
|
-
|
|
4455
|
-
// replay the change of font in the draw stack
|
|
4456
|
-
$.drawStack.push(-1, () => {
|
|
4457
|
-
$._font = fonts[fontName];
|
|
4458
|
-
$.pipelines[2] = $._font.pipeline;
|
|
4459
|
-
});
|
|
4460
4559
|
};
|
|
4461
4560
|
$.textSize = (size) => {
|
|
4462
4561
|
$._textSize = size;
|
|
@@ -4569,7 +4668,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4569
4668
|
}
|
|
4570
4669
|
}
|
|
4571
4670
|
|
|
4572
|
-
let charsData =
|
|
4671
|
+
let charsData = [];
|
|
4573
4672
|
|
|
4574
4673
|
let ta = $._textAlign,
|
|
4575
4674
|
tb = $._textBaseline,
|
|
@@ -4615,7 +4714,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4615
4714
|
}
|
|
4616
4715
|
$._charStack.push(charsData);
|
|
4617
4716
|
|
|
4618
|
-
let text =
|
|
4717
|
+
let text = [];
|
|
4619
4718
|
|
|
4620
4719
|
if ($._matrixDirty) $._saveMatrix();
|
|
4621
4720
|
|
|
@@ -4623,11 +4722,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4623
4722
|
text[1] = -y;
|
|
4624
4723
|
text[2] = $._textSize / 44;
|
|
4625
4724
|
text[3] = $._transformIndex;
|
|
4626
|
-
text[4] = $._fillIndex;
|
|
4725
|
+
text[4] = $._fillSet ? $._fillIndex : 0;
|
|
4627
4726
|
text[5] = $._strokeIndex;
|
|
4628
4727
|
|
|
4629
4728
|
$._textStack.push(text);
|
|
4630
|
-
$.drawStack.push(2, measurements.printedCharCount);
|
|
4729
|
+
$.drawStack.push(2, measurements.printedCharCount, $._font.index);
|
|
4631
4730
|
};
|
|
4632
4731
|
|
|
4633
4732
|
$.textWidth = (str) => {
|
|
@@ -4640,11 +4739,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4640
4739
|
|
|
4641
4740
|
if ($._doFill) {
|
|
4642
4741
|
let fi = $._fillIndex * 4;
|
|
4643
|
-
g.fill(
|
|
4742
|
+
g.fill(colorStack.slice(fi, fi + 4));
|
|
4644
4743
|
}
|
|
4645
4744
|
if ($._doStroke) {
|
|
4646
4745
|
let si = $._strokeIndex * 4;
|
|
4647
|
-
g.stroke(
|
|
4746
|
+
g.stroke(colorStack.slice(si, si + 4));
|
|
4648
4747
|
}
|
|
4649
4748
|
|
|
4650
4749
|
let img = g.createTextImage(str, w, h);
|
|
@@ -4695,21 +4794,15 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4695
4794
|
totalTextSize += charsData.length * 4;
|
|
4696
4795
|
}
|
|
4697
4796
|
|
|
4698
|
-
// Create a single buffer for all
|
|
4797
|
+
// Create a single buffer for all char data
|
|
4699
4798
|
let charBuffer = Q5.device.createBuffer({
|
|
4700
|
-
label: 'charBuffer',
|
|
4701
4799
|
size: totalTextSize,
|
|
4702
4800
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
4703
4801
|
mappedAtCreation: true
|
|
4704
4802
|
});
|
|
4705
4803
|
|
|
4706
4804
|
// Copy all text data into the buffer
|
|
4707
|
-
|
|
4708
|
-
let o = 0;
|
|
4709
|
-
for (let array of $._charStack) {
|
|
4710
|
-
textArray.set(array, o);
|
|
4711
|
-
o += array.length;
|
|
4712
|
-
}
|
|
4805
|
+
new Float32Array(charBuffer.getMappedRange()).set($._charStack.flat());
|
|
4713
4806
|
charBuffer.unmap();
|
|
4714
4807
|
|
|
4715
4808
|
// Calculate total buffer size for metadata
|
|
@@ -4724,12 +4817,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4724
4817
|
});
|
|
4725
4818
|
|
|
4726
4819
|
// Copy all metadata into the buffer
|
|
4727
|
-
|
|
4728
|
-
o = 0;
|
|
4729
|
-
for (let array of $._textStack) {
|
|
4730
|
-
metadataArray.set(array, o);
|
|
4731
|
-
o += array.length;
|
|
4732
|
-
}
|
|
4820
|
+
new Float32Array(textBuffer.getMappedRange()).set($._textStack.flat());
|
|
4733
4821
|
textBuffer.unmap();
|
|
4734
4822
|
|
|
4735
4823
|
// Create a single bind group for the text buffer and metadata buffer
|
|
@@ -4737,14 +4825,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
4737
4825
|
label: 'msdf text bind group',
|
|
4738
4826
|
layout: textBindGroupLayout,
|
|
4739
4827
|
entries: [
|
|
4740
|
-
{
|
|
4741
|
-
|
|
4742
|
-
resource: { buffer: charBuffer }
|
|
4743
|
-
},
|
|
4744
|
-
{
|
|
4745
|
-
binding: 1,
|
|
4746
|
-
resource: { buffer: textBuffer }
|
|
4747
|
-
}
|
|
4828
|
+
{ binding: 0, resource: { buffer: charBuffer } },
|
|
4829
|
+
{ binding: 1, resource: { buffer: textBuffer } }
|
|
4748
4830
|
]
|
|
4749
4831
|
});
|
|
4750
4832
|
});
|