@lightningjs/renderer 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +17 -0
  2. package/dist/exports/canvas.d.ts +18 -0
  3. package/dist/exports/canvas.js +37 -0
  4. package/dist/exports/canvas.js.map +1 -0
  5. package/dist/exports/index.d.ts +2 -4
  6. package/dist/exports/index.js +1 -3
  7. package/dist/exports/index.js.map +1 -1
  8. package/dist/exports/inspector.d.ts +1 -0
  9. package/dist/exports/inspector.js +20 -0
  10. package/dist/exports/inspector.js.map +1 -0
  11. package/dist/exports/webgl.d.ts +19 -0
  12. package/dist/exports/webgl.js +38 -0
  13. package/dist/exports/webgl.js.map +1 -0
  14. package/dist/src/common/EventEmitter.d.ts +1 -1
  15. package/dist/src/common/IAnimationController.d.ts +1 -1
  16. package/dist/src/common/IEventEmitter.d.ts +8 -0
  17. package/dist/src/common/IEventEmitter.js +18 -0
  18. package/dist/src/common/IEventEmitter.js.map +1 -0
  19. package/dist/src/core/CoreNode.d.ts +58 -0
  20. package/dist/src/core/CoreNode.js +57 -1
  21. package/dist/src/core/CoreNode.js.map +1 -1
  22. package/dist/src/core/CoreTextNode.d.ts +2 -2
  23. package/dist/src/core/CoreTextNode.js +20 -30
  24. package/dist/src/core/CoreTextNode.js.map +1 -1
  25. package/dist/src/core/Stage.d.ts +11 -5
  26. package/dist/src/core/Stage.js +54 -26
  27. package/dist/src/core/Stage.js.map +1 -1
  28. package/dist/src/core/TextureMemoryManager.js +4 -2
  29. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  30. package/dist/src/core/animations/CoreAnimation.js +1 -1
  31. package/dist/src/core/animations/CoreAnimation.js.map +1 -1
  32. package/dist/src/core/lib/ImageWorker.d.ts +1 -1
  33. package/dist/src/core/lib/ImageWorker.js +25 -3
  34. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  35. package/dist/src/core/lib/textureSvg.d.ts +16 -0
  36. package/dist/src/core/lib/textureSvg.js +63 -0
  37. package/dist/src/core/lib/textureSvg.js.map +1 -0
  38. package/dist/src/core/text-rendering/TrFontManager.js +11 -4
  39. package/dist/src/core/text-rendering/TrFontManager.js.map +1 -1
  40. package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js +13 -5
  41. package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js.map +1 -1
  42. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +1 -0
  43. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +1 -0
  44. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  45. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +1 -0
  46. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +1 -0
  47. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  48. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +1 -0
  49. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  50. package/dist/src/core/textures/ImageTexture.d.ts +52 -0
  51. package/dist/src/core/textures/ImageTexture.js +78 -31
  52. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  53. package/dist/src/core/textures/Texture.d.ts +1 -0
  54. package/dist/src/core/textures/Texture.js +1 -0
  55. package/dist/src/core/textures/Texture.js.map +1 -1
  56. package/dist/src/main-api/Inspector.js +2 -2
  57. package/dist/src/main-api/Inspector.js.map +1 -1
  58. package/dist/src/main-api/Renderer.d.ts +50 -8
  59. package/dist/src/main-api/Renderer.js +8 -7
  60. package/dist/src/main-api/Renderer.js.map +1 -1
  61. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  62. package/dist/tsconfig.tsbuildinfo +1 -0
  63. package/exports/canvas.ts +37 -0
  64. package/exports/index.ts +3 -3
  65. package/exports/inspector.ts +20 -0
  66. package/exports/webgl.ts +38 -0
  67. package/package.json +5 -7
  68. package/src/common/EventEmitter.ts +1 -1
  69. package/src/common/IAnimationController.ts +1 -1
  70. package/src/common/IEventEmitter.ts +28 -0
  71. package/src/core/CoreNode.test.ts +1 -0
  72. package/src/core/CoreNode.ts +120 -1
  73. package/src/core/CoreTextNode.ts +58 -65
  74. package/src/core/Stage.ts +88 -36
  75. package/src/core/TextureMemoryManager.ts +4 -2
  76. package/src/core/animations/CoreAnimation.ts +2 -1
  77. package/src/core/lib/ImageWorker.ts +44 -7
  78. package/src/core/lib/textureSvg.ts +78 -0
  79. package/src/core/text-rendering/TrFontManager.ts +15 -4
  80. package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +15 -8
  81. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +2 -0
  82. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +2 -0
  83. package/src/core/text-rendering/renderers/TextRenderer.ts +1 -0
  84. package/src/core/textures/ImageTexture.ts +159 -35
  85. package/src/core/textures/Texture.ts +2 -0
  86. package/src/main-api/Inspector.ts +2 -2
  87. package/src/main-api/Renderer.ts +59 -15
@@ -25,6 +25,17 @@ interface getImageReturn {
25
25
  premultiplyAlpha: boolean | null;
26
26
  }
27
27
 
28
+ interface ImageWorkerMessage {
29
+ id: number;
30
+ src: string;
31
+ data: getImageReturn;
32
+ error: string;
33
+ sx: number | null;
34
+ sy: number | null;
35
+ sw: number | null;
36
+ sh: number | null;
37
+ }
38
+
28
39
  /**
29
40
  * Note that, within the createImageWorker function, we must only use ES5 code to keep it ES5-valid after babelifying, as
30
41
  * the converted code of this section is converted to a blob and used as the js of the web worker thread.
@@ -43,6 +54,10 @@ function createImageWorker() {
43
54
  function getImage(
44
55
  src: string,
45
56
  premultiplyAlpha: boolean | null,
57
+ x: number | null,
58
+ y: number | null,
59
+ width: number | null,
60
+ height: number | null,
46
61
  ): Promise<getImageReturn> {
47
62
  return new Promise(function (resolve, reject) {
48
63
  var xhr = new XMLHttpRequest();
@@ -60,6 +75,21 @@ function createImageWorker() {
60
75
  ? premultiplyAlpha
61
76
  : hasAlphaChannel(blob.type);
62
77
 
78
+ if (width !== null && height !== null) {
79
+ createImageBitmap(blob, x || 0, y || 0, width, height, {
80
+ premultiplyAlpha: withAlphaChannel ? 'premultiply' : 'none',
81
+ colorSpaceConversion: 'none',
82
+ imageOrientation: 'none',
83
+ })
84
+ .then(function (data) {
85
+ resolve({ data, premultiplyAlpha: premultiplyAlpha });
86
+ })
87
+ .catch(function (error) {
88
+ reject(error);
89
+ });
90
+ return;
91
+ }
92
+
63
93
  createImageBitmap(blob, {
64
94
  premultiplyAlpha: withAlphaChannel ? 'premultiply' : 'none',
65
95
  colorSpaceConversion: 'none',
@@ -87,8 +117,12 @@ function createImageWorker() {
87
117
  var src = event.data.src;
88
118
  var id = event.data.id;
89
119
  var premultiplyAlpha = event.data.premultiplyAlpha;
120
+ var x = event.data.sx;
121
+ var y = event.data.sy;
122
+ var width = event.data.sw;
123
+ var height = event.data.sh;
90
124
 
91
- getImage(src, premultiplyAlpha)
125
+ getImage(src, premultiplyAlpha, x, y, width, height)
92
126
  .then(function (data) {
93
127
  self.postMessage({ id: id, src: src, data: data });
94
128
  })
@@ -114,12 +148,7 @@ export class ImageWorkerManager {
114
148
  }
115
149
 
116
150
  private handleMessage(event: MessageEvent) {
117
- const { id, data, error } = event.data as {
118
- id: number;
119
- src: string;
120
- data?: any;
121
- error?: string;
122
- };
151
+ const { id, data, error } = event.data as ImageWorkerMessage;
123
152
  const msg = this.messageManager[id];
124
153
  if (msg) {
125
154
  const [resolve, reject] = msg;
@@ -155,6 +184,10 @@ export class ImageWorkerManager {
155
184
  getImage(
156
185
  src: string,
157
186
  premultiplyAlpha: boolean | null,
187
+ sx: number | null,
188
+ sy: number | null,
189
+ sw: number | null,
190
+ sh: number | null,
158
191
  ): Promise<TextureData> {
159
192
  return new Promise((resolve, reject) => {
160
193
  try {
@@ -167,6 +200,10 @@ export class ImageWorkerManager {
167
200
  id,
168
201
  src: src,
169
202
  premultiplyAlpha,
203
+ sx,
204
+ sy,
205
+ sw,
206
+ sh,
170
207
  });
171
208
  }
172
209
  }
@@ -0,0 +1,78 @@
1
+ /*
2
+ * If not stated otherwise in this file or this component's LICENSE file the
3
+ * following copyright and licenses apply:
4
+ *
5
+ * Copyright 2023 Comcast Cable Communications Management, LLC.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the License);
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ import { assertTruthy } from '../../utils.js';
21
+ import { type TextureData } from '../textures/Texture.js';
22
+
23
+ /**
24
+ * Tests if the given location is a SVG
25
+ * @param url
26
+ * @remarks
27
+ * This function is used to determine if the given image url is a SVG
28
+ * image
29
+ * @returns
30
+ */
31
+ export function isSvgImage(url: string): boolean {
32
+ return /\.(svg)$/.test(url);
33
+ }
34
+
35
+ /**
36
+ * Loads a SVG image
37
+ * @param url
38
+ * @returns
39
+ */
40
+ export const loadSvg = (
41
+ url: string,
42
+ width: number | null,
43
+ height: number | null,
44
+ sx: number | null,
45
+ sy: number | null,
46
+ sw: number | null,
47
+ sh: number | null,
48
+ ): Promise<TextureData> => {
49
+ return new Promise((resolve, reject) => {
50
+ const canvas = document.createElement('canvas');
51
+ const ctx = canvas.getContext('2d');
52
+ assertTruthy(ctx);
53
+
54
+ ctx.imageSmoothingEnabled = true;
55
+ const img = new Image();
56
+ img.onload = () => {
57
+ const x = sx ?? 0;
58
+ const y = sy ?? 0;
59
+ const w = width || img.width;
60
+ const h = height || img.height;
61
+
62
+ canvas.width = w;
63
+ canvas.height = h;
64
+ ctx.drawImage(img, 0, 0, w, h);
65
+
66
+ resolve({
67
+ data: ctx.getImageData(x, y, sw ?? w, sh ?? h),
68
+ premultiplyAlpha: false,
69
+ });
70
+ };
71
+
72
+ img.onerror = (err) => {
73
+ reject(err);
74
+ };
75
+
76
+ img.src = url;
77
+ });
78
+ };
@@ -19,7 +19,8 @@
19
19
 
20
20
  import type { TrFontFace } from './font-face-types/TrFontFace.js';
21
21
  import type { TextRendererMap, TrFontProps } from './renderers/TextRenderer.js';
22
- import memize from 'memize';
22
+
23
+ const fontCache = new Map<string, TrFontFace>();
23
24
 
24
25
  const weightConversions: { [key: string]: number } = {
25
26
  normal: 400,
@@ -36,7 +37,7 @@ const fontWeightToNumber = (weight: string | number): number => {
36
37
  return weightConversions[weight] || 400;
37
38
  };
38
39
 
39
- function rawResolveFontToUse(
40
+ function resolveFontToUse(
40
41
  familyMapsByPriority: FontFamilyMap[],
41
42
  family: string,
42
43
  weightIn: string | number,
@@ -119,7 +120,6 @@ function rawResolveFontToUse(
119
120
 
120
121
  return;
121
122
  }
122
- const resolveFontToUse = memize(rawResolveFontToUse);
123
123
 
124
124
  /**
125
125
  * Structure mapping font family names to a set of font faces.
@@ -159,12 +159,23 @@ export class TrFontManager {
159
159
  props: TrFontProps,
160
160
  ): TrFontFace | undefined {
161
161
  const { fontFamily, fontWeight, fontStyle, fontStretch } = props;
162
- return resolveFontToUse(
162
+ const fontCacheString = `${fontFamily}${fontStyle}${fontWeight}${fontStretch}`;
163
+
164
+ if (fontCache.has(fontCacheString) === true) {
165
+ return fontCache.get(fontCacheString);
166
+ }
167
+
168
+ const resolvedFont = resolveFontToUse(
163
169
  familyMapsByPriority,
164
170
  fontFamily,
165
171
  fontWeight,
166
172
  fontStyle,
167
173
  fontStretch,
168
174
  );
175
+ if (resolvedFont !== undefined) {
176
+ fontCache.set(fontCacheString, resolvedFont);
177
+ }
178
+
179
+ return resolvedFont;
169
180
  }
170
181
  }
@@ -67,14 +67,21 @@ export class WebTrFontFace extends TrFontFace {
67
67
  cssDescriptors,
68
68
  );
69
69
 
70
- fontFace
71
- .load()
72
- .then(() => {
73
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
74
- (this.loaded as boolean) = true;
75
- this.emit('loaded');
76
- })
77
- .catch(console.error);
70
+ if (fontUrlWithoutParentheses.length > 0) {
71
+ fontFace
72
+ .load()
73
+ .then(() => {
74
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
75
+ (this.loaded as boolean) = true;
76
+ this.emit('loaded');
77
+ })
78
+ .catch(console.error);
79
+ } else {
80
+ // Default font
81
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
82
+ (this.loaded as boolean) = true;
83
+ this.emit('loaded');
84
+ }
78
85
 
79
86
  this.fontFace = fontFace;
80
87
  this.fontUrl = fontUrl;
@@ -90,6 +90,8 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
90
90
  private fontFamilies: FontFamilyMap = {};
91
91
  private fontFamilyArray: FontFamilyMap[] = [this.fontFamilies];
92
92
 
93
+ public type: 'canvas' | 'sdf' = 'canvas';
94
+
93
95
  constructor(stage: Stage) {
94
96
  super(stage);
95
97
  if (typeof OffscreenCanvas !== 'undefined') {
@@ -142,6 +142,8 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
142
142
  private sdfShader: SdfShader;
143
143
  private rendererBounds: Bound;
144
144
 
145
+ public type: 'canvas' | 'sdf' = 'sdf';
146
+
145
147
  constructor(stage: Stage) {
146
148
  super(stage);
147
149
  this.sdfShader = this.stage.shManager.loadShader('SdfShader').shader;
@@ -422,6 +422,7 @@ export abstract class TextRenderer<
422
422
  StateT extends TextRendererState = TextRendererState,
423
423
  > {
424
424
  readonly set: Readonly<TrPropSetters<StateT>>;
425
+ abstract type: 'canvas' | 'sdf';
425
426
 
426
427
  constructor(protected stage: Stage) {
427
428
  const propSetters = {
@@ -24,6 +24,7 @@ import {
24
24
  loadCompressedTexture,
25
25
  } from '../lib/textureCompression.js';
26
26
  import { convertUrlToAbsolute } from '../lib/utils.js';
27
+ import { isSvgImage, loadSvg } from '../lib/textureSvg.js';
27
28
 
28
29
  /**
29
30
  * Properties of the {@link ImageTexture}
@@ -55,6 +56,52 @@ export interface ImageTextureProps {
55
56
  * `ImageData` textures are not cached unless a `key` is provided
56
57
  */
57
58
  key?: string | null;
59
+ /**
60
+ * Width of the image to be used as a texture. If not provided, the image's
61
+ * natural width will be used.
62
+ */
63
+ width?: number | null;
64
+ /**
65
+ * Height of the image to be used as a texture. If not provided, the image's
66
+ * natural height will be used.
67
+ */
68
+ height?: number | null;
69
+ /**
70
+ * Type, indicate an image type for overriding type detection
71
+ *
72
+ * @default null
73
+ */
74
+ type?: 'regular' | 'compressed' | 'svg' | null;
75
+ /**
76
+ * The width of the rectangle from which the ImageBitmap will be extracted. This value
77
+ * can be negative. Only works when createImageBitmap is supported on the browser.
78
+ *
79
+ * @default null
80
+ */
81
+ sw?: number | null;
82
+ /**
83
+ * The height of the rectangle from which the ImageBitmap will be extracted. This value
84
+ * can be negative. Only works when createImageBitmap is supported on the browser.
85
+ *
86
+ * @default null
87
+ */
88
+ sh?: number | null;
89
+ /**
90
+ * The y coordinate of the reference point of the rectangle from which the ImageBitmap
91
+ * will be extracted. Only used when `sw` and `sh` are provided. And only works when
92
+ * createImageBitmap is available.
93
+ *
94
+ * @default null
95
+ */
96
+ sx?: number | null;
97
+ /**
98
+ * The x coordinate of the reference point of the rectangle from which the
99
+ * ImageBitmap will be extracted. Only used when source `sw` width and `sh` height
100
+ * are provided. Only works when createImageBitmap is supported on the browser.
101
+ *
102
+ * @default null
103
+ */
104
+ sy?: number | null;
58
105
  }
59
106
 
60
107
  /**
@@ -83,45 +130,35 @@ export class ImageTexture extends Texture {
83
130
  return mimeType.indexOf('image/png') !== -1;
84
131
  }
85
132
 
86
- override async getTextureData(): Promise<TextureData> {
87
- const { src, premultiplyAlpha } = this.props;
88
- if (!src) {
89
- return {
90
- data: null,
91
- };
92
- }
93
-
94
- if (typeof src !== 'string') {
95
- if (src instanceof ImageData) {
96
- return {
97
- data: src,
98
- premultiplyAlpha,
99
- };
100
- }
101
- return {
102
- data: src(),
103
- premultiplyAlpha,
104
- };
105
- }
106
-
107
- // Handle compressed textures
108
- if (isCompressedTextureContainer(src)) {
109
- return loadCompressedTexture(src);
110
- }
133
+ async loadImage(src: string) {
134
+ const { premultiplyAlpha, sx, sy, sw, sh, width, height } = this.props;
111
135
 
112
- // Convert relative URL to absolute URL
113
- const absoluteSrc = convertUrlToAbsolute(src);
114
-
115
- if (this.txManager.imageWorkerManager) {
136
+ if (this.txManager.imageWorkerManager !== null) {
116
137
  return await this.txManager.imageWorkerManager.getImage(
117
- absoluteSrc,
138
+ src,
118
139
  premultiplyAlpha,
140
+ sx,
141
+ sy,
142
+ sw,
143
+ sh,
119
144
  );
120
- } else if (this.txManager.hasCreateImageBitmap) {
121
- const response = await fetch(absoluteSrc);
145
+ } else if (this.txManager.hasCreateImageBitmap === true) {
146
+ const response = await fetch(src);
122
147
  const blob = await response.blob();
123
148
  const hasAlphaChannel =
124
149
  premultiplyAlpha ?? this.hasAlphaChannel(blob.type);
150
+
151
+ if (sw !== null && sh !== null) {
152
+ return {
153
+ data: await createImageBitmap(blob, sx ?? 0, sy ?? 0, sw, sh, {
154
+ premultiplyAlpha: hasAlphaChannel ? 'premultiply' : 'none',
155
+ colorSpaceConversion: 'none',
156
+ imageOrientation: 'none',
157
+ }),
158
+ premultiplyAlpha: hasAlphaChannel,
159
+ };
160
+ }
161
+
125
162
  return {
126
163
  data: await createImageBitmap(blob, {
127
164
  premultiplyAlpha: hasAlphaChannel ? 'premultiply' : 'none',
@@ -131,11 +168,11 @@ export class ImageTexture extends Texture {
131
168
  premultiplyAlpha: hasAlphaChannel,
132
169
  };
133
170
  } else {
134
- const img = new Image();
171
+ const img = new Image(width || undefined, height || undefined);
135
172
  if (!(src.substr(0, 5) === 'data:')) {
136
173
  img.crossOrigin = 'Anonymous';
137
174
  }
138
- img.src = absoluteSrc;
175
+ img.src = src;
139
176
  await new Promise<void>((resolve, reject) => {
140
177
  img.onload = () => resolve();
141
178
  img.onerror = () => reject(new Error(`Failed to load image`));
@@ -150,6 +187,73 @@ export class ImageTexture extends Texture {
150
187
  }
151
188
  }
152
189
 
190
+ override async getTextureData(): Promise<TextureData> {
191
+ const { src, premultiplyAlpha, type } = this.props;
192
+ if (src === null) {
193
+ return {
194
+ data: null,
195
+ };
196
+ }
197
+
198
+ if (typeof src !== 'string') {
199
+ if (src instanceof ImageData) {
200
+ return {
201
+ data: src,
202
+ premultiplyAlpha,
203
+ };
204
+ }
205
+ return {
206
+ data: src(),
207
+ premultiplyAlpha,
208
+ };
209
+ }
210
+
211
+ const absoluteSrc = convertUrlToAbsolute(src);
212
+ if (type === 'regular') {
213
+ return this.loadImage(absoluteSrc);
214
+ }
215
+
216
+ if (type === 'svg') {
217
+ return loadSvg(
218
+ absoluteSrc,
219
+ this.props.width,
220
+ this.props.height,
221
+ this.props.sx,
222
+ this.props.sy,
223
+ this.props.sw,
224
+ this.props.sh,
225
+ );
226
+ }
227
+
228
+ if (isSvgImage(src) === true) {
229
+ return loadSvg(
230
+ absoluteSrc,
231
+ this.props.width,
232
+ this.props.height,
233
+ this.props.sx,
234
+ this.props.sy,
235
+ this.props.sw,
236
+ this.props.sh,
237
+ );
238
+ }
239
+
240
+ if (type === 'compressed') {
241
+ return loadCompressedTexture(absoluteSrc);
242
+ }
243
+
244
+ if (isCompressedTextureContainer(src) === true) {
245
+ return loadCompressedTexture(absoluteSrc);
246
+ }
247
+
248
+ // default
249
+ return this.loadImage(absoluteSrc);
250
+ }
251
+
252
+ /**
253
+ * Generates a cache key for the ImageTexture based on the provided props.
254
+ * @param props - The props used to generate the cache key.
255
+ * @returns The cache key as a string, or `false` if the key cannot be generated.
256
+ */
153
257
  static override makeCacheKey(props: ImageTextureProps): string | false {
154
258
  const resolvedProps = ImageTexture.resolveDefaults(props);
155
259
  // Only cache key-able textures; prioritise key
@@ -157,7 +261,20 @@ export class ImageTexture extends Texture {
157
261
  if (typeof key !== 'string') {
158
262
  return false;
159
263
  }
160
- return `ImageTexture,${key},${resolvedProps.premultiplyAlpha ?? 'true'}`;
264
+
265
+ // if we have source dimensions, cache the texture separately
266
+ let dimensionProps = '';
267
+ if (resolvedProps.sh !== null && resolvedProps.sw !== null) {
268
+ dimensionProps += ',';
269
+ dimensionProps += resolvedProps.sx ?? '';
270
+ dimensionProps += resolvedProps.sy ?? '';
271
+ dimensionProps += resolvedProps.sw || '';
272
+ dimensionProps += resolvedProps.sh || '';
273
+ }
274
+
275
+ return `ImageTexture,${key},${
276
+ resolvedProps.premultiplyAlpha ?? 'true'
277
+ }${dimensionProps}`;
161
278
  }
162
279
 
163
280
  static override resolveDefaults(
@@ -167,6 +284,13 @@ export class ImageTexture extends Texture {
167
284
  src: props.src ?? '',
168
285
  premultiplyAlpha: props.premultiplyAlpha ?? true, // null,
169
286
  key: props.key ?? null,
287
+ type: props.type ?? null,
288
+ width: props.width ?? null,
289
+ height: props.height ?? null,
290
+ sx: props.sx ?? null,
291
+ sy: props.sy ?? null,
292
+ sw: props.sw ?? null,
293
+ sh: props.sh ?? null,
170
294
  };
171
295
  }
172
296
 
@@ -150,6 +150,8 @@ export abstract class Texture extends EventEmitter {
150
150
 
151
151
  readonly lastRenderableChangeTime = 0;
152
152
 
153
+ public preventCleanup = false;
154
+
153
155
  constructor(protected txManager: CoreTextureManager) {
154
156
  super();
155
157
  }
@@ -128,7 +128,7 @@ const convertColorToRgba = (color: number) => {
128
128
  };
129
129
 
130
130
  const domPropertyMap: { [key: string]: string } = {
131
- id: 'id',
131
+ id: 'test-id',
132
132
  };
133
133
 
134
134
  const gradientColorPropertyMap = [
@@ -163,7 +163,7 @@ export class Inspector {
163
163
  );
164
164
 
165
165
  this.width = Math.ceil(
166
- settings.appWidth ?? 1900 / (settings.deviceLogicalPixelRatio ?? 1),
166
+ settings.appWidth ?? 1920 / (settings.deviceLogicalPixelRatio ?? 1),
167
167
  );
168
168
 
169
169
  this.scaleX = settings.deviceLogicalPixelRatio ?? 1;