@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.
- package/dist/src/core/CoreNode.d.ts +22 -0
- package/dist/src/core/CoreNode.js +131 -25
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.js +82 -24
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.js +3 -2
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureError.d.ts +2 -1
- package/dist/src/core/TextureError.js +2 -0
- package/dist/src/core/TextureError.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.d.ts +1 -1
- package/dist/src/core/lib/ImageWorker.js +46 -26
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/Matrix3d.d.ts +9 -0
- package/dist/src/core/lib/Matrix3d.js +29 -3
- package/dist/src/core/lib/Matrix3d.js.map +1 -1
- package/dist/src/core/textures/ColorTexture.js +3 -1
- package/dist/src/core/textures/ColorTexture.js.map +1 -1
- package/dist/src/core/textures/ImageTexture.js +24 -16
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/CoreNode.test.ts +438 -0
- package/src/core/CoreNode.ts +140 -39
- package/src/core/CoreTextureManager.ts +101 -22
- package/src/core/Stage.ts +3 -2
- package/src/core/TextureError.ts +2 -0
- package/src/core/lib/ImageWorker.ts +50 -27
- package/src/core/lib/Matrix3d.test.ts +72 -0
- package/src/core/lib/Matrix3d.ts +30 -3
- package/src/core/textures/ColorTexture.ts +3 -1
- 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
|
+
});
|
package/src/core/lib/Matrix3d.ts
CHANGED
|
@@ -124,11 +124,24 @@ export class Matrix3d {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
public static rotate(angle: number, out?: Matrix3d): Matrix3d {
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
361
|
-
|
|
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
|
|
365
|
-
cacheKey += '
|
|
366
|
-
|
|
367
|
-
|
|
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;
|