@solidtv/renderer 1.1.0 → 1.1.2

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.
Files changed (33) hide show
  1. package/dist/src/core/CoreNode.d.ts +22 -0
  2. package/dist/src/core/CoreNode.js +131 -25
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreTextureManager.js +82 -24
  5. package/dist/src/core/CoreTextureManager.js.map +1 -1
  6. package/dist/src/core/Stage.js +3 -2
  7. package/dist/src/core/Stage.js.map +1 -1
  8. package/dist/src/core/TextureError.d.ts +2 -1
  9. package/dist/src/core/TextureError.js +2 -0
  10. package/dist/src/core/TextureError.js.map +1 -1
  11. package/dist/src/core/lib/ImageWorker.d.ts +1 -1
  12. package/dist/src/core/lib/ImageWorker.js +46 -26
  13. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  14. package/dist/src/core/lib/Matrix3d.d.ts +9 -0
  15. package/dist/src/core/lib/Matrix3d.js +29 -3
  16. package/dist/src/core/lib/Matrix3d.js.map +1 -1
  17. package/dist/src/core/textures/ColorTexture.js +3 -1
  18. package/dist/src/core/textures/ColorTexture.js.map +1 -1
  19. package/dist/src/core/textures/ImageTexture.js +24 -16
  20. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  21. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +1 -1
  24. package/src/core/CoreNode.test.ts +438 -0
  25. package/src/core/CoreNode.ts +140 -39
  26. package/src/core/CoreTextureManager.ts +101 -22
  27. package/src/core/Stage.ts +3 -2
  28. package/src/core/TextureError.ts +2 -0
  29. package/src/core/lib/ImageWorker.ts +50 -27
  30. package/src/core/lib/Matrix3d.test.ts +72 -0
  31. package/src/core/lib/Matrix3d.ts +30 -3
  32. package/src/core/textures/ColorTexture.ts +3 -1
  33. package/src/core/textures/ImageTexture.ts +29 -20
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Matrix3d } from './Matrix3d.js';
3
+
4
+ describe('Matrix3d.setTranslate', () => {
5
+ it('writes tx/ty without touching ta/tb/tc/td', () => {
6
+ const m = Matrix3d.identity();
7
+ m.setTranslate(10, 20);
8
+ expect(m.tx).toBe(10);
9
+ expect(m.ty).toBe(20);
10
+ expect(m.ta).toBe(1);
11
+ expect(m.tb).toBe(0);
12
+ expect(m.tc).toBe(0);
13
+ expect(m.td).toBe(1);
14
+ });
15
+
16
+ it('updates the float array on subsequent getFloatArr() calls', () => {
17
+ const m = Matrix3d.identity();
18
+ m.setTranslate(5, 7);
19
+ // First read populates the array
20
+ const arr = m.getFloatArr();
21
+ expect(arr[6]).toBe(5);
22
+ expect(arr[7]).toBe(7);
23
+
24
+ // Mutate, then expect getFloatArr() to pick up the change.
25
+ m.setTranslate(9, 11);
26
+ const arr2 = m.getFloatArr();
27
+ expect(arr2[6]).toBe(9);
28
+ expect(arr2[7]).toBe(11);
29
+ // Same array reference reused (no GC pressure).
30
+ expect(arr2).toBe(arr);
31
+ });
32
+
33
+ it('overwrites prior tx/ty values', () => {
34
+ const m = Matrix3d.translate(100, 200);
35
+ m.setTranslate(0, 0);
36
+ expect(m.tx).toBe(0);
37
+ expect(m.ty).toBe(0);
38
+ });
39
+ });
40
+
41
+ describe('Matrix3d.rotate(0) fast path', () => {
42
+ it('produces an identity matrix for angle=0', () => {
43
+ const m = Matrix3d.rotate(0);
44
+ expect(m.ta).toBe(1);
45
+ expect(m.tb).toBe(0);
46
+ expect(m.tc).toBe(0);
47
+ expect(m.td).toBe(1);
48
+ expect(m.tx).toBe(0);
49
+ expect(m.ty).toBe(0);
50
+ });
51
+
52
+ it('resets a pre-populated out matrix to identity on angle=0', () => {
53
+ const m = Matrix3d.rotate(Math.PI / 4);
54
+ // Now reuse `m` with angle=0; should overwrite to identity.
55
+ Matrix3d.rotate(0, m);
56
+ expect(m.ta).toBe(1);
57
+ expect(m.tb).toBe(0);
58
+ expect(m.tc).toBe(0);
59
+ expect(m.td).toBe(1);
60
+ expect(m.tx).toBe(0);
61
+ expect(m.ty).toBe(0);
62
+ });
63
+
64
+ it('still produces a real rotation when angle != 0', () => {
65
+ const m = Matrix3d.rotate(Math.PI / 2);
66
+ // cos(pi/2) ~ 0, sin(pi/2) = 1
67
+ expect(Math.abs(m.ta)).toBeLessThan(1e-10);
68
+ expect(m.tb).toBeCloseTo(-1, 10);
69
+ expect(m.tc).toBeCloseTo(1, 10);
70
+ expect(Math.abs(m.td)).toBeLessThan(1e-10);
71
+ });
72
+ });
@@ -124,11 +124,24 @@ export class Matrix3d {
124
124
  }
125
125
 
126
126
  public static rotate(angle: number, out?: Matrix3d): Matrix3d {
127
- const cos = Math.cos(angle);
128
- const sin = Math.sin(angle);
129
- if (!out) {
127
+ if (out === undefined) {
130
128
  out = new Matrix3d();
131
129
  }
130
+ if (angle === 0) {
131
+ // Skip Math.cos(0) / Math.sin(0) — neither V8 nor JSC constant-folds
132
+ // these calls. Identity rotation is the common case when callers
133
+ // combine rotate().scale() and only the scale is non-default.
134
+ out.ta = 1;
135
+ out.tb = 0;
136
+ out.tx = 0;
137
+ out.tc = 0;
138
+ out.td = 1;
139
+ out.ty = 0;
140
+ out.mutation = true;
141
+ return out;
142
+ }
143
+ const cos = Math.cos(angle);
144
+ const sin = Math.sin(angle);
132
145
  out.ta = cos;
133
146
  out.tb = -sin;
134
147
  out.tx = 0;
@@ -160,6 +173,20 @@ export class Matrix3d {
160
173
  return this;
161
174
  }
162
175
 
176
+ /**
177
+ * Writes `tx`/`ty` directly and marks the matrix mutated.
178
+ *
179
+ * Caller is responsible for ensuring `ta`/`tb`/`tc`/`td` are already the
180
+ * desired values — typically the identity rotation/scale (1, 0, 0, 1).
181
+ * Used by hot paths that know the matrix is in identity-shape so the
182
+ * 4 redundant field writes performed by `Matrix3d.translate` can be skipped.
183
+ */
184
+ public setTranslate(x: number, y: number): void {
185
+ this.tx = x;
186
+ this.ty = y;
187
+ this.mutation = true;
188
+ }
189
+
163
190
  public scale(sx: number, sy: number): Matrix3d {
164
191
  this.ta = this.ta * sx;
165
192
  this.tb = this.tb * sy;
@@ -70,7 +70,9 @@ export class ColorTexture extends Texture {
70
70
  }
71
71
 
72
72
  static override makeCacheKey(props: ColorTextureProps): string {
73
- return `ColorTexture,${props.color}`;
73
+ // Mirror the default from resolveDefaults so the key is stable whether
74
+ // or not the caller has run defaults first.
75
+ return `ColorTexture,${props.color || 0xffffffff}`;
74
76
  }
75
77
 
76
78
  static override resolveDefaults(
@@ -147,11 +147,22 @@ export class ImageTexture extends Texture {
147
147
  data: HTMLImageElement | null;
148
148
  premultiplyAlpha: boolean;
149
149
  }>((resolve, reject) => {
150
+ let objectUrl: string | null = null;
151
+
152
+ const cleanup = () => {
153
+ if (objectUrl !== null) {
154
+ URL.revokeObjectURL(objectUrl);
155
+ objectUrl = null;
156
+ }
157
+ };
158
+
150
159
  img.onload = () => {
160
+ cleanup();
151
161
  resolve({ data: img, premultiplyAlpha: hasAlpha });
152
162
  };
153
163
 
154
164
  img.onerror = (err) => {
165
+ cleanup();
155
166
  const errorMessage =
156
167
  err instanceof Error
157
168
  ? err.message
@@ -162,7 +173,8 @@ export class ImageTexture extends Texture {
162
173
  };
163
174
 
164
175
  if (src instanceof Blob) {
165
- img.src = URL.createObjectURL(src);
176
+ objectUrl = URL.createObjectURL(src);
177
+ img.src = objectUrl;
166
178
  } else {
167
179
  img.src = src;
168
180
  }
@@ -218,10 +230,11 @@ export class ImageTexture extends Texture {
218
230
 
219
231
  async loadImage(src: string) {
220
232
  const { premultiplyAlpha, sx, sy, sw, sh } = this.props;
233
+ const isBase64 = isBase64Image(src);
221
234
 
222
235
  if (this.txManager.hasCreateImageBitmap === true) {
223
236
  if (
224
- isBase64Image(src) === false &&
237
+ isBase64 === false &&
225
238
  this.txManager.hasWorker === true &&
226
239
  this.txManager.imageWorkerManager !== null
227
240
  ) {
@@ -235,15 +248,9 @@ export class ImageTexture extends Texture {
235
248
  );
236
249
  }
237
250
 
238
- let blob;
239
-
240
- if (isBase64Image(src) === true) {
241
- blob = dataURIToBlob(src);
242
- } else {
243
- blob = await fetchJson(src, 'blob').then(
244
- (response) => response as Blob,
245
- );
246
- }
251
+ const blob = isBase64
252
+ ? dataURIToBlob(src)
253
+ : ((await fetchJson(src, 'blob')) as Blob);
247
254
 
248
255
  return this.createImageBitmap(blob, premultiplyAlpha, sx, sy, sw, sh);
249
256
  }
@@ -357,16 +364,18 @@ export class ImageTexture extends Texture {
357
364
  return false;
358
365
  }
359
366
 
360
- let cacheKey = `ImageTexture,${key},${props.premultiplyAlpha ?? 'true'},${
361
- props.maxRetryCount
362
- }`;
367
+ // Inline default values so the key is stable whether or not the caller
368
+ // has run them through resolveDefaults first. Must mirror the defaults
369
+ // in resolveDefaults below.
370
+ const premultiplyAlpha = props.premultiplyAlpha ?? true;
371
+ const maxRetryCount = props.maxRetryCount ?? 5;
372
+
373
+ let cacheKey = `ImageTexture,${key},${premultiplyAlpha},${maxRetryCount}`;
363
374
 
364
- if (props.sh !== null && props.sw !== null) {
365
- cacheKey += ',';
366
- cacheKey += props.sx ?? '';
367
- cacheKey += props.sy ?? '';
368
- cacheKey += props.sw || '';
369
- cacheKey += props.sh || '';
375
+ if (props.sh != null && props.sw != null) {
376
+ cacheKey += `,${props.sx ?? ''},${props.sy ?? ''},${props.sw || ''},${
377
+ props.sh || ''
378
+ }`;
370
379
  }
371
380
 
372
381
  return cacheKey;