@solidtv/renderer 1.6.0 → 1.6.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/CoreTextureManager.d.ts +31 -0
- package/dist/src/core/CoreTextureManager.js +87 -6
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.js +2 -1
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.js +29 -20
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/validateImageBitmap.d.ts +13 -7
- package/dist/src/core/lib/validateImageBitmap.js +31 -10
- package/dist/src/core/lib/validateImageBitmap.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js +30 -15
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js +52 -23
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +10 -1
- package/dist/src/core/textures/ImageTexture.js +4 -2
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +37 -10
- package/dist/src/main-api/Renderer.js +8 -4
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/CoreTextureManager.test.ts +147 -0
- package/src/core/CoreTextureManager.ts +97 -8
- package/src/core/Stage.ts +2 -0
- package/src/core/lib/ImageWorker.test.ts +19 -0
- package/src/core/lib/ImageWorker.ts +34 -40
- package/src/core/lib/validateImageBitmap.test.ts +17 -10
- package/src/core/lib/validateImageBitmap.ts +32 -14
- package/src/core/text-rendering/CanvasFontHandler.ts +36 -16
- package/src/core/text-rendering/SdfFontHandler.ts +55 -25
- package/src/core/text-rendering/TextRenderer.ts +10 -1
- package/src/core/text-rendering/tests/SdfFontHandler.test.ts +65 -1
- package/src/core/textures/ImageTexture.test.ts +94 -26
- package/src/core/textures/ImageTexture.ts +4 -2
- package/src/main-api/Renderer.ts +47 -14
|
@@ -2,26 +2,70 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
2
2
|
import { ImageTexture } from './ImageTexture.js';
|
|
3
3
|
import type { CoreTextureManager } from '../CoreTextureManager.js';
|
|
4
4
|
|
|
5
|
+
const makeTxManager = (
|
|
6
|
+
imageBitmapSupported: {
|
|
7
|
+
basic: boolean;
|
|
8
|
+
options: boolean;
|
|
9
|
+
full: boolean;
|
|
10
|
+
premultiplyHonored: boolean | null;
|
|
11
|
+
},
|
|
12
|
+
createImageBitmapMock: () => Promise<unknown>,
|
|
13
|
+
) =>
|
|
14
|
+
({
|
|
15
|
+
imageBitmapSupported,
|
|
16
|
+
platform: {
|
|
17
|
+
createImageBitmap: createImageBitmapMock,
|
|
18
|
+
},
|
|
19
|
+
} as unknown as CoreTextureManager);
|
|
20
|
+
|
|
5
21
|
describe('ImageTexture.createImageBitmap', () => {
|
|
6
22
|
beforeEach(() => {
|
|
7
23
|
vi.stubGlobal('ImageBitmap', class {});
|
|
8
24
|
});
|
|
9
25
|
|
|
10
|
-
it('
|
|
26
|
+
it('requests a premultiplied bitmap when options are supported and the option is honored', async () => {
|
|
27
|
+
const createImageBitmapMock = vi.fn(() =>
|
|
28
|
+
Promise.resolve({ close: () => {} }),
|
|
29
|
+
);
|
|
30
|
+
const txManager = makeTxManager(
|
|
31
|
+
{ basic: true, options: true, full: true, premultiplyHonored: true },
|
|
32
|
+
createImageBitmapMock,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const props = ImageTexture.resolveDefaults({
|
|
36
|
+
src: 'test.png',
|
|
37
|
+
premultiplyAlpha: true,
|
|
38
|
+
});
|
|
39
|
+
const texture = new ImageTexture(txManager, props);
|
|
40
|
+
|
|
41
|
+
const blob = new Blob([], { type: 'image/png' });
|
|
42
|
+
const result = await texture.createImageBitmap(
|
|
43
|
+
blob,
|
|
44
|
+
true,
|
|
45
|
+
null,
|
|
46
|
+
null,
|
|
47
|
+
null,
|
|
48
|
+
null,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(createImageBitmapMock).toHaveBeenCalledWith(blob, {
|
|
52
|
+
premultiplyAlpha: 'premultiply',
|
|
53
|
+
colorSpaceConversion: 'none',
|
|
54
|
+
imageOrientation: 'none',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// The bitmap is already premultiplied; WebGL must not premultiply again
|
|
58
|
+
expect(result.premultiplyAlpha).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('requests a straight bitmap and defers premultiply to WebGL when the option is not honored', async () => {
|
|
11
62
|
const createImageBitmapMock = vi.fn(() =>
|
|
12
63
|
Promise.resolve({ close: () => {} }),
|
|
13
64
|
);
|
|
14
|
-
const txManager =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
full: true,
|
|
19
|
-
premultiplyHonored: false,
|
|
20
|
-
},
|
|
21
|
-
platform: {
|
|
22
|
-
createImageBitmap: createImageBitmapMock,
|
|
23
|
-
},
|
|
24
|
-
} as unknown as CoreTextureManager;
|
|
65
|
+
const txManager = makeTxManager(
|
|
66
|
+
{ basic: true, options: true, full: true, premultiplyHonored: false },
|
|
67
|
+
createImageBitmapMock,
|
|
68
|
+
);
|
|
25
69
|
|
|
26
70
|
const props = ImageTexture.resolveDefaults({
|
|
27
71
|
src: 'test.png',
|
|
@@ -39,14 +83,13 @@ describe('ImageTexture.createImageBitmap', () => {
|
|
|
39
83
|
null,
|
|
40
84
|
);
|
|
41
85
|
|
|
42
|
-
// It should have called createImageBitmap with the options object
|
|
43
86
|
expect(createImageBitmapMock).toHaveBeenCalledWith(blob, {
|
|
44
87
|
premultiplyAlpha: 'none',
|
|
45
88
|
colorSpaceConversion: 'none',
|
|
46
89
|
imageOrientation: 'none',
|
|
47
90
|
});
|
|
48
91
|
|
|
49
|
-
// Since premultiplyHonored is false,
|
|
92
|
+
// Since premultiplyHonored is false, WebGL premultiplies on upload
|
|
50
93
|
expect(result.premultiplyAlpha).toBe(true);
|
|
51
94
|
});
|
|
52
95
|
|
|
@@ -54,17 +97,10 @@ describe('ImageTexture.createImageBitmap', () => {
|
|
|
54
97
|
const createImageBitmapMock = vi.fn(() =>
|
|
55
98
|
Promise.resolve({ close: () => {} }),
|
|
56
99
|
);
|
|
57
|
-
const txManager =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
full: false,
|
|
62
|
-
premultiplyHonored: null,
|
|
63
|
-
},
|
|
64
|
-
platform: {
|
|
65
|
-
createImageBitmap: createImageBitmapMock,
|
|
66
|
-
},
|
|
67
|
-
} as unknown as CoreTextureManager;
|
|
100
|
+
const txManager = makeTxManager(
|
|
101
|
+
{ basic: true, options: false, full: false, premultiplyHonored: null },
|
|
102
|
+
createImageBitmapMock,
|
|
103
|
+
);
|
|
68
104
|
|
|
69
105
|
const props = ImageTexture.resolveDefaults({
|
|
70
106
|
src: 'test.png',
|
|
@@ -85,7 +121,39 @@ describe('ImageTexture.createImageBitmap', () => {
|
|
|
85
121
|
// It should have called basic createImageBitmap without options
|
|
86
122
|
expect(createImageBitmapMock).toHaveBeenCalledWith(blob);
|
|
87
123
|
|
|
88
|
-
//
|
|
124
|
+
// The browser default is assumed to premultiply; WebGL must not premultiply again
|
|
89
125
|
expect(result.premultiplyAlpha).toBe(false);
|
|
90
126
|
});
|
|
127
|
+
|
|
128
|
+
it('defers premultiply to WebGL on basic-only devices when premultiplyAlphaHonored is false', async () => {
|
|
129
|
+
const createImageBitmapMock = vi.fn(() =>
|
|
130
|
+
Promise.resolve({ close: () => {} }),
|
|
131
|
+
);
|
|
132
|
+
const txManager = makeTxManager(
|
|
133
|
+
{ basic: true, options: false, full: false, premultiplyHonored: false },
|
|
134
|
+
createImageBitmapMock,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const props = ImageTexture.resolveDefaults({
|
|
138
|
+
src: 'test.png',
|
|
139
|
+
premultiplyAlpha: true,
|
|
140
|
+
});
|
|
141
|
+
const texture = new ImageTexture(txManager, props);
|
|
142
|
+
|
|
143
|
+
const blob = new Blob([], { type: 'image/png' });
|
|
144
|
+
const result = await texture.createImageBitmap(
|
|
145
|
+
blob,
|
|
146
|
+
true,
|
|
147
|
+
null,
|
|
148
|
+
null,
|
|
149
|
+
null,
|
|
150
|
+
null,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Still the basic call — no options are supported on these devices
|
|
154
|
+
expect(createImageBitmapMock).toHaveBeenCalledWith(blob);
|
|
155
|
+
|
|
156
|
+
// Device default returns straight alpha; WebGL premultiplies on upload
|
|
157
|
+
expect(result.premultiplyAlpha).toBe(true);
|
|
158
|
+
});
|
|
91
159
|
});
|
|
@@ -225,10 +225,12 @@ export class ImageTexture extends Texture {
|
|
|
225
225
|
) {
|
|
226
226
|
// basic createImageBitmap without options or crop
|
|
227
227
|
// this is supported for Chrome v50 to v52/54 that doesn't support options.
|
|
228
|
-
// The browser default premultiplies, so WebGL must not premultiply again
|
|
228
|
+
// The browser default premultiplies, so WebGL must not premultiply again —
|
|
229
|
+
// except on devices whose default returns straight alpha
|
|
230
|
+
// (premultiplyAlphaHonored: false); there WebGL premultiplies on upload.
|
|
229
231
|
return {
|
|
230
232
|
data: await this.platform.createImageBitmap(blob),
|
|
231
|
-
premultiplyAlpha:
|
|
233
|
+
premultiplyAlpha: useGlPremultiply,
|
|
232
234
|
};
|
|
233
235
|
}
|
|
234
236
|
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -481,6 +481,26 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
481
481
|
*/
|
|
482
482
|
numImageWorkers: number;
|
|
483
483
|
|
|
484
|
+
/**
|
|
485
|
+
* Maximum number of image fetch+decode operations allowed to run at once
|
|
486
|
+
* when image workers are unavailable
|
|
487
|
+
*
|
|
488
|
+
* @remarks
|
|
489
|
+
* Only applies when there is no image worker manager (i.e.
|
|
490
|
+
* `numImageWorkers === 0`, or workers/`createImageBitmap` are unsupported).
|
|
491
|
+
* In that mode every image decode runs on the main thread, so a burst — such
|
|
492
|
+
* as a scroll that makes many image nodes renderable in a single tick — can
|
|
493
|
+
* fire dozens of decodes back-to-back and starve the render loop. This caps
|
|
494
|
+
* how many run concurrently; on-screen (priority) textures bypass the cap so
|
|
495
|
+
* they never wait behind off-screen prefetch.
|
|
496
|
+
*
|
|
497
|
+
* Has no effect when image workers are active — the worker pool already
|
|
498
|
+
* bounds concurrency by its size. Set to `0` to disable the cap (unbounded).
|
|
499
|
+
*
|
|
500
|
+
* @defaultValue `4`
|
|
501
|
+
*/
|
|
502
|
+
imageDecodeConcurrency: number;
|
|
503
|
+
|
|
484
504
|
/**
|
|
485
505
|
* Renderer Engine
|
|
486
506
|
*
|
|
@@ -610,13 +630,17 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
610
630
|
* - Full - Supports createImageBitmap(image, sx, sy, sw, sh, options)
|
|
611
631
|
*
|
|
612
632
|
* Note with auto detection, the renderer will attempt to use the most advanced
|
|
613
|
-
* version of the API available. If the API is not available
|
|
614
|
-
*
|
|
633
|
+
* version of the API available. If the API is not available (e.g. Chrome < 50,
|
|
634
|
+
* including the Chrome 38 support floor), the renderer falls back to loading
|
|
635
|
+
* images via `new Image()`.
|
|
615
636
|
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
637
|
+
* `'auto'` runs a small startup probe (a 1x1 PNG) to determine support; this
|
|
638
|
+
* adds a negligible one-time startup cost. Set an explicit value (`'full'`,
|
|
639
|
+
* `'options'`, `'basic'`) to skip the probe ONLY when the target runtime is
|
|
640
|
+
* known to support that level — forcing a level the runtime lacks will fail
|
|
641
|
+
* to load images. When in doubt, use `'auto'`.
|
|
618
642
|
*
|
|
619
|
-
* @defaultValue `
|
|
643
|
+
* @defaultValue `auto`
|
|
620
644
|
*/
|
|
621
645
|
createImageBitmapSupport: 'auto' | 'basic' | 'options' | 'full';
|
|
622
646
|
|
|
@@ -630,12 +654,16 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
630
654
|
* ignore it, returning straight (non-premultiplied) alpha. This causes edge
|
|
631
655
|
* "ghosting" on images with transparency.
|
|
632
656
|
*
|
|
633
|
-
*
|
|
634
|
-
* upload + framebuffer readback).
|
|
635
|
-
*
|
|
636
|
-
*
|
|
657
|
+
* `'auto'` (the default) detects via a cheap startup probe (one 1×1 PNG
|
|
658
|
+
* decode + texture upload + framebuffer readback). On devices that honor the
|
|
659
|
+
* option — the vast majority — the probe returns `true` and behavior is
|
|
660
|
+
* identical to assuming it honored; only devices that actually ignore it
|
|
661
|
+
* (e.g. the Movistar STB) switch to the WebGL-side premultiply fallback, so
|
|
662
|
+
* the change is invisible everywhere it isn't needed. Set to a boolean to
|
|
663
|
+
* force the value and skip the probe (`false` forces the GL-premultiply
|
|
664
|
+
* fallback; `true` skips it).
|
|
637
665
|
*
|
|
638
|
-
* @defaultValue `
|
|
666
|
+
* @defaultValue `'auto'` (probe once at startup)
|
|
639
667
|
*/
|
|
640
668
|
premultiplyAlphaHonored?: boolean | 'auto';
|
|
641
669
|
|
|
@@ -781,6 +809,10 @@ export class RendererMain extends EventEmitter {
|
|
|
781
809
|
textLayoutCacheSize: settings.textLayoutCacheSize ?? 250,
|
|
782
810
|
numImageWorkers:
|
|
783
811
|
settings.numImageWorkers !== undefined ? settings.numImageWorkers : 2,
|
|
812
|
+
imageDecodeConcurrency:
|
|
813
|
+
settings.imageDecodeConcurrency !== undefined
|
|
814
|
+
? settings.imageDecodeConcurrency
|
|
815
|
+
: 4,
|
|
784
816
|
enableContextSpy: settings.enableContextSpy ?? false,
|
|
785
817
|
forceWebGL2: settings.forceWebGL2 ?? false,
|
|
786
818
|
disableVertexArrayObject: settings.disableVertexArrayObject ?? false,
|
|
@@ -792,12 +824,12 @@ export class RendererMain extends EventEmitter {
|
|
|
792
824
|
textBaselineMode: settings.textBaselineMode ?? 'optical',
|
|
793
825
|
textureProcessingTimeLimit: settings.textureProcessingTimeLimit || 10,
|
|
794
826
|
canvas: settings.canvas,
|
|
795
|
-
createImageBitmapSupport: settings.createImageBitmapSupport || '
|
|
796
|
-
// undefined ->
|
|
797
|
-
// explicit boolean -> force the value.
|
|
827
|
+
createImageBitmapSupport: settings.createImageBitmapSupport || 'auto',
|
|
828
|
+
// undefined -> 'auto' (probe once at startup); 'auto' -> probe;
|
|
829
|
+
// explicit boolean -> force the value, skip the probe.
|
|
798
830
|
premultiplyAlphaHonored:
|
|
799
831
|
settings.premultiplyAlphaHonored === undefined
|
|
800
|
-
?
|
|
832
|
+
? 'auto'
|
|
801
833
|
: settings.premultiplyAlphaHonored,
|
|
802
834
|
platform: settings.platform || null,
|
|
803
835
|
maxRetryCount: settings.maxRetryCount ?? 5,
|
|
@@ -854,6 +886,7 @@ export class RendererMain extends EventEmitter {
|
|
|
854
886
|
fpsUpdateInterval: settings.fpsUpdateInterval!,
|
|
855
887
|
enableClear: settings.enableClear!,
|
|
856
888
|
numImageWorkers: settings.numImageWorkers!,
|
|
889
|
+
imageDecodeConcurrency: settings.imageDecodeConcurrency!,
|
|
857
890
|
renderEngine: settings.renderEngine!,
|
|
858
891
|
textureMemory: resolvedTxSettings,
|
|
859
892
|
eventBus: this,
|