@lightningjs/renderer 0.7.0 → 0.7.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 (83) hide show
  1. package/dist/exports/main-api.d.ts +1 -0
  2. package/dist/src/core/CoreNode.d.ts +4 -7
  3. package/dist/src/core/CoreNode.js +29 -25
  4. package/dist/src/core/CoreNode.js.map +1 -1
  5. package/dist/src/core/CoreShaderManager.d.ts +13 -6
  6. package/dist/src/core/CoreShaderManager.js +6 -6
  7. package/dist/src/core/CoreShaderManager.js.map +1 -1
  8. package/dist/src/core/CoreTextNode.d.ts +2 -2
  9. package/dist/src/core/CoreTextNode.js +1 -1
  10. package/dist/src/core/CoreTextNode.js.map +1 -1
  11. package/dist/src/core/Stage.js +1 -1
  12. package/dist/src/core/Stage.js.map +1 -1
  13. package/dist/src/core/animations/CoreAnimation.d.ts +1 -0
  14. package/dist/src/core/animations/CoreAnimation.js +7 -0
  15. package/dist/src/core/animations/CoreAnimation.js.map +1 -1
  16. package/dist/src/core/lib/WebGlContextWrapper.d.ts +9 -0
  17. package/dist/src/core/lib/WebGlContextWrapper.js +12 -0
  18. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  19. package/dist/src/core/lib/textureCompression.d.ts +16 -0
  20. package/dist/src/core/lib/textureCompression.js +129 -0
  21. package/dist/src/core/lib/textureCompression.js.map +1 -0
  22. package/dist/src/core/lib/utils.d.ts +9 -0
  23. package/dist/src/core/lib/utils.js +48 -1
  24. package/dist/src/core/lib/utils.js.map +1 -1
  25. package/dist/src/core/renderers/CoreRenderer.d.ts +2 -2
  26. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +12 -0
  27. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  28. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +3 -3
  29. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +1 -1
  30. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -1
  31. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +5 -0
  32. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  33. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.d.ts +4 -0
  34. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +11 -0
  35. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  36. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js.map +1 -1
  37. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -2
  38. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +10 -1
  39. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  40. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +21 -7
  41. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
  42. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +6 -10
  43. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +73 -46
  44. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  45. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +2 -1
  46. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  47. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js +1 -1
  48. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js.map +1 -1
  49. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +2 -2
  50. package/dist/src/core/textures/ImageTexture.js +5 -0
  51. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  52. package/dist/src/core/textures/Texture.d.ts +28 -1
  53. package/dist/src/core/textures/Texture.js.map +1 -1
  54. package/dist/src/main-api/Inspector.d.ts +15 -0
  55. package/dist/src/main-api/Inspector.js +216 -0
  56. package/dist/src/main-api/Inspector.js.map +1 -0
  57. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  58. package/exports/main-api.ts +9 -0
  59. package/package.json +1 -1
  60. package/src/core/CoreNode.ts +30 -29
  61. package/src/core/CoreShaderManager.ts +39 -6
  62. package/src/core/CoreTextNode.ts +2 -2
  63. package/src/core/Stage.ts +1 -1
  64. package/src/core/animations/CoreAnimation.ts +8 -0
  65. package/src/core/lib/WebGlContextWrapper.ts +27 -0
  66. package/src/core/lib/textureCompression.ts +152 -0
  67. package/src/core/lib/utils.ts +68 -1
  68. package/src/core/renderers/CoreRenderer.ts +2 -2
  69. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +20 -0
  70. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +3 -3
  71. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +7 -1
  72. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +15 -1
  73. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.test.ts +12 -4
  74. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +4 -1
  75. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +16 -2
  76. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +42 -8
  77. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +96 -60
  78. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +2 -1
  79. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.test.ts +4 -1
  80. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.ts +1 -1
  81. package/src/core/text-rendering/renderers/TextRenderer.ts +2 -2
  82. package/src/core/textures/ImageTexture.ts +9 -0
  83. package/src/core/textures/Texture.ts +33 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/renderer",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Lightning 3 Renderer",
5
5
  "type": "module",
6
6
  "main": "./dist/exports/index.js",
@@ -37,7 +37,7 @@ import type {
37
37
  NodeTextureLoadedPayload,
38
38
  } from '../common/CommonTypes.js';
39
39
  import { EventEmitter } from '../common/EventEmitter.js';
40
- import { intersectRect, type Rect } from './lib/utils.js';
40
+ import { copyRect, intersectRect, type RectWithValid } from './lib/utils.js';
41
41
  import { Matrix3d } from './lib/Matrix3d.js';
42
42
 
43
43
  export interface CoreNodeProps {
@@ -150,9 +150,14 @@ export class CoreNode extends EventEmitter implements ICoreNode {
150
150
  public globalTransform?: Matrix3d;
151
151
  public scaleRotateTransform?: Matrix3d;
152
152
  public localTransform?: Matrix3d;
153
- public clippingRect: Rect | null = null;
153
+ public clippingRect: RectWithValid = {
154
+ x: 0,
155
+ y: 0,
156
+ width: 0,
157
+ height: 0,
158
+ valid: false,
159
+ };
154
160
  public isRenderable = false;
155
- private parentClippingRect: Rect | null = null;
156
161
  public worldAlpha = 1;
157
162
  public premultipliedColorTl = 0;
158
163
  public premultipliedColorTr = 0;
@@ -296,7 +301,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
296
301
  * @todo: test for correct calculation flag
297
302
  * @param delta
298
303
  */
299
- update(delta: number, parentClippingRect: Rect | null = null): void {
304
+ update(delta: number, parentClippingRect: RectWithValid): void {
300
305
  if (this.updateType & UpdateType.ScaleRotate) {
301
306
  this.updateScaleRotateTransform();
302
307
  this.setUpdateType(UpdateType.Local);
@@ -473,40 +478,36 @@ export class CoreNode extends EventEmitter implements ICoreNode {
473
478
  /**
474
479
  * This function calculates the clipping rectangle for a node.
475
480
  *
476
- * If the parent clipping rectangle has not changed and the node's clipping rectangle is already set, the function returns immediately.
477
- *
478
481
  * The function then checks if the node is rotated. If the node requires clipping and is not rotated, a new clipping rectangle is created based on the node's global transform and dimensions.
479
482
  * If a parent clipping rectangle exists, it is intersected with the node's clipping rectangle (if it exists), or replaces the node's clipping rectangle.
480
483
  *
481
484
  * Finally, the node's parentClippingRect and clippingRect properties are updated.
482
485
  */
483
- calculateClippingRect(parentClippingRect: Rect | null = null) {
486
+ calculateClippingRect(parentClippingRect: RectWithValid) {
484
487
  assertTruthy(this.globalTransform);
488
+ const { clippingRect, props, globalTransform: gt } = this;
489
+ const { clipping } = props;
485
490
 
486
- if (this.parentClippingRect === parentClippingRect && this.clippingRect) {
487
- return;
488
- }
489
-
490
- const gt = this.globalTransform;
491
491
  const isRotated = gt.tb !== 0 || gt.tc !== 0;
492
492
 
493
- let clippingRect: Rect | null =
494
- this.props.clipping && !isRotated
495
- ? {
496
- x: gt.tx,
497
- y: gt.ty,
498
- width: this.width * gt.ta,
499
- height: this.height * gt.td,
500
- }
501
- : null;
502
- if (parentClippingRect && clippingRect) {
503
- clippingRect = intersectRect(parentClippingRect, clippingRect);
504
- } else if (parentClippingRect) {
505
- clippingRect = parentClippingRect;
506
- }
507
-
508
- this.parentClippingRect = parentClippingRect;
509
- this.clippingRect = clippingRect;
493
+ if (clipping && !isRotated) {
494
+ clippingRect.x = gt.tx;
495
+ clippingRect.y = gt.ty;
496
+ clippingRect.width = this.width * gt.ta;
497
+ clippingRect.height = this.height * gt.td;
498
+ clippingRect.valid = true;
499
+ } else {
500
+ clippingRect.valid = false;
501
+ }
502
+
503
+ if (parentClippingRect.valid && clippingRect.valid) {
504
+ // Intersect parent clipping rect with node clipping rect
505
+ intersectRect(parentClippingRect, clippingRect, clippingRect);
506
+ } else if (parentClippingRect.valid) {
507
+ // Copy parent clipping rect
508
+ copyRect(parentClippingRect, clippingRect);
509
+ clippingRect.valid = true;
510
+ }
510
511
  }
511
512
 
512
513
  calculateZIndex(): void {
@@ -31,17 +31,42 @@ import { SdfShader } from './renderers/webgl/shaders/SdfShader.js';
31
31
 
32
32
  import { RadiusEffect } from './renderers/webgl/shaders/effects/RadiusEffect.js';
33
33
  import { BorderEffect } from './renderers/webgl/shaders/effects/BorderEffect.js';
34
- import { LinearGradientEffect } from './renderers/webgl/shaders/effects/LinearGradientEffect.js';
35
- import { GrayscaleEffect } from './renderers/webgl/shaders/effects/GrayscaleEffect.js';
34
+ import {
35
+ LinearGradientEffect,
36
+ type LinearGradientEffectProps,
37
+ } from './renderers/webgl/shaders/effects/LinearGradientEffect.js';
38
+ import {
39
+ GrayscaleEffect,
40
+ type GrayscaleEffectProps,
41
+ } from './renderers/webgl/shaders/effects/GrayscaleEffect.js';
36
42
  import { BorderRightEffect } from './renderers/webgl/shaders/effects/BorderRightEffect.js';
37
43
  import { BorderTopEffect } from './renderers/webgl/shaders/effects/BorderTopEffect.js';
38
44
  import { BorderBottomEffect } from './renderers/webgl/shaders/effects/BorderBottomEffect.js';
39
45
  import { BorderLeftEffect } from './renderers/webgl/shaders/effects/BorderLeftEffect.js';
40
- import { GlitchEffect } from './renderers/webgl/shaders/effects/GlitchEffect.js';
41
- import { FadeOutEffect } from './renderers/webgl/shaders/effects/FadeOutEffect.js';
42
- import { RadialGradientEffect } from './renderers/webgl/shaders/effects/RadialGradientEffect.js';
46
+ import {
47
+ GlitchEffect,
48
+ type GlitchEffectProps,
49
+ } from './renderers/webgl/shaders/effects/GlitchEffect.js';
50
+ import {
51
+ FadeOutEffect,
52
+ type FadeOutEffectProps,
53
+ } from './renderers/webgl/shaders/effects/FadeOutEffect.js';
54
+ import {
55
+ RadialGradientEffect,
56
+ type RadialGradientEffectProps,
57
+ } from './renderers/webgl/shaders/effects/RadialGradientEffect.js';
43
58
  import type { WebGlCoreRenderer } from './renderers/webgl/WebGlCoreRenderer.js';
44
- import { RadialProgressEffect } from './renderers/webgl/shaders/effects/RadialProgressEffect.js';
59
+ import {
60
+ RadialProgressEffect,
61
+ type RadialProgressEffectProps,
62
+ } from './renderers/webgl/shaders/effects/RadialProgressEffect.js';
63
+
64
+ export type { FadeOutEffectProps };
65
+ export type { LinearGradientEffectProps };
66
+ export type { RadialGradientEffectProps };
67
+ export type { GrayscaleEffectProps };
68
+ export type { GlitchEffectProps };
69
+ export type { RadialProgressEffectProps };
45
70
 
46
71
  export interface ShaderMap {
47
72
  DefaultShader: typeof DefaultShader;
@@ -71,6 +96,14 @@ export interface EffectMap {
71
96
  radialProgress: typeof RadialProgressEffect;
72
97
  }
73
98
 
99
+ export type EffectProps =
100
+ | FadeOutEffectProps
101
+ | LinearGradientEffectProps
102
+ | RadialGradientEffectProps
103
+ | GrayscaleEffectProps
104
+ | GlitchEffectProps
105
+ | RadialProgressEffectProps;
106
+
74
107
  export class CoreShaderManager {
75
108
  protected shCache: Map<string, InstanceType<ShaderMap[keyof ShaderMap]>> =
76
109
  new Map();
@@ -32,7 +32,7 @@ import type {
32
32
  NodeTextFailedPayload,
33
33
  NodeTextLoadedPayload,
34
34
  } from '../common/CommonTypes.js';
35
- import type { Rect } from './lib/utils.js';
35
+ import type { Rect, RectWithValid } from './lib/utils.js';
36
36
  import { assertTruthy } from '../utils.js';
37
37
 
38
38
  export interface CoreTextNodeProps extends CoreNodeProps, TrProps {
@@ -318,7 +318,7 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
318
318
  this.textRenderer.set.debug(this.trState, value);
319
319
  }
320
320
 
321
- override update(delta: number, parentClippingRect: Rect | null = null) {
321
+ override update(delta: number, parentClippingRect: RectWithValid) {
322
322
  super.update(delta, parentClippingRect);
323
323
 
324
324
  assertTruthy(this.globalTransform);
package/src/core/Stage.ts CHANGED
@@ -210,7 +210,7 @@ export class Stage extends EventEmitter {
210
210
 
211
211
  // Update tree if needed
212
212
  if (this.root.updateType !== 0) {
213
- this.root.update(this.deltaTime);
213
+ this.root.update(this.deltaTime, this.root.clippingRect);
214
214
  }
215
215
 
216
216
  // test if we need to update the scene
@@ -37,6 +37,7 @@ export class CoreAnimation extends EventEmitter {
37
37
  public propStartValues: Partial<INodeAnimatableProps> = {};
38
38
  public restoreValues: Partial<INodeAnimatableProps> = {};
39
39
  private progress = 0;
40
+ private delayFor = 0;
40
41
  private timingFunction: (t: number) => number | undefined;
41
42
  private propsList: Array<keyof INodeAnimatableProps>; //fixme - aint got not time for this
42
43
 
@@ -57,10 +58,12 @@ export class CoreAnimation extends EventEmitter {
57
58
  if (settings.easing && typeof settings.easing === 'string') {
58
59
  this.timingFunction = getTimingFunction(settings.easing);
59
60
  }
61
+ this.delayFor = settings.delay || 0;
60
62
  }
61
63
 
62
64
  reset() {
63
65
  this.progress = 0;
66
+ this.delayFor = this.settings.delay || 0;
64
67
  this.update(0);
65
68
  }
66
69
 
@@ -104,6 +107,11 @@ export class CoreAnimation extends EventEmitter {
104
107
  return;
105
108
  }
106
109
 
110
+ if (this.delayFor > 0) {
111
+ this.delayFor -= dt;
112
+ return;
113
+ }
114
+
107
115
  this.progress += dt / duration;
108
116
 
109
117
  if (this.progress > 1) {
@@ -335,7 +335,34 @@ export class WebGlContextWrapper {
335
335
  );
336
336
  }
337
337
  }
338
+ /**
339
+ * ```
340
+ * gl.compressedTexImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, data);
341
+ * ```
342
+ *
343
+ * @remarks
344
+ * **WebGL Difference**: Bind target is always `gl.TEXTURE_2D`
345
+ */
338
346
 
347
+ compressedTexImage2D(
348
+ level: GLint,
349
+ internalformat: GLenum,
350
+ width: GLsizei,
351
+ height: GLsizei,
352
+ border: GLint,
353
+ data?: ArrayBufferView,
354
+ ): void {
355
+ const { gl } = this;
356
+ gl.compressedTexImage2D(
357
+ gl.TEXTURE_2D,
358
+ level,
359
+ internalformat,
360
+ width,
361
+ height,
362
+ border,
363
+ data as ArrayBufferView,
364
+ );
365
+ }
339
366
  /**
340
367
  * ```
341
368
  * gl.pixelStorei(pname, param);
@@ -0,0 +1,152 @@
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 { type TextureData } from '../textures/Texture.js';
21
+
22
+ /**
23
+ * Tests if the given location is a compressed texture container
24
+ * @param url
25
+ * @remarks
26
+ * This function is used to determine if the given image url is a compressed
27
+ * and only supports the following extensions: .ktx and .pvr
28
+ * @returns
29
+ */
30
+ export function isCompressedTextureContainer(url: string): boolean {
31
+ return /\.(ktx|pvr)$/.test(url);
32
+ }
33
+
34
+ /**
35
+ * Loads a compressed texture container
36
+ * @param url
37
+ * @returns
38
+ */
39
+ export const loadCompressedTexture = async (
40
+ url: string,
41
+ ): Promise<TextureData> => {
42
+ const response = await fetch(url);
43
+ const arrayBuffer = await response.arrayBuffer();
44
+
45
+ if (url.indexOf('.ktx') !== -1) {
46
+ return loadKTXData(arrayBuffer);
47
+ }
48
+
49
+ return loadPVRData(arrayBuffer);
50
+ };
51
+
52
+ /**
53
+ * Loads a KTX texture container and returns the texture data
54
+ * @param buffer
55
+ * @returns
56
+ */
57
+ const loadKTXData = async (buffer: ArrayBuffer): Promise<TextureData> => {
58
+ const view = new DataView(buffer);
59
+ const littleEndian = view.getUint32(12) === 16909060 ? true : false;
60
+ const mipmaps = [];
61
+
62
+ const data = {
63
+ glInternalFormat: view.getUint32(28, littleEndian),
64
+ pixelWidth: view.getUint32(36, littleEndian),
65
+ pixelHeight: view.getUint32(40, littleEndian),
66
+ numberOfMipmapLevels: view.getUint32(56, littleEndian),
67
+ bytesOfKeyValueData: view.getUint32(60, littleEndian),
68
+ };
69
+
70
+ let offset = 64;
71
+
72
+ // Key Value Pairs of data start at byte offset 64
73
+ // But the only known kvp is the API version, so skipping parsing.
74
+ offset += data.bytesOfKeyValueData;
75
+
76
+ for (let i = 0; i < data.numberOfMipmapLevels; i++) {
77
+ const imageSize = view.getUint32(offset);
78
+ offset += 4;
79
+
80
+ mipmaps.push(view.buffer.slice(offset, imageSize));
81
+ offset += imageSize;
82
+ }
83
+
84
+ return {
85
+ data: {
86
+ glInternalFormat: data.glInternalFormat,
87
+ mipmaps,
88
+ width: data.pixelWidth || 0,
89
+ height: data.pixelHeight || 0,
90
+ type: 'ktx',
91
+ },
92
+ premultiplyAlpha: false,
93
+ };
94
+ };
95
+
96
+ /**
97
+ * Loads a PVR texture container and returns the texture data
98
+ * @param buffer
99
+ * @returns
100
+ */
101
+ const loadPVRData = async (buffer: ArrayBuffer): Promise<TextureData> => {
102
+ // pvr header length in 32 bits
103
+ const pvrHeaderLength = 13;
104
+ // for now only we only support: COMPRESSED_RGB_ETC1_WEBGL
105
+ const pvrFormatEtc1 = 0x8d64;
106
+ const pvrWidth = 7;
107
+ const pvrHeight = 6;
108
+ const pvrMipmapCount = 11;
109
+ const pvrMetadata = 12;
110
+ const arrayBuffer = buffer;
111
+ const header = new Int32Array(arrayBuffer, 0, pvrHeaderLength);
112
+
113
+ // @ts-expect-error Object possibly undefined
114
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
115
+ const dataOffset = header[pvrMetadata] + 52;
116
+ const pvrtcData = new Uint8Array(arrayBuffer, dataOffset);
117
+ const mipmaps = [];
118
+ const data = {
119
+ pixelWidth: header[pvrWidth],
120
+ pixelHeight: header[pvrHeight],
121
+ numberOfMipmapLevels: header[pvrMipmapCount] || 0,
122
+ };
123
+
124
+ let offset = 0;
125
+ let width = data.pixelWidth || 0;
126
+ let height = data.pixelHeight || 0;
127
+
128
+ for (let i = 0; i < data.numberOfMipmapLevels; i++) {
129
+ const level = ((width + 3) >> 2) * ((height + 3) >> 2) * 8;
130
+ const view = new Uint8Array(
131
+ arrayBuffer,
132
+ pvrtcData.byteOffset + offset,
133
+ level,
134
+ );
135
+
136
+ mipmaps.push(view);
137
+ offset += level;
138
+ width = width >> 1;
139
+ height = height >> 1;
140
+ }
141
+
142
+ return {
143
+ data: {
144
+ glInternalFormat: pvrFormatEtc1,
145
+ mipmaps: mipmaps,
146
+ width: data.pixelWidth || 0,
147
+ height: data.pixelHeight || 0,
148
+ type: 'pvr',
149
+ },
150
+ premultiplyAlpha: false,
151
+ };
152
+ };
@@ -81,6 +81,10 @@ export interface Rect {
81
81
  height: number;
82
82
  }
83
83
 
84
+ export interface RectWithValid extends Rect {
85
+ valid: boolean;
86
+ }
87
+
84
88
  export interface Bound {
85
89
  x1: number;
86
90
  y1: number;
@@ -132,12 +136,50 @@ export function intersectBound<T extends Bound = Bound>(
132
136
  return createBound(0, 0, 0, 0, intersection);
133
137
  }
134
138
 
135
- export function intersectRect(a: Rect, b: Rect): Rect {
139
+ export function boundsOverlap(a: Bound, b: Bound): boolean {
140
+ return a.x1 < b.x2 && a.x2 > b.x1 && a.y1 < b.y2 && a.y2 > b.y1;
141
+ }
142
+
143
+ export function convertBoundToRect(bound: Bound): Rect;
144
+ export function convertBoundToRect<T extends Rect = Rect>(
145
+ bound: Bound,
146
+ out: T,
147
+ ): T;
148
+ export function convertBoundToRect(bound: Bound, out?: Rect): Rect {
149
+ if (out) {
150
+ out.x = bound.x1;
151
+ out.y = bound.y1;
152
+ out.width = bound.x2 - bound.x1;
153
+ out.height = bound.y2 - bound.y1;
154
+ return out;
155
+ }
156
+ return {
157
+ x: bound.x1,
158
+ y: bound.y1,
159
+ width: bound.x2 - bound.x1,
160
+ height: bound.y2 - bound.y1,
161
+ };
162
+ }
163
+
164
+ export function intersectRect(a: Rect, b: Rect): Rect;
165
+ export function intersectRect<T extends Rect = Rect>(
166
+ a: Rect,
167
+ b: Rect,
168
+ out: T,
169
+ ): T;
170
+ export function intersectRect(a: Rect, b: Rect, out?: Rect): Rect {
136
171
  const x = Math.max(a.x, b.x);
137
172
  const y = Math.max(a.y, b.y);
138
173
  const width = Math.min(a.x + a.width, b.x + b.width) - x;
139
174
  const height = Math.min(a.y + a.height, b.y + b.height) - y;
140
175
  if (width > 0 && height > 0) {
176
+ if (out) {
177
+ out.x = x;
178
+ out.y = y;
179
+ out.width = width;
180
+ out.height = height;
181
+ return out;
182
+ }
141
183
  return {
142
184
  x,
143
185
  y,
@@ -145,6 +187,13 @@ export function intersectRect(a: Rect, b: Rect): Rect {
145
187
  height,
146
188
  };
147
189
  }
190
+ if (out) {
191
+ out.x = 0;
192
+ out.y = 0;
193
+ out.width = 0;
194
+ out.height = 0;
195
+ return out;
196
+ }
148
197
  return {
149
198
  x: 0,
150
199
  y: 0,
@@ -153,6 +202,24 @@ export function intersectRect(a: Rect, b: Rect): Rect {
153
202
  };
154
203
  }
155
204
 
205
+ export function copyRect(a: Rect): Rect;
206
+ export function copyRect<T extends Rect = Rect>(a: Rect, out: T): T;
207
+ export function copyRect(a: Rect, out?: Rect): Rect {
208
+ if (out) {
209
+ out.x = a.x;
210
+ out.y = a.y;
211
+ out.width = a.width;
212
+ out.height = a.height;
213
+ return out;
214
+ }
215
+ return {
216
+ x: a.x,
217
+ y: a.y,
218
+ width: a.width,
219
+ height: a.height,
220
+ };
221
+ }
222
+
156
223
  export function compareRect(a: Rect | null, b: Rect | null): boolean {
157
224
  if (a === b) {
158
225
  return true;
@@ -20,7 +20,7 @@
20
20
  import type { CoreShaderManager } from '../CoreShaderManager.js';
21
21
  import type { TextureOptions } from '../CoreTextureManager.js';
22
22
  import type { Stage } from '../Stage.js';
23
- import type { Rect } from '../lib/utils.js';
23
+ import type { Rect, RectWithValid } from '../lib/utils.js';
24
24
  import type { Texture } from '../textures/Texture.js';
25
25
  import { CoreContextTexture } from './CoreContextTexture.js';
26
26
  import type { CoreRenderOp } from './CoreRenderOp.js';
@@ -39,7 +39,7 @@ export interface QuadOptions {
39
39
  shader: CoreShader | null;
40
40
  shaderProps: Record<string, unknown> | null;
41
41
  alpha: number;
42
- clippingRect: Rect | null;
42
+ clippingRect: RectWithValid;
43
43
  tx: number;
44
44
  ty: number;
45
45
  ta: number;
@@ -168,6 +168,26 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
168
168
  glw.UNSIGNED_BYTE,
169
169
  TRANSPARENT_TEXTURE_DATA,
170
170
  );
171
+ } else if ('mipmaps' in textureData.data && textureData.data.mipmaps) {
172
+ const {
173
+ mipmaps,
174
+ width = 0,
175
+ height = 0,
176
+ type,
177
+ glInternalFormat,
178
+ } = textureData.data;
179
+ const view =
180
+ type === 'ktx'
181
+ ? new DataView(mipmaps[0] ?? new ArrayBuffer(0))
182
+ : (mipmaps[0] as unknown as ArrayBufferView);
183
+
184
+ glw.bindTexture(this._nativeCtxTexture);
185
+ glw.compressedTexImage2D(0, glInternalFormat, width, height, 0, view);
186
+
187
+ glw.texParameteri(glw.TEXTURE_WRAP_S, glw.CLAMP_TO_EDGE);
188
+ glw.texParameteri(glw.TEXTURE_WRAP_T, glw.CLAMP_TO_EDGE);
189
+ glw.texParameteri(glw.TEXTURE_MAG_FILTER, glw.LINEAR);
190
+ glw.texParameteri(glw.TEXTURE_MIN_FILTER, glw.LINEAR);
171
191
  } else {
172
192
  console.error(
173
193
  `WebGlCoreCtxTexture.onLoadRequest: Unexpected textureData returned`,
@@ -23,7 +23,7 @@ import type { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js';
23
23
  import type { WebGlCoreRendererOptions } from './WebGlCoreRenderer.js';
24
24
  import type { BufferCollection } from './internal/BufferCollection.js';
25
25
  import type { Dimensions } from '../../../common/CommonTypes.js';
26
- import type { Rect } from '../../lib/utils.js';
26
+ import type { Rect, RectWithValid } from '../../lib/utils.js';
27
27
  import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
28
28
 
29
29
  const MAX_TEXTURES = 8; // TODO: get from gl
@@ -45,7 +45,7 @@ export class WebGlCoreRenderOp extends CoreRenderOp {
45
45
  readonly shader: WebGlCoreShader,
46
46
  readonly shaderProps: Record<string, unknown>,
47
47
  readonly alpha: number,
48
- readonly clippingRect: Rect | null,
48
+ readonly clippingRect: RectWithValid,
49
49
  readonly dimensions: Dimensions,
50
50
  readonly bufferIdx: number,
51
51
  readonly zIndex: number,
@@ -82,7 +82,7 @@ export class WebGlCoreRenderOp extends CoreRenderOp {
82
82
  const quadIdx = (this.bufferIdx / 24) * 6 * 2;
83
83
 
84
84
  // Clipping
85
- if (this.clippingRect) {
85
+ if (this.clippingRect.valid) {
86
86
  const { x, y, width, height } = this.clippingRect;
87
87
  const pixelRatio = options.pixelRatio;
88
88
  const canvasHeight = options.canvas.height;
@@ -50,6 +50,7 @@ import {
50
50
  compareRect,
51
51
  getNormalizedRgbaComponents,
52
52
  type Rect,
53
+ type RectWithValid,
53
54
  } from '../../lib/utils.js';
54
55
  import type { Dimensions } from '../../../common/CommonTypes.js';
55
56
  import { WebGlCoreShader } from './WebGlCoreShader.js';
@@ -115,6 +116,11 @@ export class WebGlCoreRenderer extends CoreRenderer {
115
116
  this.txManager = options.txManager;
116
117
  this.shManager = options.shManager;
117
118
  this.defaultTexture = new ColorTexture(this.txManager);
119
+ // When the default texture is loaded, request a render in case the
120
+ // RAF is paused. Fixes: https://github.com/lightning-js/renderer/issues/123
121
+ this.defaultTexture.once('loaded', () => {
122
+ this.stage.requestRender();
123
+ });
118
124
 
119
125
  const gl = createWebGLContext(canvas, options.contextSpy);
120
126
  const glw = (this.glw = new WebGlContextWrapper(gl));
@@ -412,7 +418,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
412
418
  shaderProps: Record<string, unknown>,
413
419
  alpha: number,
414
420
  dimensions: Dimensions,
415
- clippingRect: Rect | null,
421
+ clippingRect: RectWithValid,
416
422
  bufferIdx: number,
417
423
  ) {
418
424
  const curRenderOp = new WebGlCoreRenderOp(
@@ -47,6 +47,10 @@ export class SdfTrFontFace<
47
47
  > extends TrFontFace {
48
48
  public readonly type: FontTypeT;
49
49
  public readonly texture: ImageTexture;
50
+ /**
51
+ * Height of the tallest character in the font including the whitespace above it
52
+ */
53
+ public readonly maxCharHeight: number = 0;
50
54
  public readonly data: SdfFontData | undefined;
51
55
  public readonly shaper: FontShaper | undefined;
52
56
  public readonly glyphMap: Map<number, SdfFontData['chars'][0]> = new Map();
@@ -93,12 +97,22 @@ export class SdfTrFontFace<
93
97
  (this.data as SdfFontData) = await response.json();
94
98
  // Add all the glyphs to the glyph map
95
99
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
100
+ let maxCharHeight = 0;
96
101
  this.data!.chars.forEach((glyph) => {
97
102
  this.glyphMap.set(glyph.id, glyph);
103
+ const charHeight = glyph.yoffset + glyph.height;
104
+ if (charHeight > maxCharHeight) {
105
+ maxCharHeight = charHeight;
106
+ }
98
107
  });
108
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
109
+ (this.maxCharHeight as number) = maxCharHeight;
99
110
  // We know `data` is defined here, because we just set it
100
111
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
101
- (this.shaper as FontShaper) = new SdfFontShaper(this.data!, this.glyphMap);
112
+ (this.shaper as FontShaper) = new SdfFontShaper(
113
+ this.data!,
114
+ this.glyphMap,
115
+ );
102
116
  this.checkLoaded();
103
117
  })
104
118
  .catch(console.error);