jassub 2.0.19 → 2.1.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.
- package/README.md +26 -8
- package/dist/debug.js +2 -2
- package/dist/debug.js.map +1 -1
- package/dist/jassub.d.ts +1 -1
- package/dist/jassub.js +1 -1
- package/dist/jassub.js.map +1 -1
- package/dist/wasm/jassub-worker-modern.wasm +0 -0
- package/dist/wasm/jassub-worker.js +1 -1
- package/dist/wasm/jassub-worker.js.map +1 -1
- package/dist/wasm/jassub-worker.wasm +0 -0
- package/dist/wasm/types.d.ts +1 -5
- package/dist/worker/webgl-renderer.d.ts +48 -0
- package/dist/worker/webgl-renderer.js +290 -0
- package/dist/worker/webgl-renderer.js.map +1 -0
- package/dist/worker/webgpu-renderer.d.ts +4 -5
- package/dist/worker/webgpu-renderer.js +84 -99
- package/dist/worker/webgpu-renderer.js.map +1 -1
- package/dist/worker/worker.d.ts +3 -2
- package/dist/worker/worker.js +18 -8
- package/dist/worker/worker.js.map +1 -1
- package/package.json +3 -2
- package/src/debug.ts +2 -2
- package/src/jassub.ts +1 -1
- package/src/wasm/types.d.ts +1 -5
- package/src/worker/webgl-renderer.ts +383 -0
- package/src/worker/webgpu-renderer.ts +85 -103
- package/src/worker/worker.ts +21 -10
package/dist/worker/worker.js
CHANGED
|
@@ -3,14 +3,15 @@ import { finalizer } from 'abslink';
|
|
|
3
3
|
import { expose } from 'abslink/w3c';
|
|
4
4
|
import WASM from '../wasm/jassub-worker.js';
|
|
5
5
|
import { libassYCbCrMap, read_, readAsync, _applyKeys } from './util';
|
|
6
|
-
import {
|
|
6
|
+
import { WebGL2Renderer } from './webgl-renderer';
|
|
7
|
+
import { WebGPURenderer } from './webgpu-renderer';
|
|
7
8
|
export class ASSRenderer {
|
|
8
9
|
_offCanvas;
|
|
9
10
|
_wasm;
|
|
10
11
|
_subtitleColorSpace;
|
|
11
12
|
_videoColorSpace;
|
|
12
13
|
_malloc;
|
|
13
|
-
_gpurender
|
|
14
|
+
_gpurender;
|
|
14
15
|
debug = false;
|
|
15
16
|
useLocalFonts = false;
|
|
16
17
|
_availableFonts = {};
|
|
@@ -27,15 +28,19 @@ export class ASSRenderer {
|
|
|
27
28
|
const _fetch = globalThis.fetch;
|
|
28
29
|
globalThis.fetch = _ => _fetch(data.wasmUrl);
|
|
29
30
|
// TODO: abslink doesnt support transferables yet
|
|
30
|
-
const handleMessage = ({ data }) => {
|
|
31
|
+
const handleMessage = async ({ data }) => {
|
|
31
32
|
if (data.name === 'offscreenCanvas') {
|
|
33
|
+
await this._ready;
|
|
32
34
|
this._offCanvas = data.ctrl;
|
|
33
35
|
this._gpurender.setCanvas(this._offCanvas, this._offCanvas.width, this._offCanvas.height);
|
|
34
36
|
removeEventListener('message', handleMessage);
|
|
35
37
|
}
|
|
36
38
|
};
|
|
37
39
|
addEventListener('message', handleMessage);
|
|
38
|
-
|
|
40
|
+
const devicePromise = navigator.gpu?.requestAdapter({
|
|
41
|
+
powerPreference: 'high-performance'
|
|
42
|
+
}).then(adapter => adapter?.requestDevice());
|
|
43
|
+
this._ready = WASM({ __url: data.wasmUrl }).then(async (Module) => {
|
|
39
44
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
40
45
|
this._malloc = Module._malloc;
|
|
41
46
|
const fallbackFont = data.fallbackFont.toLowerCase();
|
|
@@ -52,6 +57,10 @@ export class ASSRenderer {
|
|
|
52
57
|
if (data.libassMemoryLimit > 0 || data.libassGlyphLimit > 0) {
|
|
53
58
|
this._wasm.setMemoryLimits(data.libassGlyphLimit || 0, data.libassMemoryLimit || 0);
|
|
54
59
|
}
|
|
60
|
+
const device = await devicePromise;
|
|
61
|
+
this._gpurender = device ? new WebGPURenderer(device) : new WebGL2Renderer();
|
|
62
|
+
if (this._offCanvas)
|
|
63
|
+
this._gpurender.setCanvas(this._offCanvas, this._offCanvas.width, this._offCanvas.height);
|
|
55
64
|
this._checkColorSpace();
|
|
56
65
|
});
|
|
57
66
|
}
|
|
@@ -121,7 +130,7 @@ export class ASSRenderer {
|
|
|
121
130
|
_checkColorSpace() {
|
|
122
131
|
if (!this._subtitleColorSpace || !this._videoColorSpace)
|
|
123
132
|
return;
|
|
124
|
-
this._gpurender.setColorMatrix(
|
|
133
|
+
this._gpurender.setColorMatrix(this._subtitleColorSpace, this._videoColorSpace);
|
|
125
134
|
}
|
|
126
135
|
_findAvailableFonts(font) {
|
|
127
136
|
font = font.trim().toLowerCase();
|
|
@@ -168,11 +177,12 @@ export class ASSRenderer {
|
|
|
168
177
|
}
|
|
169
178
|
}
|
|
170
179
|
_canvas(width, height, videoWidth, videoHeight) {
|
|
171
|
-
if (this._offCanvas)
|
|
180
|
+
if (this._offCanvas && this._gpurender)
|
|
172
181
|
this._gpurender.setCanvas(this._offCanvas, width, height);
|
|
173
182
|
this._wasm.resizeCanvas(width, height, videoWidth, videoHeight);
|
|
174
183
|
}
|
|
175
|
-
[finalizer]() {
|
|
184
|
+
async [finalizer]() {
|
|
185
|
+
await this._ready;
|
|
176
186
|
this._wasm.quitLibrary();
|
|
177
187
|
this._gpurender.destroy();
|
|
178
188
|
// @ts-expect-error force GC
|
|
@@ -182,7 +192,7 @@ export class ASSRenderer {
|
|
|
182
192
|
this._availableFonts = {};
|
|
183
193
|
}
|
|
184
194
|
_draw(time, force = false) {
|
|
185
|
-
if (!this._offCanvas)
|
|
195
|
+
if (!this._offCanvas || !this._gpurender)
|
|
186
196
|
return;
|
|
187
197
|
const result = this._wasm.rawRender(time, Number(force));
|
|
188
198
|
if (this._wasm.changed === 0 && !force)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,IAAI,MAAM,0BAA0B,CAAA;AAE3C,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACrE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jassub",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "The Fastest JavaScript SSA/ASS Subtitle Renderer For Browsers",
|
|
5
5
|
"main": "dist/jassub.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"WebAssembly",
|
|
29
29
|
"subtitles",
|
|
30
30
|
"webgpu",
|
|
31
|
+
"webgl2",
|
|
31
32
|
"typescript"
|
|
32
33
|
],
|
|
33
34
|
"author": "ThaUnknown",
|
package/src/debug.ts
CHANGED
|
@@ -31,14 +31,14 @@ export class Debug {
|
|
|
31
31
|
this._startTime = performance.now()
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
onsubtitleFrameCallback?: (now: DOMHighResTimeStamp, metadata: SubtitleCallbackMetadata) => void
|
|
34
|
+
onsubtitleFrameCallback?: (now: DOMHighResTimeStamp, metadata: SubtitleCallbackMetadata) => void
|
|
35
35
|
|
|
36
36
|
_endFrame (meta: VideoFrameCallbackMetadata) {
|
|
37
37
|
++this.presentedFrames
|
|
38
38
|
const fps = this.fps(1)
|
|
39
39
|
const now = performance.now()
|
|
40
40
|
const processingDuration = this.processingDuration((now - this._startTime) / fps)
|
|
41
|
-
const frameDelay = Math.max(0, meta.expectedDisplayTime
|
|
41
|
+
const frameDelay = Math.max(0, now - meta.expectedDisplayTime)
|
|
42
42
|
if (frameDelay) ++this.mistimedFrames
|
|
43
43
|
|
|
44
44
|
this.onsubtitleFrameCallback?.(now, {
|
package/src/jassub.ts
CHANGED
|
@@ -4,7 +4,7 @@ import 'rvfc-polyfill'
|
|
|
4
4
|
import { proxy, releaseProxy } from 'abslink'
|
|
5
5
|
import { wrap } from 'abslink/w3c'
|
|
6
6
|
|
|
7
|
-
import { Debug } from './debug
|
|
7
|
+
import { Debug } from './debug'
|
|
8
8
|
|
|
9
9
|
import type { ASS_Event, ASS_Image, ASS_Style, ClassHandle } from './wasm/types'
|
|
10
10
|
import type { ASSRenderer } from './worker/worker'
|
package/src/wasm/types.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
|
|
2
|
-
declare namespace RuntimeExports {
|
|
3
|
-
function getTempRet0(val: any): any;
|
|
4
|
-
function setTempRet0(val: any): any;
|
|
5
|
-
}
|
|
6
2
|
interface WasmModule {
|
|
7
3
|
__ZdlPvm(_0: number, _1: number): void;
|
|
8
4
|
_malloc(_0: number): number;
|
|
@@ -134,5 +130,5 @@ interface EmbindModule {
|
|
|
134
130
|
};
|
|
135
131
|
}
|
|
136
132
|
|
|
137
|
-
export type MainModule = WasmModule &
|
|
133
|
+
export type MainModule = WasmModule & EmbindModule;
|
|
138
134
|
export default function MainModuleFactory (options?: unknown): Promise<MainModule>;
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import type { ASSImage } from '../jassub'
|
|
2
|
+
|
|
3
|
+
declare const self: DedicatedWorkerGlobalScope &
|
|
4
|
+
typeof globalThis & {
|
|
5
|
+
HEAPU8RAW: Uint8Array<ArrayBuffer>
|
|
6
|
+
WASMMEMORY: WebAssembly.Memory
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const IDENTITY_MATRIX = new Float32Array([
|
|
10
|
+
1, 0, 0,
|
|
11
|
+
0, 1, 0,
|
|
12
|
+
0, 0, 1
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
// Color matrix conversion map - mat3x3 for WebGL2
|
|
16
|
+
// Each matrix converts FROM the key color space TO the nested key color space
|
|
17
|
+
export const colorMatrixConversionMap = {
|
|
18
|
+
BT601: {
|
|
19
|
+
BT709: new Float32Array([
|
|
20
|
+
1.0863, 0.0965, -0.01411,
|
|
21
|
+
-0.0723, 0.8451, -0.0277,
|
|
22
|
+
-0.0141, 0.0584, 1.0418
|
|
23
|
+
]),
|
|
24
|
+
BT601: IDENTITY_MATRIX
|
|
25
|
+
},
|
|
26
|
+
BT709: {
|
|
27
|
+
BT601: new Float32Array([
|
|
28
|
+
0.9137, 0.0784, 0.0079,
|
|
29
|
+
-0.1049, 1.1722, -0.0671,
|
|
30
|
+
0.0096, 0.0322, 0.9582
|
|
31
|
+
]),
|
|
32
|
+
BT709: IDENTITY_MATRIX
|
|
33
|
+
},
|
|
34
|
+
FCC: {
|
|
35
|
+
BT709: new Float32Array([
|
|
36
|
+
1.0873, -0.0736, -0.0137,
|
|
37
|
+
0.0974, 0.8494, 0.0531,
|
|
38
|
+
-0.0127, -0.0251, 1.0378
|
|
39
|
+
]),
|
|
40
|
+
BT601: new Float32Array([
|
|
41
|
+
1.001, -0.0008, -0.0002,
|
|
42
|
+
0.0009, 1.005, -0.006,
|
|
43
|
+
0.0013, 0.0027, 0.996
|
|
44
|
+
])
|
|
45
|
+
},
|
|
46
|
+
SMPTE240M: {
|
|
47
|
+
BT709: new Float32Array([
|
|
48
|
+
0.9993, 0.0006, 0.0001,
|
|
49
|
+
-0.0004, 0.9812, 0.0192,
|
|
50
|
+
-0.0034, -0.0114, 1.0148
|
|
51
|
+
]),
|
|
52
|
+
BT601: new Float32Array([
|
|
53
|
+
0.913, 0.0774, 0.0096,
|
|
54
|
+
-0.1051, 1.1508, -0.0456,
|
|
55
|
+
0.0063, 0.0207, 0.973
|
|
56
|
+
])
|
|
57
|
+
}
|
|
58
|
+
} as const
|
|
59
|
+
|
|
60
|
+
export type ColorSpace = keyof typeof colorMatrixConversionMap
|
|
61
|
+
|
|
62
|
+
// GLSL Vertex Shader
|
|
63
|
+
const VERTEX_SHADER = `#version 300 es
|
|
64
|
+
precision highp float;
|
|
65
|
+
|
|
66
|
+
uniform vec2 u_resolution;
|
|
67
|
+
uniform vec4 u_destRect; // x, y, w, h
|
|
68
|
+
uniform vec4 u_color; // RGBA
|
|
69
|
+
|
|
70
|
+
out vec2 v_texCoord;
|
|
71
|
+
out vec4 v_color;
|
|
72
|
+
|
|
73
|
+
// Quad vertices (0,0 to 1,1)
|
|
74
|
+
const vec2 QUAD_POSITIONS[6] = vec2[6](
|
|
75
|
+
vec2(0.0, 0.0),
|
|
76
|
+
vec2(1.0, 0.0),
|
|
77
|
+
vec2(0.0, 1.0),
|
|
78
|
+
vec2(1.0, 0.0),
|
|
79
|
+
vec2(1.0, 1.0),
|
|
80
|
+
vec2(0.0, 1.0)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
void main() {
|
|
84
|
+
vec2 quadPos = QUAD_POSITIONS[gl_VertexID];
|
|
85
|
+
|
|
86
|
+
// Calculate pixel position
|
|
87
|
+
vec2 pixelPos = u_destRect.xy + quadPos * u_destRect.zw;
|
|
88
|
+
|
|
89
|
+
// Convert to clip space (-1 to 1)
|
|
90
|
+
vec2 clipPos = (pixelPos / u_resolution) * 2.0 - 1.0;
|
|
91
|
+
clipPos.y = -clipPos.y; // Flip Y for canvas coordinates
|
|
92
|
+
|
|
93
|
+
gl_Position = vec4(clipPos, 0.0, 1.0);
|
|
94
|
+
v_texCoord = quadPos;
|
|
95
|
+
v_color = u_color;
|
|
96
|
+
}
|
|
97
|
+
`
|
|
98
|
+
|
|
99
|
+
// GLSL Fragment Shader
|
|
100
|
+
const FRAGMENT_SHADER = `#version 300 es
|
|
101
|
+
precision highp float;
|
|
102
|
+
|
|
103
|
+
uniform sampler2D u_texture;
|
|
104
|
+
uniform mat3 u_colorMatrix;
|
|
105
|
+
|
|
106
|
+
in vec2 v_texCoord;
|
|
107
|
+
in vec4 v_color;
|
|
108
|
+
|
|
109
|
+
out vec4 fragColor;
|
|
110
|
+
|
|
111
|
+
void main() {
|
|
112
|
+
// Sample texture
|
|
113
|
+
float mask = texture(u_texture, v_texCoord).r;
|
|
114
|
+
|
|
115
|
+
// Apply color matrix conversion (identity if no conversion needed)
|
|
116
|
+
vec3 correctedColor = u_colorMatrix * v_color.rgb;
|
|
117
|
+
|
|
118
|
+
// libass color alpha: 0 = opaque, 255 = transparent (inverted)
|
|
119
|
+
float colorAlpha = 1.0 - v_color.a;
|
|
120
|
+
|
|
121
|
+
// Final alpha = colorAlpha * mask
|
|
122
|
+
float a = colorAlpha * mask;
|
|
123
|
+
|
|
124
|
+
// Premultiplied alpha output
|
|
125
|
+
fragColor = vec4(correctedColor * a, a);
|
|
126
|
+
}
|
|
127
|
+
`
|
|
128
|
+
|
|
129
|
+
interface TextureInfo {
|
|
130
|
+
texture: WebGLTexture
|
|
131
|
+
width: number
|
|
132
|
+
height: number
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class WebGL2Renderer {
|
|
136
|
+
gl: WebGL2RenderingContext | null = null
|
|
137
|
+
program: WebGLProgram | null = null
|
|
138
|
+
vao: WebGLVertexArrayObject | null = null
|
|
139
|
+
|
|
140
|
+
// Uniform locations
|
|
141
|
+
u_resolution: WebGLUniformLocation | null = null
|
|
142
|
+
u_destRect: WebGLUniformLocation | null = null
|
|
143
|
+
u_color: WebGLUniformLocation | null = null
|
|
144
|
+
u_texture: WebGLUniformLocation | null = null
|
|
145
|
+
u_colorMatrix: WebGLUniformLocation | null = null
|
|
146
|
+
|
|
147
|
+
// Textures created on-demand (no fixed limit)
|
|
148
|
+
textures: TextureInfo[] = []
|
|
149
|
+
|
|
150
|
+
colorMatrix: Float32Array = IDENTITY_MATRIX
|
|
151
|
+
|
|
152
|
+
setCanvas (canvas: OffscreenCanvas, width: number, height: number) {
|
|
153
|
+
// WebGL2 doesn't allow 0-sized canvases
|
|
154
|
+
if (width <= 0 || height <= 0) return
|
|
155
|
+
|
|
156
|
+
canvas.width = width
|
|
157
|
+
canvas.height = height
|
|
158
|
+
|
|
159
|
+
if (!this.gl) {
|
|
160
|
+
// Get canvas context
|
|
161
|
+
// Note: preserveDrawingBuffer is false (default) - the browser handles
|
|
162
|
+
// buffer swaps for OffscreenCanvas, avoiding flicker
|
|
163
|
+
this.gl = canvas.getContext('webgl2', {
|
|
164
|
+
alpha: true,
|
|
165
|
+
premultipliedAlpha: true,
|
|
166
|
+
antialias: false,
|
|
167
|
+
depth: false,
|
|
168
|
+
stencil: false
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
if (!this.gl) {
|
|
172
|
+
throw new Error('Could not get WebGL2 context')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Create shaders
|
|
176
|
+
const vertexShader = this.createShader(this.gl.VERTEX_SHADER, VERTEX_SHADER)
|
|
177
|
+
const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, FRAGMENT_SHADER)
|
|
178
|
+
|
|
179
|
+
if (!vertexShader || !fragmentShader) {
|
|
180
|
+
throw new Error('Failed to create shaders')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Create program
|
|
184
|
+
this.program = this.gl.createProgram()!
|
|
185
|
+
this.gl.attachShader(this.program, vertexShader)
|
|
186
|
+
this.gl.attachShader(this.program, fragmentShader)
|
|
187
|
+
this.gl.linkProgram(this.program)
|
|
188
|
+
|
|
189
|
+
if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
|
|
190
|
+
const info = this.gl.getProgramInfoLog(this.program)
|
|
191
|
+
throw new Error('Failed to link program: ' + info)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Get uniform locations
|
|
195
|
+
this.u_resolution = this.gl.getUniformLocation(this.program, 'u_resolution')
|
|
196
|
+
this.u_destRect = this.gl.getUniformLocation(this.program, 'u_destRect')
|
|
197
|
+
this.u_color = this.gl.getUniformLocation(this.program, 'u_color')
|
|
198
|
+
this.u_texture = this.gl.getUniformLocation(this.program, 'u_texture')
|
|
199
|
+
this.u_colorMatrix = this.gl.getUniformLocation(this.program, 'u_colorMatrix')
|
|
200
|
+
|
|
201
|
+
// Create a VAO (required for WebGL2)
|
|
202
|
+
this.vao = this.gl.createVertexArray()
|
|
203
|
+
this.gl.bindVertexArray(this.vao)
|
|
204
|
+
|
|
205
|
+
// Set up blending for premultiplied alpha
|
|
206
|
+
this.gl.enable(this.gl.BLEND)
|
|
207
|
+
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA)
|
|
208
|
+
|
|
209
|
+
// Use the program
|
|
210
|
+
this.gl.useProgram(this.program)
|
|
211
|
+
|
|
212
|
+
// Set texture unit
|
|
213
|
+
this.gl.uniform1i(this.u_texture, 0)
|
|
214
|
+
|
|
215
|
+
// Set initial color matrix
|
|
216
|
+
this.gl.uniformMatrix3fv(this.u_colorMatrix, false, this.colorMatrix)
|
|
217
|
+
|
|
218
|
+
// Set one-time GL state
|
|
219
|
+
this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1)
|
|
220
|
+
this.gl.clearColor(0, 0, 0, 0)
|
|
221
|
+
this.gl.activeTexture(this.gl.TEXTURE0)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Update viewport and resolution uniform
|
|
225
|
+
this.gl.viewport(0, 0, width, height)
|
|
226
|
+
this.gl.uniform2f(this.u_resolution, width, height)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
createShader (type: number, source: string): WebGLShader | null {
|
|
230
|
+
const shader = this.gl!.createShader(type)!
|
|
231
|
+
this.gl!.shaderSource(shader, source)
|
|
232
|
+
this.gl!.compileShader(shader)
|
|
233
|
+
|
|
234
|
+
if (!this.gl!.getShaderParameter(shader, this.gl!.COMPILE_STATUS)) {
|
|
235
|
+
const info = this.gl!.getShaderInfoLog(shader)
|
|
236
|
+
console.log(info)
|
|
237
|
+
this.gl!.deleteShader(shader)
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return shader
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Set the color matrix for color space conversion.
|
|
246
|
+
* Pass null or undefined to use identity (no conversion).
|
|
247
|
+
*/
|
|
248
|
+
setColorMatrix (subtitleColorSpace?: 'BT601' | 'BT709' | 'SMPTE240M' | 'FCC', videoColorSpace?: 'BT601' | 'BT709') {
|
|
249
|
+
this.colorMatrix = (subtitleColorSpace && videoColorSpace && colorMatrixConversionMap[subtitleColorSpace]?.[videoColorSpace]) ?? IDENTITY_MATRIX
|
|
250
|
+
if (this.gl && this.u_colorMatrix && this.program) {
|
|
251
|
+
this.gl.useProgram(this.program)
|
|
252
|
+
this.gl.uniformMatrix3fv(this.u_colorMatrix, false, this.colorMatrix)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
createTextureInfo (width: number, height: number): TextureInfo {
|
|
257
|
+
const texture = this.gl!.createTexture()
|
|
258
|
+
|
|
259
|
+
this.gl!.bindTexture(this.gl!.TEXTURE_2D, texture)
|
|
260
|
+
|
|
261
|
+
// Set texture parameters for nearest-neighbor sampling (pixel-perfect)
|
|
262
|
+
this.gl!.texParameteri(this.gl!.TEXTURE_2D, this.gl!.TEXTURE_MIN_FILTER, this.gl!.NEAREST)
|
|
263
|
+
this.gl!.texParameteri(this.gl!.TEXTURE_2D, this.gl!.TEXTURE_MAG_FILTER, this.gl!.NEAREST)
|
|
264
|
+
this.gl!.texParameteri(this.gl!.TEXTURE_2D, this.gl!.TEXTURE_WRAP_S, this.gl!.CLAMP_TO_EDGE)
|
|
265
|
+
this.gl!.texParameteri(this.gl!.TEXTURE_2D, this.gl!.TEXTURE_WRAP_T, this.gl!.CLAMP_TO_EDGE)
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
texture,
|
|
269
|
+
width,
|
|
270
|
+
height
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
render (images: ASSImage[], heap: Uint8Array): void {
|
|
275
|
+
if (!this.gl || !this.program || !this.vao) return
|
|
276
|
+
|
|
277
|
+
// Hack: work around shared memory issues, webGL doesnt support shared memory, so there are race conditions when growing memory
|
|
278
|
+
if (self.HEAPU8RAW.buffer !== self.WASMMEMORY.buffer) {
|
|
279
|
+
heap = self.HEAPU8RAW = new Uint8Array(self.WASMMEMORY.buffer)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Clear canvas
|
|
283
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT)
|
|
284
|
+
|
|
285
|
+
// Grow texture array if needed
|
|
286
|
+
while (this.textures.length < images.length) {
|
|
287
|
+
this.textures.push(this.createTextureInfo(64, 64))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Render each image
|
|
291
|
+
for (let i = 0, texIndex = -1; i < images.length; i++) {
|
|
292
|
+
const img = images[i]!
|
|
293
|
+
|
|
294
|
+
// Skip images with invalid dimensions
|
|
295
|
+
if (img.w <= 0 || img.h <= 0) continue
|
|
296
|
+
|
|
297
|
+
const texInfo = this.textures[++texIndex]!
|
|
298
|
+
|
|
299
|
+
// Bind texture
|
|
300
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texInfo.texture)
|
|
301
|
+
|
|
302
|
+
// Upload bitmap data using bytesPerRow to handle stride
|
|
303
|
+
// Only need stride * (h-1) + w bytes per ASS spec
|
|
304
|
+
this.gl.pixelStorei(this.gl.UNPACK_ROW_LENGTH, img.stride) // Source rows are stride bytes apart
|
|
305
|
+
|
|
306
|
+
// Recreate texture if size changed (use actual w, not stride)
|
|
307
|
+
if (texInfo.width === img.w && texInfo.height === img.h) {
|
|
308
|
+
this.gl.texSubImage2D(
|
|
309
|
+
this.gl.TEXTURE_2D,
|
|
310
|
+
0,
|
|
311
|
+
0,
|
|
312
|
+
0,
|
|
313
|
+
img.w, // But we only copy w pixels per row
|
|
314
|
+
img.h,
|
|
315
|
+
this.gl.RED,
|
|
316
|
+
this.gl.UNSIGNED_BYTE,
|
|
317
|
+
heap,
|
|
318
|
+
img.bitmap
|
|
319
|
+
)
|
|
320
|
+
} else {
|
|
321
|
+
this.gl.texImage2D(
|
|
322
|
+
this.gl.TEXTURE_2D,
|
|
323
|
+
0,
|
|
324
|
+
this.gl.R8,
|
|
325
|
+
img.w,
|
|
326
|
+
img.h,
|
|
327
|
+
0,
|
|
328
|
+
this.gl.RED,
|
|
329
|
+
this.gl.UNSIGNED_BYTE,
|
|
330
|
+
heap,
|
|
331
|
+
img.bitmap
|
|
332
|
+
)
|
|
333
|
+
texInfo.width = img.w
|
|
334
|
+
texInfo.height = img.h
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Reset unpack parameters
|
|
338
|
+
this.gl.pixelStorei(this.gl.UNPACK_ROW_LENGTH, 0)
|
|
339
|
+
|
|
340
|
+
// destRect
|
|
341
|
+
this.gl.uniform4f(
|
|
342
|
+
this.u_destRect,
|
|
343
|
+
img.dst_x,
|
|
344
|
+
img.dst_y,
|
|
345
|
+
img.w,
|
|
346
|
+
img.h
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
// color (RGBA from 0xRRGGBBAA)
|
|
350
|
+
this.gl.uniform4f(
|
|
351
|
+
this.u_color,
|
|
352
|
+
((img.color >>> 24) & 0xFF) / 255,
|
|
353
|
+
((img.color >>> 16) & 0xFF) / 255,
|
|
354
|
+
((img.color >>> 8) & 0xFF) / 255,
|
|
355
|
+
(img.color & 0xFF) / 255
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
// 6 vertices for quad
|
|
359
|
+
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
destroy () {
|
|
364
|
+
if (this.gl) {
|
|
365
|
+
for (const tex of this.textures) {
|
|
366
|
+
this.gl.deleteTexture(tex.texture)
|
|
367
|
+
}
|
|
368
|
+
this.textures = []
|
|
369
|
+
|
|
370
|
+
if (this.vao) {
|
|
371
|
+
this.gl.deleteVertexArray(this.vao)
|
|
372
|
+
this.vao = null
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (this.program) {
|
|
376
|
+
this.gl.deleteProgram(this.program)
|
|
377
|
+
this.program = null
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this.gl = null
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|