akarisub 0.2.2 → 1.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 (76) hide show
  1. package/README.md +198 -167
  2. package/THIRD_PARTY_NOTICES.md +55 -163
  3. package/dist/COPYRIGHT +56 -164
  4. package/dist/index.d.ts +12 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +11 -147
  7. package/dist/index.js.map +1 -0
  8. package/dist/ts/{ts/akarisub.d.ts → akarisub.d.ts} +80 -99
  9. package/dist/ts/akarisub.d.ts.map +1 -0
  10. package/dist/ts/akarisub.js +2086 -0
  11. package/dist/ts/akarisub.js.map +1 -0
  12. package/dist/ts/timing.d.ts +63 -0
  13. package/dist/ts/timing.d.ts.map +1 -0
  14. package/dist/ts/timing.js +219 -0
  15. package/dist/ts/timing.js.map +1 -0
  16. package/dist/ts/{ts/types.d.ts → types.d.ts} +103 -78
  17. package/dist/ts/types.d.ts.map +1 -0
  18. package/dist/ts/types.js +2 -0
  19. package/dist/ts/{ts/types.js.map → types.js.map} +1 -1
  20. package/dist/ts/{ts/utils.d.ts → utils.d.ts} +0 -31
  21. package/dist/ts/utils.d.ts.map +1 -0
  22. package/dist/ts/{ts/utils.js → utils.js} +0 -56
  23. package/dist/ts/utils.js.map +1 -0
  24. package/dist/ts/wasm.d.ts +12 -0
  25. package/dist/ts/wasm.d.ts.map +1 -0
  26. package/dist/ts/wasm.js +46 -0
  27. package/dist/ts/wasm.js.map +1 -0
  28. package/dist/ts/{ts/webgl2-renderer.d.ts → webgl2-renderer.d.ts} +0 -12
  29. package/dist/ts/webgl2-renderer.d.ts.map +1 -0
  30. package/dist/ts/{ts/webgl2-renderer.js → webgl2-renderer.js} +0 -27
  31. package/dist/ts/webgl2-renderer.js.map +1 -0
  32. package/dist/ts/{ts/webgpu-renderer.d.ts → webgpu-renderer.d.ts} +17 -16
  33. package/dist/ts/webgpu-renderer.d.ts.map +1 -0
  34. package/dist/ts/{ts/webgpu-renderer.js → webgpu-renderer.js} +148 -71
  35. package/dist/ts/webgpu-renderer.js.map +1 -0
  36. package/dist/ts/worker.d.ts +2 -0
  37. package/dist/ts/{ts/worker.d.ts.map → worker.d.ts.map} +1 -1
  38. package/dist/ts/{ts/worker.js → worker.js} +1064 -264
  39. package/dist/ts/worker.js.map +1 -0
  40. package/dist/wrapper.d.ts +8 -0
  41. package/dist/wrapper.d.ts.map +1 -0
  42. package/dist/{ts/index.js → wrapper.js} +4 -14
  43. package/dist/wrapper.js.map +1 -0
  44. package/package.json +24 -22
  45. package/pkg/akarisub.js +53 -0
  46. package/{dist/akarisub-worker.wasm → pkg/akarisub.wasm} +0 -0
  47. package/src/wrapper.ts +46 -0
  48. package/dist/akarisub-worker.js +0 -39
  49. package/dist/akarisub.umd.js +0 -159
  50. package/dist/ts/index.d.ts +0 -14
  51. package/dist/ts/index.d.ts.map +0 -1
  52. package/dist/ts/index.js.map +0 -1
  53. package/dist/ts/ts/akarisub.d.ts.map +0 -1
  54. package/dist/ts/ts/akarisub.js +0 -1073
  55. package/dist/ts/ts/akarisub.js.map +0 -1
  56. package/dist/ts/ts/types.d.ts.map +0 -1
  57. package/dist/ts/ts/types.js +0 -5
  58. package/dist/ts/ts/utils.d.ts.map +0 -1
  59. package/dist/ts/ts/utils.js.map +0 -1
  60. package/dist/ts/ts/webgl2-renderer.d.ts.map +0 -1
  61. package/dist/ts/ts/webgl2-renderer.js.map +0 -1
  62. package/dist/ts/ts/webgpu-renderer.d.ts.map +0 -1
  63. package/dist/ts/ts/webgpu-renderer.js.map +0 -1
  64. package/dist/ts/ts/worker.d.ts +0 -6
  65. package/dist/ts/ts/worker.js.map +0 -1
  66. package/dist/ts/wrapper.d.ts +0 -8
  67. package/dist/ts/wrapper.d.ts.map +0 -1
  68. package/dist/ts/wrapper.js +0 -9
  69. package/dist/ts/wrapper.js.map +0 -1
  70. package/src/ts/akarisub.ts +0 -1272
  71. package/src/ts/types.ts +0 -397
  72. package/src/ts/utils.ts +0 -512
  73. package/src/ts/webgl2-renderer.ts +0 -426
  74. package/src/ts/webgpu-renderer.ts +0 -691
  75. package/src/ts/worker.ts +0 -2052
  76. /package/{dist → assets}/default.woff2 +0 -0
@@ -1,426 +0,0 @@
1
- import type { RenderImage } from './types'
2
-
3
- const MAX_IMAGES_PER_BATCH = 256
4
- const MAX_TEXTURE_ARRAY_LAYERS = 256
5
-
6
- // GLSL Vertex Shader (GLSL ES 3.00)
7
- const VERTEX_SHADER = /* glsl */ `#version 300 es
8
- precision highp float;
9
-
10
- in vec4 a_destRect;
11
- in vec4 a_texInfo;
12
-
13
- uniform vec2 u_resolution;
14
-
15
- out vec2 v_uv;
16
- flat out int v_texIndex;
17
- flat out vec2 v_texSize;
18
-
19
- vec2 quadPos(int id) {
20
- if (id == 0) return vec2(0.0, 0.0);
21
- if (id == 1) return vec2(1.0, 0.0);
22
- if (id == 2) return vec2(0.0, 1.0);
23
- if (id == 3) return vec2(1.0, 0.0);
24
- if (id == 4) return vec2(1.0, 1.0);
25
- return vec2(0.0, 1.0);
26
- }
27
-
28
- void main() {
29
- vec2 qp = quadPos(gl_VertexID);
30
- vec2 pixelPos = a_destRect.xy + qp * a_destRect.zw;
31
-
32
- // Convert CSS pixel coords (y=0 top) to GL clip space (y=1 top)
33
- vec2 clip = (pixelPos / u_resolution) * 2.0 - 1.0;
34
- clip.y = -clip.y;
35
-
36
- gl_Position = vec4(clip, 0.0, 1.0);
37
- v_uv = qp;
38
- v_texIndex = int(a_texInfo.z);
39
- v_texSize = a_texInfo.xy;
40
- }
41
- `
42
-
43
- // GLSL Fragment Shader (GLSL ES 3.00)
44
- const FRAGMENT_SHADER = /* glsl */ `#version 300 es
45
- precision highp float;
46
- precision highp sampler2DArray;
47
-
48
- uniform sampler2DArray u_texArray;
49
- uniform ivec2 u_texArraySize;
50
-
51
- in vec2 v_uv;
52
- flat in int v_texIndex;
53
- flat in vec2 v_texSize;
54
-
55
- out vec4 fragColor;
56
-
57
- void main() {
58
- vec2 normalizedCoord = v_uv * v_texSize / vec2(u_texArraySize);
59
- vec4 color = texture(u_texArray, vec3(normalizedCoord, float(v_texIndex)));
60
- // Premultiplied alpha output (matches WebGPU renderer behaviour)
61
- fragColor = vec4(color.rgb * color.a, color.a);
62
- }
63
- `
64
-
65
- /**
66
- * Check if WebGL2 is supported in the current browser.
67
- */
68
- export function isWebGL2Supported(): boolean {
69
- if (typeof document === 'undefined') return false
70
- try {
71
- const canvas = document.createElement('canvas')
72
- return canvas.getContext('webgl2') !== null
73
- } catch {
74
- return false
75
- }
76
- }
77
-
78
- function isArrayBufferView(value: unknown): value is Uint8Array | Uint8ClampedArray {
79
- return value instanceof Uint8Array || value instanceof Uint8ClampedArray
80
- }
81
-
82
- function compileShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader {
83
- const shader = gl.createShader(type)!
84
- gl.shaderSource(shader, source)
85
- gl.compileShader(shader)
86
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
87
- const info = gl.getShaderInfoLog(shader)
88
- gl.deleteShader(shader)
89
- throw new Error(`WebGL2 shader compilation failed: ${info}`)
90
- }
91
- return shader
92
- }
93
-
94
- /**
95
- * High-performance WebGL2 subtitle renderer for AkariSub.
96
- */
97
- export class WebGL2Renderer {
98
- private _gl: WebGL2RenderingContext | null = null
99
- private _canvas: HTMLCanvasElement | null = null
100
- private _program: WebGLProgram | null = null
101
- private _vao: WebGLVertexArrayObject | null = null
102
- private _instanceBuffer: WebGLBuffer | null = null
103
- private _texArray: WebGLTexture | null = null
104
-
105
- private _texWidth = 0
106
- private _texHeight = 0
107
- private _texLayers = 0
108
-
109
- private _resolutionLoc: WebGLUniformLocation | null = null
110
- private _texArraySizeLoc: WebGLUniformLocation | null = null
111
- private readonly _instanceData: Float32Array
112
-
113
- private _lastCanvasWidth = 0
114
- private _lastCanvasHeight = 0
115
- private _initialized = false
116
- private _initPromise: Promise<void> | null = null
117
-
118
- constructor() {
119
- this._instanceData = new Float32Array(MAX_IMAGES_PER_BATCH * 8)
120
- }
121
-
122
- async init(): Promise<void> {
123
- if (this._initPromise) return this._initPromise
124
- this._initPromise = this._checkSupport()
125
- return this._initPromise
126
- }
127
-
128
- private async _checkSupport(): Promise<void> {
129
- if (typeof document === 'undefined') throw new Error('WebGL2 requires a DOM environment')
130
- const canvas = document.createElement('canvas')
131
- if (!canvas.getContext('webgl2')) throw new Error('WebGL2 not supported')
132
- }
133
-
134
- private _initGL(): void {
135
- if (!this._canvas) throw new Error('Canvas not set before _initGL')
136
- if (this._gl) return // already initialised
137
-
138
- const gl = this._canvas.getContext('webgl2', { alpha: true, premultipliedAlpha: true, antialias: false })
139
- if (!gl) throw new Error('Failed to create WebGL2 context')
140
- this._gl = gl
141
-
142
- // Compile and link program
143
- const vert = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER)
144
- const frag = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER)
145
- const program = gl.createProgram()!
146
- gl.attachShader(program, vert)
147
- gl.attachShader(program, frag)
148
- gl.linkProgram(program)
149
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
150
- throw new Error(`WebGL2 program link failed: ${gl.getProgramInfoLog(program)}`)
151
- }
152
- gl.deleteShader(vert)
153
- gl.deleteShader(frag)
154
- this._program = program
155
-
156
- this._resolutionLoc = gl.getUniformLocation(program, 'u_resolution')
157
- this._texArraySizeLoc = gl.getUniformLocation(program, 'u_texArraySize')
158
-
159
- // VAO + instance VBO
160
- this._vao = gl.createVertexArray()!
161
- gl.bindVertexArray(this._vao)
162
-
163
- this._instanceBuffer = gl.createBuffer()!
164
- gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
165
- // Size: MAX_IMAGES * 8 floats * 4 bytes
166
- gl.bufferData(gl.ARRAY_BUFFER, MAX_IMAGES_PER_BATCH * 32, gl.DYNAMIC_DRAW)
167
-
168
- // stride = 32 bytes (8 × float)
169
- const aDestRect = gl.getAttribLocation(program, 'a_destRect')
170
- gl.enableVertexAttribArray(aDestRect)
171
- gl.vertexAttribPointer(aDestRect, 4, gl.FLOAT, false, 32, 0)
172
- gl.vertexAttribDivisor(aDestRect, 1)
173
-
174
- const aTexInfo = gl.getAttribLocation(program, 'a_texInfo')
175
- gl.enableVertexAttribArray(aTexInfo)
176
- gl.vertexAttribPointer(aTexInfo, 4, gl.FLOAT, false, 32, 16)
177
- gl.vertexAttribDivisor(aTexInfo, 1)
178
-
179
- gl.bindVertexArray(null)
180
-
181
- // Texture array
182
- this._allocateTextureArray(256, 256, 32)
183
-
184
- // Premultiplied-alpha blending
185
- gl.enable(gl.BLEND)
186
- gl.blendEquation(gl.FUNC_ADD)
187
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
188
-
189
- this._initialized = true
190
- }
191
-
192
- // ==========================================================================
193
- // Texture management
194
- // ==========================================================================
195
-
196
- // Round up to a multiple of 64: gives headroom against size jitter without
197
- // power-of-2 over-allocation (a 1920x1080 blend region would otherwise
198
- // round to 2048x2048 and waste ~2x memory).
199
- private _roundDim(n: number): number {
200
- return (Math.max(n, 64) + 63) & ~63
201
- }
202
-
203
- // Round layers up to a multiple of 8, clamped to the layer cap
204
- private _roundLayers(n: number): number {
205
- return Math.min((Math.max(n, 8) + 7) & ~7, MAX_TEXTURE_ARRAY_LAYERS)
206
- }
207
-
208
- private _allocateTextureArray(width: number, height: number, layers: number): void {
209
- const gl = this._gl!
210
- const w = this._roundDim(width)
211
- const h = this._roundDim(height)
212
- const l = this._roundLayers(layers)
213
-
214
- if (this._texArray) gl.deleteTexture(this._texArray)
215
- this._texArray = gl.createTexture()!
216
- gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
217
- gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, w, h, l)
218
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
219
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
220
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
221
- gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
222
-
223
- this._texWidth = w
224
- this._texHeight = h
225
- this._texLayers = l
226
- }
227
-
228
- private _ensureTextureArray(maxW: number, maxH: number, count: number): void {
229
- const c = Math.min(count, MAX_TEXTURE_ARRAY_LAYERS)
230
- if (maxW <= this._texWidth && maxH <= this._texHeight && c <= this._texLayers) return
231
- const newW = Math.max(this._texWidth, maxW)
232
- const newH = Math.max(this._texHeight, maxH)
233
- const newL = Math.max(c, Math.min(this._texLayers, 16))
234
- this._allocateTextureArray(newW, newH, newL)
235
- }
236
-
237
- // ==========================================================================
238
- // Public interface
239
- // ==========================================================================
240
-
241
- async setCanvas(canvas: HTMLCanvasElement, width: number, height: number): Promise<void> {
242
- await this.init()
243
- if (width <= 0 || height <= 0) return
244
- this._canvas = canvas
245
- canvas.width = width
246
- canvas.height = height
247
- this._initGL()
248
- this._gl!.viewport(0, 0, width, height)
249
- this._lastCanvasWidth = width
250
- this._lastCanvasHeight = height
251
- }
252
-
253
- updateSize(width: number, height: number): void {
254
- if (!this._gl || !this._canvas || width <= 0 || height <= 0) return
255
- if (width === this._lastCanvasWidth && height === this._lastCanvasHeight) return
256
- this._canvas.width = width
257
- this._canvas.height = height
258
- this._gl.viewport(0, 0, width, height)
259
- this._lastCanvasWidth = width
260
- this._lastCanvasHeight = height
261
- }
262
-
263
- /**
264
- * Render from ImageBitmaps (async render mode)
265
- */
266
- renderBitmaps(
267
- images: { image: ImageBitmap; x: number; y: number }[],
268
- _canvasWidth: number,
269
- _canvasHeight: number
270
- ): void {
271
- if (!this._gl || !this._initialized) return
272
-
273
- const len = images.length
274
- if (len === 0) {
275
- this.clear()
276
- return
277
- }
278
-
279
- let maxW = 0, maxH = 0
280
- for (let i = 0; i < len; i++) {
281
- const { image } = images[i]
282
- if (image.width > maxW) maxW = image.width
283
- if (image.height > maxH) maxH = image.height
284
- }
285
-
286
- this._ensureTextureArray(maxW, maxH, Math.min(len, MAX_TEXTURE_ARRAY_LAYERS))
287
-
288
- const gl = this._gl
289
- gl.clearColor(0, 0, 0, 0)
290
- gl.clear(gl.COLOR_BUFFER_BIT)
291
- gl.useProgram(this._program)
292
- gl.uniform2f(this._resolutionLoc, this._lastCanvasWidth, this._lastCanvasHeight)
293
- gl.uniform2i(this._texArraySizeLoc, this._texWidth, this._texHeight)
294
- gl.activeTexture(gl.TEXTURE0)
295
- gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
296
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
297
-
298
- const instanceData = this._instanceData
299
- let imageIndex = 0
300
-
301
- while (imageIndex < len) {
302
- let count = 0
303
- while (imageIndex < len && count < MAX_TEXTURE_ARRAY_LAYERS) {
304
- const img = images[imageIndex++]
305
- const w = img.image.width, h = img.image.height
306
- if (w <= 0 || h <= 0) continue
307
- gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, img.image)
308
- const off = count << 3
309
- instanceData[off] = img.x
310
- instanceData[off + 1] = img.y
311
- instanceData[off + 2] = w
312
- instanceData[off + 3] = h
313
- instanceData[off + 4] = w
314
- instanceData[off + 5] = h
315
- instanceData[off + 6] = count
316
- instanceData[off + 7] = 0
317
- count++
318
- }
319
- if (count === 0) continue
320
- gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
321
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, instanceData, 0, count << 3)
322
- gl.bindVertexArray(this._vao)
323
- gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, count)
324
- gl.bindVertexArray(null)
325
- }
326
- }
327
-
328
- /**
329
- * Render from raw ArrayBuffer data (non-async render mode)
330
- */
331
- render(
332
- images: RenderImage[],
333
- _canvasWidth: number,
334
- _canvasHeight: number
335
- ): void {
336
- if (!this._gl || !this._initialized) return
337
-
338
- const len = images.length
339
- if (len === 0) {
340
- this.clear()
341
- return
342
- }
343
-
344
- let maxW = 0, maxH = 0
345
- for (let i = 0; i < len; i++) {
346
- const { w, h } = images[i]
347
- if (w > maxW) maxW = w
348
- if (h > maxH) maxH = h
349
- }
350
-
351
- this._ensureTextureArray(maxW, maxH, Math.min(len, MAX_TEXTURE_ARRAY_LAYERS))
352
-
353
- const gl = this._gl
354
- gl.clearColor(0, 0, 0, 0)
355
- gl.clear(gl.COLOR_BUFFER_BIT)
356
- gl.useProgram(this._program)
357
- gl.uniform2f(this._resolutionLoc, this._lastCanvasWidth, this._lastCanvasHeight)
358
- gl.uniform2i(this._texArraySizeLoc, this._texWidth, this._texHeight)
359
- gl.activeTexture(gl.TEXTURE0)
360
- gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
361
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
362
-
363
- const instanceData = this._instanceData
364
- let imageIndex = 0
365
-
366
- while (imageIndex < len) {
367
- let count = 0
368
- while (imageIndex < len && count < MAX_TEXTURE_ARRAY_LAYERS) {
369
- const img = images[imageIndex++]
370
- const w = img.w, h = img.h
371
- if (w <= 0 || h <= 0) continue
372
- const imgData = img.image
373
- if (imgData instanceof ImageBitmap) {
374
- gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, imgData)
375
- } else if (imgData instanceof ArrayBuffer || isArrayBufferView(imgData)) {
376
- const uploadData = imgData instanceof ArrayBuffer ? new Uint8Array(imgData) : imgData
377
- gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, uploadData)
378
- }
379
- const off = count << 3
380
- instanceData[off] = img.x
381
- instanceData[off + 1] = img.y
382
- instanceData[off + 2] = w
383
- instanceData[off + 3] = h
384
- instanceData[off + 4] = w
385
- instanceData[off + 5] = h
386
- instanceData[off + 6] = count
387
- instanceData[off + 7] = 0
388
- count++
389
- }
390
- if (count === 0) continue
391
- gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
392
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, instanceData, 0, count << 3)
393
- gl.bindVertexArray(this._vao)
394
- gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, count)
395
- gl.bindVertexArray(null)
396
- }
397
- }
398
-
399
- clear(): void {
400
- if (!this._gl) return
401
- this._gl.clearColor(0, 0, 0, 0)
402
- this._gl.clear(this._gl.COLOR_BUFFER_BIT)
403
- }
404
-
405
- get initialized(): boolean {
406
- return this._initialized
407
- }
408
-
409
- destroy(): void {
410
- const gl = this._gl
411
- if (gl) {
412
- gl.deleteProgram(this._program)
413
- gl.deleteVertexArray(this._vao)
414
- gl.deleteBuffer(this._instanceBuffer)
415
- gl.deleteTexture(this._texArray)
416
- }
417
- this._gl = null
418
- this._program = null
419
- this._vao = null
420
- this._instanceBuffer = null
421
- this._texArray = null
422
- this._canvas = null
423
- this._initialized = false
424
- this._initPromise = null
425
- }
426
- }