q5 2.12.12 → 2.13.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5",
3
- "version": "2.12.12",
3
+ "version": "2.13.1",
4
4
  "description": "A sequel to p5.js that's optimized for interactive art",
5
5
  "author": "quinton-ashley",
6
6
  "contributors": [
package/q5.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * q5.js
3
- * @version 2.12
3
+ * @version 2.13
4
4
  * @author quinton-ashley, Tezumie, and LingDong-
5
5
  * @license LGPL-3.0
6
6
  * @class Q5
@@ -3594,6 +3594,9 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3594
3594
  c.width = $.width = 500;
3595
3595
  c.height = $.height = 500;
3596
3596
 
3597
+ // q2d graphics context
3598
+ $._g = $.createGraphics(1, 1);
3599
+
3597
3600
  if ($.colorMode) $.colorMode('rgb', 1);
3598
3601
 
3599
3602
  let pass,
@@ -3729,7 +3732,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3729
3732
  };
3730
3733
 
3731
3734
  $._stroke = 0;
3732
- $._fill = 1;
3735
+ $._fill = $._tint = 1;
3733
3736
  $._doFill = $._doStroke = true;
3734
3737
 
3735
3738
  $.fill = (r, g, b, a) => {
@@ -3742,42 +3745,51 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3742
3745
  $._doStroke = $._strokeSet = true;
3743
3746
  $._stroke = colorIndex;
3744
3747
  };
3748
+ $.tint = (r, g, b, a) => {
3749
+ addColor(r, g, b, a);
3750
+ $._tint = colorIndex;
3751
+ };
3745
3752
 
3746
3753
  $.noFill = () => ($._doFill = false);
3747
3754
  $.noStroke = () => ($._doStroke = false);
3755
+ $.noTint = () => ($._tint = 1);
3748
3756
 
3749
3757
  $._strokeWeight = 1;
3750
3758
  $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
3751
3759
 
3752
- $.resetMatrix = () => {
3753
- // initialize the transformation matrix as 4x4 identity matrix
3754
-
3755
- // prettier-ignore
3756
- $._matrix = [
3757
- 1, 0, 0, 0,
3758
- 0, 1, 0, 0,
3759
- 0, 0, 1, 0,
3760
- 0, 0, 0, 1
3761
- ];
3762
- $._transformIndex = 0;
3763
- };
3764
- $.resetMatrix();
3760
+ const MAX_TRANSFORMS = 1e7, // or whatever maximum you need
3761
+ MATRIX_SIZE = 16, // 4x4 matrix
3762
+ transforms = new Float32Array(MAX_TRANSFORMS * MATRIX_SIZE),
3763
+ matrices = [],
3764
+ matricesIndexStack = [];
3765
+ let matrix;
3765
3766
 
3766
3767
  // tracks if the matrix has been modified
3767
3768
  $._matrixDirty = false;
3768
3769
 
3769
- // array to store transformation matrices for the render pass
3770
- let transformStates = [$._matrix.slice()];
3770
+ // initialize with a 4x4 identity matrix
3771
+ // prettier-ignore
3772
+ matrices.push([
3773
+ 1, 0, 0, 0,
3774
+ 0, 1, 0, 0,
3775
+ 0, 0, 1, 0,
3776
+ 0, 0, 0, 1
3777
+ ]);
3771
3778
 
3772
- // stack to keep track of transformation matrix indexes
3773
- $._transformIndexStack = [];
3779
+ transforms.set(matrices[0]);
3780
+
3781
+ $.resetMatrix = () => {
3782
+ matrix = matrices[0].slice();
3783
+ $._matrixIndex = 0;
3784
+ };
3785
+ $.resetMatrix();
3774
3786
 
3775
3787
  $.translate = (x, y, z) => {
3776
3788
  if (!x && !y && !z) return;
3777
3789
  // update the translation values
3778
- $._matrix[12] += x;
3779
- $._matrix[13] -= y;
3780
- $._matrix[14] += z || 0;
3790
+ matrix[12] += x;
3791
+ matrix[13] -= y;
3792
+ matrix[14] += z || 0;
3781
3793
  $._matrixDirty = true;
3782
3794
  };
3783
3795
 
@@ -3788,7 +3800,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3788
3800
  let cosR = Math.cos(a);
3789
3801
  let sinR = Math.sin(a);
3790
3802
 
3791
- let m = $._matrix;
3803
+ let m = matrix;
3792
3804
 
3793
3805
  let m0 = m[0],
3794
3806
  m1 = m[1],
@@ -3815,7 +3827,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3815
3827
  $.scale = (x = 1, y, z = 1) => {
3816
3828
  y ??= x;
3817
3829
 
3818
- let m = $._matrix;
3830
+ let m = matrix;
3819
3831
 
3820
3832
  m[0] *= x;
3821
3833
  m[1] *= x;
@@ -3839,13 +3851,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3839
3851
 
3840
3852
  let tanAng = Math.tan(ang);
3841
3853
 
3842
- let m0 = $._matrix[0],
3843
- m1 = $._matrix[1],
3844
- m4 = $._matrix[4],
3845
- m5 = $._matrix[5];
3854
+ let m0 = matrix[0],
3855
+ m1 = matrix[1],
3856
+ m4 = matrix[4],
3857
+ m5 = matrix[5];
3846
3858
 
3847
- $._matrix[0] = m0 + m4 * tanAng;
3848
- $._matrix[1] = m1 + m5 * tanAng;
3859
+ matrix[0] = m0 + m4 * tanAng;
3860
+ matrix[1] = m1 + m5 * tanAng;
3849
3861
 
3850
3862
  $._matrixDirty = true;
3851
3863
  };
@@ -3856,13 +3868,13 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3856
3868
 
3857
3869
  let tanAng = Math.tan(ang);
3858
3870
 
3859
- let m0 = $._matrix[0],
3860
- m1 = $._matrix[1],
3861
- m4 = $._matrix[4],
3862
- m5 = $._matrix[5];
3871
+ let m0 = matrix[0],
3872
+ m1 = matrix[1],
3873
+ m4 = matrix[4],
3874
+ m5 = matrix[5];
3863
3875
 
3864
- $._matrix[4] = m4 + m0 * tanAng;
3865
- $._matrix[5] = m5 + m1 * tanAng;
3876
+ matrix[4] = m4 + m0 * tanAng;
3877
+ matrix[5] = m5 + m1 * tanAng;
3866
3878
 
3867
3879
  $._matrixDirty = true;
3868
3880
  };
@@ -3880,31 +3892,32 @@ Q5.renderers.webgpu.canvas = ($, q) => {
3880
3892
  }
3881
3893
 
3882
3894
  // overwrite the current transformation matrix
3883
- $._matrix = m.slice();
3895
+ matrix = m.slice();
3884
3896
  $._matrixDirty = true;
3885
3897
  };
3886
3898
 
3887
3899
  // function to save the current matrix state if dirty
3888
3900
  $._saveMatrix = () => {
3889
- transformStates.push($._matrix.slice());
3890
- $._transformIndex = transformStates.length - 1;
3901
+ transforms.set(matrix, matrices.length * MATRIX_SIZE);
3902
+ $._matrixIndex = matrices.length;
3903
+ matrices.push(matrix.slice());
3891
3904
  $._matrixDirty = false;
3892
3905
  };
3893
3906
 
3894
3907
  // push the current matrix index onto the stack
3895
3908
  $.pushMatrix = () => {
3896
3909
  if ($._matrixDirty) $._saveMatrix();
3897
- $._transformIndexStack.push($._transformIndex);
3910
+ matricesIndexStack.push($._matrixIndex);
3898
3911
  };
3899
3912
 
3900
3913
  $.popMatrix = () => {
3901
- if (!$._transformIndexStack.length) {
3914
+ if (!matricesIndexStack.length) {
3902
3915
  return console.warn('Matrix index stack is empty!');
3903
3916
  }
3904
3917
  // pop the last matrix index and set it as the current matrix index
3905
- let idx = $._transformIndexStack.pop();
3906
- $._matrix = transformStates[idx].slice();
3907
- $._transformIndex = idx;
3918
+ let idx = matricesIndexStack.pop();
3919
+ matrix = matrices[idx].slice();
3920
+ $._matrixIndex = idx;
3908
3921
  $._matrixDirty = false;
3909
3922
  };
3910
3923
 
@@ -4029,14 +4042,14 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4029
4042
  };
4030
4043
 
4031
4044
  $._render = () => {
4032
- if (transformStates.length > 1 || !$._transformBindGroup) {
4045
+ if (matrices.length > 1 || !$._transformBindGroup) {
4033
4046
  let transformBuffer = Q5.device.createBuffer({
4034
- size: transformStates.length * 64, // 64 is the size of 16 floats
4047
+ size: matrices.length * MATRIX_SIZE * 4, // 4 bytes per float
4035
4048
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
4036
4049
  mappedAtCreation: true
4037
4050
  });
4038
4051
 
4039
- new Float32Array(transformBuffer.getMappedRange()).set(transformStates.flat());
4052
+ new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
4040
4053
  transformBuffer.unmap();
4041
4054
 
4042
4055
  $._transformBindGroup = Q5.device.createBindGroup({
@@ -4064,7 +4077,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4064
4077
  entries: [{ binding: 0, resource: { buffer: colorsBuffer } }]
4065
4078
  });
4066
4079
 
4067
- $.pass.setBindGroup(1, $._colorsBindGroup);
4080
+ pass.setBindGroup(1, $._colorsBindGroup);
4068
4081
 
4069
4082
  for (let m of $._hooks.preRender) m();
4070
4083
 
@@ -4088,6 +4101,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4088
4101
  pass.draw(v, 1, drawVertOffset);
4089
4102
  drawVertOffset += v;
4090
4103
  } else if (curPipelineIndex == 1) {
4104
+ // let vertCount = drawStack[i + 2];
4091
4105
  // draw images
4092
4106
  if (curTextureIndex != v) {
4093
4107
  // v is the texture index
@@ -4095,6 +4109,7 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4095
4109
  }
4096
4110
  pass.draw(4, 1, imageVertOffset);
4097
4111
  imageVertOffset += 4;
4112
+ // i++;
4098
4113
  } else if (curPipelineIndex == 2) {
4099
4114
  // draw text
4100
4115
  let o = drawStack[i + 2];
@@ -4123,8 +4138,9 @@ Q5.renderers.webgpu.canvas = ($, q) => {
4123
4138
  colorIndex = 1;
4124
4139
  colorStackIndex = 8;
4125
4140
  rotation = 0;
4126
- transformStates.length = 1;
4127
- $._transformIndexStack.length = 0;
4141
+ transforms.length = MATRIX_SIZE;
4142
+ matrices.length = 1;
4143
+ matricesIndexStack.length = 0;
4128
4144
  };
4129
4145
  };
4130
4146
 
@@ -4135,7 +4151,10 @@ Q5.initWebGPU = async () => {
4135
4151
  }
4136
4152
  if (!Q5.device) {
4137
4153
  let adapter = await navigator.gpu.requestAdapter();
4138
- if (!adapter) throw new Error('No appropriate GPUAdapter found.');
4154
+ if (!adapter) {
4155
+ console.warn('q5 WebGPU could not start! No appropriate GPUAdapter found, vulkan may need to be enabled.');
4156
+ return false;
4157
+ }
4139
4158
  Q5.device = await adapter.requestDevice();
4140
4159
  }
4141
4160
  return true;
@@ -4160,7 +4179,7 @@ Q5.renderers.webgpu.drawing = ($, q) => {
4160
4179
  struct VertexInput {
4161
4180
  @location(0) pos: vec2f,
4162
4181
  @location(1) colorIndex: f32,
4163
- @location(2) transformIndex: f32
4182
+ @location(2) matrixIndex: f32
4164
4183
  }
4165
4184
  struct VertexOutput {
4166
4185
  @builtin(position) position: vec4f,
@@ -4179,7 +4198,7 @@ struct Uniforms {
4179
4198
  @vertex
4180
4199
  fn vertexMain(input: VertexInput) -> VertexOutput {
4181
4200
  var vert = vec4f(input.pos, 0.0, 1.0);
4182
- vert = transforms[i32(input.transformIndex)] * vert;
4201
+ vert = transforms[i32(input.matrixIndex)] * vert;
4183
4202
  vert.x /= uniforms.halfWidth;
4184
4203
  vert.y /= uniforms.halfHeight;
4185
4204
 
@@ -4206,7 +4225,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4206
4225
  attributes: [
4207
4226
  { format: 'float32x2', offset: 0, shaderLocation: 0 }, // position
4208
4227
  { format: 'float32', offset: 8, shaderLocation: 1 }, // colorIndex
4209
- { format: 'float32', offset: 12, shaderLocation: 2 } // transformIndex
4228
+ { format: 'float32', offset: 12, shaderLocation: 2 } // matrixIndex
4210
4229
  ]
4211
4230
  };
4212
4231
 
@@ -4360,7 +4379,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4360
4379
  let [l, r, t, b] = $._calcBox(x, y, w, h, $._rectMode);
4361
4380
  let ci, ti;
4362
4381
  if ($._matrixDirty) $._saveMatrix();
4363
- ti = $._transformIndex;
4382
+ ti = $._matrixIndex;
4364
4383
 
4365
4384
  if ($._doFill) {
4366
4385
  ci = $._fill;
@@ -4434,7 +4453,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4434
4453
  let b = w == h ? a : Math.max(h, 1) / 2;
4435
4454
 
4436
4455
  if ($._matrixDirty) $._saveMatrix();
4437
- let ti = $._transformIndex;
4456
+ let ti = $._matrixIndex;
4438
4457
 
4439
4458
  if ($._doFill) {
4440
4459
  addEllipse(x, y, a, b, n, $._fill, ti);
@@ -4450,7 +4469,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4450
4469
 
4451
4470
  $.point = (x, y) => {
4452
4471
  if ($._matrixDirty) $._saveMatrix();
4453
- let ti = $._transformIndex,
4472
+ let ti = $._matrixIndex,
4454
4473
  ci = $._stroke,
4455
4474
  sw = $._strokeWeight;
4456
4475
 
@@ -4470,7 +4489,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4470
4489
 
4471
4490
  $.line = (x1, y1, x2, y2) => {
4472
4491
  if ($._matrixDirty) $._saveMatrix();
4473
- let ti = $._transformIndex,
4492
+ let ti = $._matrixIndex,
4474
4493
  ci = $._stroke,
4475
4494
  sw = $._strokeWeight,
4476
4495
  hsw = sw / 2;
@@ -4505,7 +4524,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4505
4524
 
4506
4525
  $.vertex = (x, y) => {
4507
4526
  if ($._matrixDirty) $._saveMatrix();
4508
- sv.push(x, -y, $._fill, $._transformIndex);
4527
+ sv.push(x, -y, $._fill, $._matrixIndex);
4509
4528
  shapeVertCount++;
4510
4529
  };
4511
4530
 
@@ -4550,7 +4569,7 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4550
4569
  (2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y) * t2 +
4551
4570
  (-p0.y + 3 * p1.y - 3 * p2.y + p3.y) * t3);
4552
4571
 
4553
- sv.push(x, y, $._fill, $._transformIndex);
4572
+ sv.push(x, y, $._fill, $._matrixIndex);
4554
4573
  shapeVertCount++;
4555
4574
  }
4556
4575
  }
@@ -4668,14 +4687,22 @@ fn fragmentMain(@location(0) color: vec4f) -> @location(0) vec4f {
4668
4687
  };
4669
4688
  Q5.renderers.webgpu.image = ($, q) => {
4670
4689
  $._textureBindGroups = [];
4671
- let vertexStack = [];
4690
+ let vertexStack = new Float32Array(1e7),
4691
+ vertIndex = 0;
4672
4692
 
4673
- let vertexShader = Q5.device.createShaderModule({
4674
- label: 'imageVertexShader',
4693
+ let imageShader = Q5.device.createShaderModule({
4694
+ label: 'imageShader',
4675
4695
  code: `
4696
+ struct VertexInput {
4697
+ @location(0) pos: vec2f,
4698
+ @location(1) texCoord: vec2f,
4699
+ @location(2) tintIndex: f32,
4700
+ @location(3) matrixIndex: f32
4701
+ }
4676
4702
  struct VertexOutput {
4677
4703
  @builtin(position) position: vec4f,
4678
- @location(0) texCoord: vec2f
4704
+ @location(0) texCoord: vec2f,
4705
+ @location(1) tintIndex: f32
4679
4706
  }
4680
4707
  struct Uniforms {
4681
4708
  halfWidth: f32,
@@ -4685,31 +4712,33 @@ struct Uniforms {
4685
4712
  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
4686
4713
  @group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
4687
4714
 
4715
+ @group(1) @binding(0) var<storage> colors : array<vec4f>;
4716
+
4717
+ @group(2) @binding(0) var samp: sampler;
4718
+ @group(2) @binding(1) var texture: texture_2d<f32>;
4719
+
4688
4720
  @vertex
4689
- fn vertexMain(@location(0) pos: vec2f, @location(1) texCoord: vec2f, @location(2) transformIndex: f32) -> VertexOutput {
4690
- var vert = vec4f(pos, 0.0, 1.0);
4691
- vert = transforms[i32(transformIndex)] * vert;
4721
+ fn vertexMain(input: VertexInput) -> VertexOutput {
4722
+ var vert = vec4f(input.pos, 0.0, 1.0);
4723
+ vert = transforms[i32(input.matrixIndex)] * vert;
4692
4724
  vert.x /= uniforms.halfWidth;
4693
4725
  vert.y /= uniforms.halfHeight;
4694
4726
 
4695
4727
  var output: VertexOutput;
4696
4728
  output.position = vert;
4697
- output.texCoord = texCoord;
4729
+ output.texCoord = input.texCoord;
4730
+ output.tintIndex = input.tintIndex;
4698
4731
  return output;
4699
4732
  }
4700
- `
4701
- });
4702
-
4703
- let fragmentShader = Q5.device.createShaderModule({
4704
- label: 'imageFragmentShader',
4705
- code: `
4706
- @group(2) @binding(0) var samp: sampler;
4707
- @group(2) @binding(1) var texture: texture_2d<f32>;
4708
4733
 
4709
4734
  @fragment
4710
- fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
4711
- // Sample the texture using the interpolated texture coordinate
4712
- return textureSample(texture, samp, texCoord);
4735
+ fn fragmentMain(@location(0) texCoord: vec2f, @location(1) tintIndex: f32) -> @location(0) vec4f {
4736
+ let texColor = textureSample(texture, samp, texCoord);
4737
+ let tintColor = colors[i32(tintIndex)];
4738
+
4739
+ // Mix original and tinted colors using tint alpha as blend factor
4740
+ let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a);
4741
+ return mix(texColor, tinted, tintColor.a);
4713
4742
  }
4714
4743
  `
4715
4744
  });
@@ -4731,11 +4760,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
4731
4760
  });
4732
4761
 
4733
4762
  const vertexBufferLayout = {
4734
- arrayStride: 20,
4763
+ arrayStride: 24,
4735
4764
  attributes: [
4736
4765
  { shaderLocation: 0, offset: 0, format: 'float32x2' },
4737
4766
  { shaderLocation: 1, offset: 8, format: 'float32x2' },
4738
- { shaderLocation: 2, offset: 16, format: 'float32' } // transformIndex
4767
+ { shaderLocation: 2, offset: 16, format: 'float32' }, // tintIndex
4768
+ { shaderLocation: 3, offset: 20, format: 'float32' } // matrixIndex
4739
4769
  ]
4740
4770
  };
4741
4771
 
@@ -4748,12 +4778,12 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
4748
4778
  label: 'imagePipeline',
4749
4779
  layout: pipelineLayout,
4750
4780
  vertex: {
4751
- module: vertexShader,
4781
+ module: imageShader,
4752
4782
  entryPoint: 'vertexMain',
4753
4783
  buffers: [{ arrayStride: 0, attributes: [] }, vertexBufferLayout]
4754
4784
  },
4755
4785
  fragment: {
4756
- module: fragmentShader,
4786
+ module: imageShader,
4757
4787
  entryPoint: 'fragmentMain',
4758
4788
  targets: [{ format: 'bgra8unorm', blend: $.blendConfigs.normal }]
4759
4789
  },
@@ -4829,58 +4859,63 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
4829
4859
 
4830
4860
  $.loadImage = (src, cb) => {
4831
4861
  q._preloadCount++;
4832
- const img = new Image();
4833
- img.crossOrigin = 'Anonymous';
4834
- img.onload = () => {
4835
- // calculate the default width and height that the image
4836
- // should be drawn at if the user doesn't specify a display size
4837
- img.defaultWidth = img.width * $._defaultImageScale;
4838
- img.defaultHeight = img.height * $._defaultImageScale;
4839
- img.pixelDensity = 1;
4840
-
4862
+ let g = $._g.loadImage(src, (img) => {
4863
+ g.defaultWidth = img.width * $._defaultImageScale;
4864
+ g.defaultHeight = img.height * $._defaultImageScale;
4841
4865
  $._createTexture(img);
4842
4866
  q._preloadCount--;
4843
4867
  if (cb) cb(img);
4844
- };
4845
- img.src = src;
4846
- return img;
4868
+ });
4869
+ return g;
4847
4870
  };
4848
4871
 
4849
4872
  $.imageMode = (x) => ($._imageMode = x);
4850
4873
 
4851
- $.image = (img, dx, dy, dw, dh, sx = 0, sy = 0, sw, sh) => {
4874
+ const addVert = (x, y, u, v, ci, ti) => {
4875
+ let s = vertexStack,
4876
+ i = vertIndex;
4877
+ s[i++] = x;
4878
+ s[i++] = y;
4879
+ s[i++] = u;
4880
+ s[i++] = v;
4881
+ s[i++] = ci;
4882
+ s[i++] = ti;
4883
+ vertIndex = i;
4884
+ };
4885
+
4886
+ $.image = (img, dx = 0, dy = 0, dw, dh, sx = 0, sy = 0, sw, sh) => {
4887
+ let g = img;
4852
4888
  if (img.canvas) img = img.canvas;
4853
4889
  if (img.textureIndex == undefined) return;
4854
4890
 
4855
4891
  if ($._matrixDirty) $._saveMatrix();
4856
- let ti = $._transformIndex;
4857
4892
 
4858
- let w = img.width;
4859
- let h = img.height;
4893
+ let ti = $._matrixIndex,
4894
+ w = img.width,
4895
+ h = img.height;
4860
4896
 
4861
- dw ??= img.defaultWidth;
4862
- dh ??= img.defaultHeight;
4897
+ dw ??= g.defaultWidth;
4898
+ dh ??= g.defaultHeight;
4863
4899
  sw ??= w;
4864
4900
  sh ??= h;
4865
4901
 
4866
- let pd = img.pixelDensity || 1;
4902
+ let pd = g._pixelDensity || 1;
4867
4903
  dw *= pd;
4868
4904
  dh *= pd;
4869
4905
 
4870
4906
  let [l, r, t, b] = $._calcBox(dx, dy, dw, dh, $._imageMode);
4871
4907
 
4872
- let u0 = sx / w;
4873
- let v0 = sy / h;
4874
- let u1 = (sx + sw) / w;
4875
- let v1 = (sy + sh) / h;
4876
-
4877
- // prettier-ignore
4878
- vertexStack.push(
4879
- l, t, u0, v0, ti,
4880
- r, t, u1, v0, ti,
4881
- l, b, u0, v1, ti,
4882
- r, b, u1, v1, ti
4883
- );
4908
+ let u0 = sx / w,
4909
+ v0 = sy / h,
4910
+ u1 = (sx + sw) / w,
4911
+ v1 = (sy + sh) / h;
4912
+
4913
+ let ci = $._tint;
4914
+
4915
+ addVert(l, t, u0, v0, ci, ti);
4916
+ addVert(r, t, u1, v0, ci, ti);
4917
+ addVert(l, b, u0, v1, ci, ti);
4918
+ addVert(r, b, u1, v1, ci, ti);
4884
4919
 
4885
4920
  $.drawStack.push(1, img.textureIndex);
4886
4921
  };
@@ -4891,20 +4926,20 @@ fn fragmentMain(@location(0) texCoord: vec2f) -> @location(0) vec4f {
4891
4926
  // Switch to image pipeline
4892
4927
  $.pass.setPipeline($._pipelines[1]);
4893
4928
 
4894
- const vertexBuffer = Q5.device.createBuffer({
4895
- size: vertexStack.length * 4,
4929
+ let vertexBuffer = Q5.device.createBuffer({
4930
+ size: vertIndex * 4,
4896
4931
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
4897
4932
  mappedAtCreation: true
4898
4933
  });
4899
4934
 
4900
- new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack);
4935
+ new Float32Array(vertexBuffer.getMappedRange()).set(vertexStack.slice(0, vertIndex));
4901
4936
  vertexBuffer.unmap();
4902
4937
 
4903
4938
  $.pass.setVertexBuffer(1, vertexBuffer);
4904
4939
  });
4905
4940
 
4906
4941
  $._hooks.postRender.push(() => {
4907
- vertexStack.length = 0;
4942
+ vertIndex = 0;
4908
4943
  });
4909
4944
  };
4910
4945
 
@@ -4941,7 +4976,7 @@ struct Char {
4941
4976
  struct Text {
4942
4977
  pos: vec2f,
4943
4978
  scale: f32,
4944
- transformIndex: f32,
4979
+ matrixIndex: f32,
4945
4980
  fillIndex: f32,
4946
4981
  strokeIndex: f32
4947
4982
  }
@@ -4973,7 +5008,7 @@ fn vertexMain(input : VertexInput) -> VertexOutput {
4973
5008
  let charPos = ((pos[input.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
4974
5009
 
4975
5010
  var vert = vec4f(charPos, 0.0, 1.0);
4976
- vert = transforms[i32(text.transformIndex)] * vert;
5011
+ vert = transforms[i32(text.matrixIndex)] * vert;
4977
5012
  vert.x /= uniforms.halfWidth;
4978
5013
  vert.y /= uniforms.halfHeight;
4979
5014
 
@@ -5197,13 +5232,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5197
5232
  if (cb) cb(fontName);
5198
5233
  };
5199
5234
 
5200
- // q2d graphics context to use for text image creation
5201
- let g = $.createGraphics(1, 1);
5202
- g.colorMode($.RGB, 1);
5235
+ $._g.colorMode($.RGB, 1);
5203
5236
 
5204
5237
  $.loadFont = (url, cb) => {
5205
5238
  let ext = url.slice(url.lastIndexOf('.') + 1);
5206
- if (ext != 'json') return g.loadFont(url, cb);
5239
+ if (ext != 'json') return $._g.loadFont(url, cb);
5207
5240
  let fontName = url.slice(url.lastIndexOf('/') + 1, url.lastIndexOf('-'));
5208
5241
  createFont(url, fontName, cb);
5209
5242
  return fontName;
@@ -5388,7 +5421,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5388
5421
  text[0] = x;
5389
5422
  text[1] = -y;
5390
5423
  text[2] = $._textSize / 44;
5391
- text[3] = $._transformIndex;
5424
+ text[3] = $._matrixIndex;
5392
5425
  text[4] = $._fillSet ? $._fill : 0;
5393
5426
  text[5] = $._stroke;
5394
5427
 
@@ -5402,18 +5435,18 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
5402
5435
  };
5403
5436
 
5404
5437
  $.createTextImage = (str, w, h) => {
5405
- g.textSize($._textSize);
5438
+ $._g.textSize($._textSize);
5406
5439
 
5407
5440
  if ($._doFill) {
5408
5441
  let fi = $._fill * 4;
5409
- g.fill(colorStack.slice(fi, fi + 4));
5442
+ $._g.fill(colorStack.slice(fi, fi + 4));
5410
5443
  }
5411
5444
  if ($._doStroke) {
5412
5445
  let si = $._stroke * 4;
5413
- g.stroke(colorStack.slice(si, si + 4));
5446
+ $._g.stroke(colorStack.slice(si, si + 4));
5414
5447
  }
5415
5448
 
5416
- let img = g.createTextImage(str, w, h);
5449
+ let img = $._g.createTextImage(str, w, h);
5417
5450
 
5418
5451
  if (img.canvas.textureIndex == undefined) {
5419
5452
  $._createTexture(img);