akarisub 0.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/LICENSE +23 -0
- package/README.md +388 -0
- package/dist/COPYRIGHT +951 -0
- package/dist/akarisub-worker.js +39 -0
- package/dist/akarisub-worker.wasm +0 -0
- package/dist/akarisub.umd.js +159 -0
- package/dist/default.woff2 +0 -0
- package/dist/index.js +147 -0
- package/package.json +63 -0
- package/src/ts/akarisub.ts +1159 -0
- package/src/ts/types.ts +391 -0
- package/src/ts/utils.ts +512 -0
- package/src/ts/webgl2-renderer.ts +415 -0
- package/src/ts/webgpu-renderer.ts +728 -0
- package/src/ts/worker.ts +1866 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
var u={bt709:"BT709",bt470bg:"BT601",smpte170m:"BT601"},y={BT601:{BT709:"1.0863 -0.0723 -0.014 0 0 0.0965 0.8451 0.0584 0 0 -0.0141 -0.0277 1.0418"},BT709:{BT601:"0.9137 0.0784 0.0079 0 0 -0.1049 1.1722 -0.0671 0 0 0.0096 0.0322 0.9582"},FCC:{BT709:"1.0873 -0.0736 -0.0137 0 0 0.0974 0.8494 0.0531 0 0 -0.0127 -0.0251 1.0378",BT601:"1.001 -0.0008 -0.0002 0 0 0.0009 1.005 -0.006 0 0 0.0013 0.0027 0.996"},SMPTE240M:{BT709:"0.9993 0.0006 0.0001 0 0 -0.0004 0.9812 0.0192 0 0 -0.0034 -0.0114 1.0148",BT601:"0.913 0.0774 0.0096 0 0 -0.1051 1.1508 -0.0456 0 0 0.0063 0.0207 0.973"}},g=[null,"BT601",null,"BT601","BT601","BT709","BT709","SMPTE240M","SMPTE240M","FCC","FCC"];function s(J,K){if(!J||!K)return null;if(J===K)return null;let Z=y[J]?.[K];if(!Z)return null;return`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${Z} 0 0 0 0 0 1 0'/></filter></svg>#f")`}function w(J,K,Z,N,Q){let F=Z<=0?1:Z,$=globalThis.devicePixelRatio||1;if(K<=0||J<=0)return{width:0,height:0};let U=F<1?-1:1,O=K*$;if(U*O*F<=U*N)O*=F;else if(U*O<U*N)O=N;if(Q>0&&O>Q)O=Q;return J*=O/K,K=O,{width:J,height:K}}function c(J,K=J.videoWidth,Z=J.videoHeight){let N=K/Z,{offsetWidth:Q,offsetHeight:F}=J,$=Q/F,U=Q,O=F;if($>N)U=Math.floor(F*N);else O=Math.floor(Q/N);let E=(Q-U)/2,q=(F-O)/2;return{width:U,height:O,x:E,y:q}}function m(J,K){if(!K)return J;let Z=J.length,N=Z-Z%16,Q=3;for(;Q<N;Q+=16){if(J[Q]<2)J[Q]=1;if(J[Q+4]<2)J[Q+4]=1;if(J[Q+8]<2)J[Q+8]=1;if(J[Q+12]<2)J[Q+12]=1}for(;Q<Z;Q+=4)if(J[Q]<2)J[Q]=1;return J}function o(J,K=!1){let Z=[],N=J.split(/[\r\n]+/g),Q=N.length,F=null,$=null;for(let U=0;U<Q;U++){let O=N[U];if(!O||/^\s*$/.test(O))continue;let E=O[0];if(E==="["){let q=O.match(/^\[(.*)\]$/);if(q){if(K&&q[1].toLowerCase()==="events")break;F=null,$={name:q[1],body:[]},Z.push($);continue}}if(!$)continue;if(E===";")$.body.push({type:"comment",value:O.substring(1)});else{let q=O.indexOf(":");if(q===-1)continue;let L=O.substring(0,q),_=O.substring(q+1).trim();if(F||L==="Format"){let Y=_.split(",");if(F&&Y.length>F.length){let z=Y.slice(F.length-1).join(",");Y=Y.slice(0,F.length-1),Y.push(z)}let T=Y.length;for(let z=0;z<T;z++)Y[z]=Y[z].trim();if(F){let z={},j=Math.min(F.length,T);for(let P=0;P<j;P++)z[F[P]]=Y[P];_=z}else _=Y}if(L==="Format")F=_;$.body.push({key:L,value:_})}}return Z}var r=/\\blur(?:[0-9]+\.)?[0-9]+/gm;function n(J){return J.replace(r,"")}var i=[{w:7680,h:4320},{w:3840,h:2160},{w:2560,h:1440},{w:1920,h:1080},{w:1280,h:720}];function a(J,K){let Z=[...i].sort((N,Q)=>N.w-Q.w);for(let N of Z)if(J<=N.w&&K<=N.h)return N;return{w:Math.ceil(J/100)*100,h:Math.ceil(K/100)*100}}function k(J,K){return K&&K.includes(".")?J.toFixed(2).replace(/\.?0+$/,""):Math.round(J)}function t(J){let K=J.match(/PlayResX:\s*(\d+)/i),Z=J.match(/PlayResY:\s*(\d+)/i),N=K?parseInt(K[1],10):1920,Q=Z?parseInt(Z[1],10):1080,F=/\\pos\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,$=/\\move\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)/g,U=/\\org\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,O=/\\i?clip\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,E=0,q=0,L=(M,X,V)=>{let I,D=new RegExp(M.source,"g");while((I=D.exec(J))!==null){for(let C of X)if(I[C]){let A=Math.abs(parseFloat(I[C]));if(A>E)E=A}for(let C of V)if(I[C]){let A=Math.abs(parseFloat(I[C]));if(A>q)q=A}}};if(L(F,[1],[2]),L($,[1,3],[2,4]),L(U,[1],[2]),L(O,[1,3],[2,4]),E<=N&&q<=Q)return J;let _=a(E,q),Y=N/_.w,T=Q/_.h,z=Math.min(Y,T),j=Math.max(Y,T),P=1,W=J,B=W.match(/(\[Events\][\s\S]*)/i);if(!B)return W;let G=B[1];return G=G.replace(F,(M,X,V)=>`\\pos(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*T,V)})`),G=G.replace(/\\move\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)(?:\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+))?\s*\)/g,(M,X,V,I,D,C,A)=>{let h=`\\move(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*T,V)},${k(parseFloat(I)*Y,I)},${k(parseFloat(D)*T,D)}`;return C?`${h},${C},${A})`:`${h})`}),G=G.replace(U,(M,X,V)=>`\\org(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*T,V)})`),G=G.replace(/\\(i?clip)\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,(M,X,V,I,D,C)=>`\\${X}(${k(parseFloat(V)*Y,V)},${k(parseFloat(I)*T,I)},${k(parseFloat(D)*Y,D)},${k(parseFloat(C)*T,C)})`),G=G.replace(/\\fs([\d.]+)/g,(M,X)=>`\\fs${k(parseFloat(X)*j,X)}`),G=G.replace(/\\fscx([\d.]+)/g,(M,X)=>`\\fscx${k(parseFloat(X)*P,X)}`),G=G.replace(/\\xbord([\d.]+)/g,(M,X)=>`\\xbord${k(parseFloat(X)*Y,X)}`),G=G.replace(/\\ybord([\d.]+)/g,(M,X)=>`\\ybord${k(parseFloat(X)*T,X)}`),G=G.replace(/\\xshad(-?[\d.]+)/g,(M,X)=>`\\xshad${k(parseFloat(X)*Y,X)}`),G=G.replace(/\\yshad(-?[\d.]+)/g,(M,X)=>`\\yshad${k(parseFloat(X)*T,X)}`),["fsp","bord","shad","be","blur"].forEach((M)=>{let X=new RegExp(`\\\\${M}(-?[\\d.]+)`,"g");G=G.replace(X,(V,I)=>`\\${M}${k(parseFloat(I)*z,I)}`)}),G=G.replace(/(\\i?clip\s*\([^,)]+m[^)]+\)|\\p[1-9][^}]*?)(?=[\\}]|$)/g,(M)=>{return M.replace(/(-?[\d.]+)\s+(-?[\d.]+)/g,(X,V,I)=>{return`${k(parseFloat(V)*Y,V)} ${k(parseFloat(I)*T,I)}`})}),W.substring(0,B.index)+G}var b=null,S=null;async function l(){if(b!==null&&S!==null)return{hasAlphaBug:b,hasBitmapBug:S};let J=document.createElement("canvas"),K=J.getContext("2d",{willReadFrequently:!0});if(!K)throw Error("Canvas rendering not supported");if(typeof ImageData.prototype.constructor==="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so")}let Z=document.createElement("canvas"),N=Z.getContext("2d",{willReadFrequently:!0});if(!N)throw Error("Canvas rendering not supported");J.width=Z.width=1,J.height=Z.height=1,K.clearRect(0,0,1,1),N.clearRect(0,0,1,1);let Q=N.getImageData(0,0,1,1).data;K.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),N.drawImage(J,0,0);let F=N.getImageData(0,0,1,1).data;if(b=Q[1]!==F[1],b)console.log("Detected a browser having issue with transparent pixels, applying workaround");if(typeof createImageBitmap<"u"){let $=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);N.drawImage(await createImageBitmap(new ImageData($,1)),0,0);let{data:U}=N.getImageData(0,0,1,1);S=!1;for(let O=0;O<U.length;O++)if(Math.abs($[O]-U[O])>15){S=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else S=!1;return J.remove(),Z.remove(),{hasAlphaBug:b,hasBitmapBug:S}}async function e(){return l()}function JJ(){return b}function KJ(){return S}function f(){return typeof navigator<"u"&&"gpu"in navigator}class v{device=null;context=null;pipeline=null;bindGroupLayout=null;uniformBuffer=null;imageDataBuffer=null;textureArray=null;textureArrayView=null;textureArraySize=0;textureArrayWidth=0;textureArrayHeight=0;pendingDestroyTextures=[];imageDataArray;resolutionArray=new Float32Array(2);conversionBuffer=null;conversionBufferSize=0;bindGroup=null;bindGroupDirty=!0;lastCanvasWidth=0;lastCanvasHeight=0;format="bgra8unorm";_canvas=null;_initPromise=null;_initialized=!1;constructor(){this.imageDataArray=new Float32Array(2048)}async init(){if(this._initPromise)return this._initPromise;return this._initPromise=this._initDevice(),this._initPromise}async _initDevice(){if(!navigator.gpu)throw Error("WebGPU not supported");let J=await navigator.gpu.requestAdapter({powerPreference:"high-performance"});if(!J)throw Error("No WebGPU adapter found");this.device=await J.requestDevice(),this.format=navigator.gpu.getPreferredCanvasFormat();let K=this.device.createShaderModule({code:`
|
|
2
|
+
struct VertexOutput {
|
|
3
|
+
@builtin(position) position: vec4f,
|
|
4
|
+
@location(0) @interpolate(flat) instanceIndex: u32,
|
|
5
|
+
@location(1) @interpolate(flat) destXY: vec2f,
|
|
6
|
+
@location(2) @interpolate(flat) texSize: vec2f,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
struct Uniforms {
|
|
10
|
+
resolution: vec2f,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
struct ImageData {
|
|
14
|
+
destRect: vec4f, // x, y, w, h
|
|
15
|
+
texInfo: vec4f, // texW, texH, texIndex, 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
19
|
+
@group(0) @binding(1) var<storage, read> imageData: array<ImageData>;
|
|
20
|
+
|
|
21
|
+
// Quad vertices (two triangles)
|
|
22
|
+
const QUAD_POSITIONS = array<vec2f, 6>(
|
|
23
|
+
vec2f(0.0, 0.0),
|
|
24
|
+
vec2f(1.0, 0.0),
|
|
25
|
+
vec2f(0.0, 1.0),
|
|
26
|
+
vec2f(1.0, 0.0),
|
|
27
|
+
vec2f(1.0, 1.0),
|
|
28
|
+
vec2f(0.0, 1.0)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
@vertex
|
|
32
|
+
fn vertexMain(
|
|
33
|
+
@builtin(vertex_index) vertexIndex: u32,
|
|
34
|
+
@builtin(instance_index) instanceIndex: u32
|
|
35
|
+
) -> VertexOutput {
|
|
36
|
+
var output: VertexOutput;
|
|
37
|
+
|
|
38
|
+
let data = imageData[instanceIndex];
|
|
39
|
+
let quadPos = QUAD_POSITIONS[vertexIndex];
|
|
40
|
+
let wh = data.destRect.zw;
|
|
41
|
+
|
|
42
|
+
// Calculate pixel position
|
|
43
|
+
let pixelPos = data.destRect.xy + quadPos * wh;
|
|
44
|
+
|
|
45
|
+
// Convert to clip space (-1 to 1)
|
|
46
|
+
var clipPos = (pixelPos / uniforms.resolution) * 2.0 - 1.0;
|
|
47
|
+
clipPos.y = -clipPos.y;
|
|
48
|
+
|
|
49
|
+
output.position = vec4f(clipPos, 0.0, 1.0);
|
|
50
|
+
output.instanceIndex = instanceIndex;
|
|
51
|
+
output.destXY = data.destRect.xy;
|
|
52
|
+
output.texSize = data.texInfo.xy;
|
|
53
|
+
|
|
54
|
+
return output;
|
|
55
|
+
}
|
|
56
|
+
`}),Z=this.device.createShaderModule({code:`
|
|
57
|
+
@group(0) @binding(2) var texArray: texture_2d_array<f32>;
|
|
58
|
+
|
|
59
|
+
struct ImageData {
|
|
60
|
+
destRect: vec4f,
|
|
61
|
+
texInfo: vec4f,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@group(0) @binding(1) var<storage, read> imageData: array<ImageData>;
|
|
65
|
+
|
|
66
|
+
struct FragmentInput {
|
|
67
|
+
@builtin(position) fragCoord: vec4f,
|
|
68
|
+
@location(0) @interpolate(flat) instanceIndex: u32,
|
|
69
|
+
@location(1) @interpolate(flat) destXY: vec2f,
|
|
70
|
+
@location(2) @interpolate(flat) texSize: vec2f,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@fragment
|
|
74
|
+
fn fragmentMain(input: FragmentInput) -> @location(0) vec4f {
|
|
75
|
+
let data = imageData[input.instanceIndex];
|
|
76
|
+
let texIndex = u32(data.texInfo.z);
|
|
77
|
+
|
|
78
|
+
// Calculate texel coordinates
|
|
79
|
+
let texCoordF = floor(input.fragCoord.xy - input.destXY);
|
|
80
|
+
let texCoord = vec2i(texCoordF);
|
|
81
|
+
|
|
82
|
+
// Bounds check
|
|
83
|
+
let texSizeI = vec2i(input.texSize);
|
|
84
|
+
if (texCoord.x < 0 || texCoord.y < 0 || texCoord.x >= texSizeI.x || texCoord.y >= texSizeI.y) {
|
|
85
|
+
discard;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Load from texture array
|
|
89
|
+
let color = textureLoad(texArray, texCoord, texIndex, 0);
|
|
90
|
+
|
|
91
|
+
// Premultiplied alpha output
|
|
92
|
+
return vec4f(color.rgb * color.a, color.a);
|
|
93
|
+
}
|
|
94
|
+
`});this.uniformBuffer=this.device.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.imageDataBuffer=this.device.createBuffer({size:8192,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.createTextureArray(256,256,32),this.bindGroupLayout=this.device.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"unfilterable-float",viewDimension:"2d-array"}}]});let N=this.device.createPipelineLayout({bindGroupLayouts:[this.bindGroupLayout]});this.pipeline=this.device.createRenderPipeline({layout:N,vertex:{module:K,entryPoint:"vertexMain"},fragment:{module:Z,entryPoint:"fragmentMain",targets:[{format:this.format,blend:{color:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha",operation:"add"}}}]},primitive:{topology:"triangle-list"}}),this._initialized=!0}nextPowerOf2(J){return J--,J|=J>>1,J|=J>>2,J|=J>>4,J|=J>>8,J|=J>>16,J+1}createTextureArray(J,K,Z){if(this.textureArray)this.pendingDestroyTextures.push(this.textureArray);let N=this.nextPowerOf2(Math.max(J,64)),Q=this.nextPowerOf2(Math.max(K,64)),F=Math.min(this.nextPowerOf2(Math.max(Z,16)),256);this.textureArray=this.device.createTexture({size:[N,Q,F],format:this.format,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),this.textureArrayView=this.textureArray.createView({dimension:"2d-array"}),this.textureArrayWidth=N,this.textureArrayHeight=Q,this.textureArraySize=F,this.bindGroupDirty=!0;let $=this.device.createCommandEncoder();for(let U=0;U<F;U++){let O=this.textureArray.createView({dimension:"2d",baseArrayLayer:U,arrayLayerCount:1});$.beginRenderPass({colorAttachments:[{view:O,clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}]}).end()}this.device.queue.submit([$.finish()])}ensureTextureArray(J,K,Z){let N=Math.min(Z,256);if(J<=this.textureArrayWidth&&K<=this.textureArrayHeight&&N<=this.textureArraySize)return!1;let Q=this.nextPowerOf2(Math.max(this.textureArrayWidth,J)),F=this.nextPowerOf2(Math.max(this.textureArrayHeight,K)),$=Math.min(this.nextPowerOf2(Math.max(this.textureArraySize,N,N+16)),256);return this.createTextureArray(Q,F,$),!0}updateBindGroup(){if(!this.bindGroupDirty||!this.device||!this.bindGroupLayout)return;this.bindGroup=this.device.createBindGroup({layout:this.bindGroupLayout,entries:[{binding:0,resource:{buffer:this.uniformBuffer}},{binding:1,resource:{buffer:this.imageDataBuffer}},{binding:2,resource:this.textureArrayView}]}),this.bindGroupDirty=!1}ensureConversionBuffer(J){if(this.conversionBufferSize<J)this.conversionBufferSize=Math.max(J,this.conversionBufferSize*1.5|0,65536),this.conversionBuffer=new Uint8Array(this.conversionBufferSize);return this.conversionBuffer}async setCanvas(J,K,Z){if(await this.init(),!this.device)throw Error("WebGPU device not initialized");if(K<=0||Z<=0)return;if(this._canvas=J,J.width=K,J.height=Z,!this.context){if(this.context=J.getContext("webgpu"),!this.context)throw Error("Could not get WebGPU context");this.context.configure({device:this.device,format:this.format,alphaMode:"premultiplied"})}this.resolutionArray[0]=K,this.resolutionArray[1]=Z,this.device.queue.writeBuffer(this.uniformBuffer,0,this.resolutionArray),this.lastCanvasWidth=K,this.lastCanvasHeight=Z}updateSize(J,K){if(!this.device||!this._canvas||J<=0||K<=0)return;if(J===this.lastCanvasWidth&&K===this.lastCanvasHeight)return;this._canvas.width=J,this._canvas.height=K,this.resolutionArray[0]=J,this.resolutionArray[1]=K,this.device.queue.writeBuffer(this.uniformBuffer,0,this.resolutionArray),this.lastCanvasWidth=J,this.lastCanvasHeight=K}renderBitmaps(J,K,Z){if(!this.device||!this.context||!this.pipeline)return;let N=J.length;if(N===0){this.clear();return}let Q=this.context.getCurrentTexture();if(Q.width===0||Q.height===0)return;let F=0,$=0,U=0;for(let j=0;j<N;j++){let{image:P}=J[j],W=P.width,B=P.height;if(W>0&&B>0){if(W>F)F=W;if(B>$)$=B;U++}}if(U===0){this.clear();return}let O=Math.min(U,256);this.ensureTextureArray(F,$,O),this.updateBindGroup();let E=this.device,q=E.queue,L=this.textureArray,_=this.imageDataArray,Y=Q.createView(),T=0,z=!0;while(T<N){let j=0;while(T<N&&j<256){let B=J[T++],G=B.image,H=G.width,M=G.height;if(H<=0||M<=0)continue;q.copyExternalImageToTexture({source:G,flipY:!1},{texture:L,origin:[0,0,j],premultipliedAlpha:!1},{width:H,height:M});let X=j<<3;_[X]=B.x,_[X+1]=B.y,_[X+2]=H,_[X+3]=M,_[X+4]=H,_[X+5]=M,_[X+6]=j,_[X+7]=0,j++}if(j===0)continue;q.writeBuffer(this.imageDataBuffer,0,_.buffer,0,j<<5);let P=E.createCommandEncoder(),W=P.beginRenderPass({colorAttachments:[{view:Y,clearValue:{r:0,g:0,b:0,a:0},loadOp:z?"clear":"load",storeOp:"store"}]});W.setPipeline(this.pipeline),W.setBindGroup(0,this.bindGroup),W.draw(6,j),W.end(),q.submit([P.finish()]),z=!1}this.cleanupPendingTextures()}render(J,K,Z){if(!this.device||!this.context||!this.pipeline)return;let N=J.length;if(N===0){this.clear();return}let Q=this.context.getCurrentTexture();if(Q.width===0||Q.height===0)return;let F=0,$=0,U=0;for(let P=0;P<N;P++){let{w:W,h:B}=J[P];if(W>0&&B>0){if(W>F)F=W;if(B>$)$=B;U++}}if(U===0){this.clear();return}let O=Math.min(U,256);this.ensureTextureArray(F,$,O),this.updateBindGroup();let E=this.device,q=E.queue,L=this.textureArray,_=this.imageDataArray,Y=this.format==="bgra8unorm",T=Q.createView(),z=0,j=!0;while(z<N){let P=0;while(z<N&&P<256){let G=J[z++],H=G.w,M=G.h;if(H<=0||M<=0)continue;let X=G.image;if(X instanceof ImageBitmap)q.copyExternalImageToTexture({source:X,flipY:!1},{texture:L,origin:[0,0,P],premultipliedAlpha:!1},{width:H,height:M});else if(X instanceof ArrayBuffer)this.uploadTextureData(P,X,H,M,Y);let V=P<<3;_[V]=G.x,_[V+1]=G.y,_[V+2]=H,_[V+3]=M,_[V+4]=H,_[V+5]=M,_[V+6]=P,_[V+7]=0,P++}if(P===0)continue;q.writeBuffer(this.imageDataBuffer,0,_.buffer,0,P<<5);let W=E.createCommandEncoder(),B=W.beginRenderPass({colorAttachments:[{view:T,clearValue:{r:0,g:0,b:0,a:0},loadOp:j?"clear":"load",storeOp:"store"}]});B.setPipeline(this.pipeline),B.setBindGroup(0,this.bindGroup),B.draw(6,P),B.end(),q.submit([W.finish()]),j=!1}this.cleanupPendingTextures()}uploadTextureData(J,K,Z,N,Q){let F=Z*N*4;if(Q){let $=this.ensureConversionBuffer(F),U=new Uint8Array(K);for(let O=0;O<F;O+=4)$[O]=U[O+2],$[O+1]=U[O+1],$[O+2]=U[O],$[O+3]=U[O+3];this.device.queue.writeTexture({texture:this.textureArray,origin:[0,0,J]},$.buffer,{bytesPerRow:Z*4},{width:Z,height:N})}else this.device.queue.writeTexture({texture:this.textureArray,origin:[0,0,J]},K,{bytesPerRow:Z*4},{width:Z,height:N})}cleanupPendingTextures(){let J=this.pendingDestroyTextures,K=J.length;if(K===0)return;for(let Z=0;Z<K;Z++)J[Z].destroy();J.length=0}clear(){if(!this.device||!this.context)return;try{let J=this.context.getCurrentTexture();if(J.width===0||J.height===0)return;let K=this.device.createCommandEncoder();K.beginRenderPass({colorAttachments:[{view:J.createView(),clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}]}).end(),this.device.queue.submit([K.finish()])}catch{}}get initialized(){return this._initialized}destroy(){this.cleanupPendingTextures(),this.textureArray?.destroy(),this.textureArray=null,this.textureArrayView=null,this.uniformBuffer?.destroy(),this.uniformBuffer=null,this.imageDataBuffer?.destroy(),this.imageDataBuffer=null,this.bindGroup=null,this.conversionBuffer=null,this.conversionBufferSize=0,this.device?.destroy(),this.device=null,this.context=null,this._canvas=null,this._initialized=!1,this._initPromise=null}}function p(){if(typeof document>"u")return!1;try{return document.createElement("canvas").getContext("webgl2")!==null}catch{return!1}}function d(J,K,Z){let N=J.createShader(K);if(J.shaderSource(N,Z),J.compileShader(N),!J.getShaderParameter(N,J.COMPILE_STATUS)){let Q=J.getShaderInfoLog(N);throw J.deleteShader(N),Error(`WebGL2 shader compilation failed: ${Q}`)}return N}class x{_gl=null;_canvas=null;_program=null;_vao=null;_instanceBuffer=null;_texArray=null;_texWidth=0;_texHeight=0;_texLayers=0;_resolutionLoc=null;_texArraySizeLoc=null;_instanceData;_lastCanvasWidth=0;_lastCanvasHeight=0;_initialized=!1;_initPromise=null;constructor(){this._instanceData=new Float32Array(2048)}async init(){if(this._initPromise)return this._initPromise;return this._initPromise=this._checkSupport(),this._initPromise}async _checkSupport(){if(typeof document>"u")throw Error("WebGL2 requires a DOM environment");if(!document.createElement("canvas").getContext("webgl2"))throw Error("WebGL2 not supported")}_initGL(){if(!this._canvas)throw Error("Canvas not set before _initGL");if(this._gl)return;let J=this._canvas.getContext("webgl2",{alpha:!0,premultipliedAlpha:!0,antialias:!1});if(!J)throw Error("Failed to create WebGL2 context");this._gl=J;let K=d(J,J.VERTEX_SHADER,`#version 300 es
|
|
95
|
+
precision highp float;
|
|
96
|
+
|
|
97
|
+
in vec4 a_destRect;
|
|
98
|
+
in vec4 a_texInfo;
|
|
99
|
+
|
|
100
|
+
uniform vec2 u_resolution;
|
|
101
|
+
|
|
102
|
+
out vec2 v_uv;
|
|
103
|
+
flat out int v_texIndex;
|
|
104
|
+
flat out vec2 v_texSize;
|
|
105
|
+
|
|
106
|
+
vec2 quadPos(int id) {
|
|
107
|
+
if (id == 0) return vec2(0.0, 0.0);
|
|
108
|
+
if (id == 1) return vec2(1.0, 0.0);
|
|
109
|
+
if (id == 2) return vec2(0.0, 1.0);
|
|
110
|
+
if (id == 3) return vec2(1.0, 0.0);
|
|
111
|
+
if (id == 4) return vec2(1.0, 1.0);
|
|
112
|
+
return vec2(0.0, 1.0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void main() {
|
|
116
|
+
vec2 qp = quadPos(gl_VertexID);
|
|
117
|
+
vec2 pixelPos = a_destRect.xy + qp * a_destRect.zw;
|
|
118
|
+
|
|
119
|
+
// Convert CSS pixel coords (y=0 top) to GL clip space (y=1 top)
|
|
120
|
+
vec2 clip = (pixelPos / u_resolution) * 2.0 - 1.0;
|
|
121
|
+
clip.y = -clip.y;
|
|
122
|
+
|
|
123
|
+
gl_Position = vec4(clip, 0.0, 1.0);
|
|
124
|
+
v_uv = qp;
|
|
125
|
+
v_texIndex = int(a_texInfo.z);
|
|
126
|
+
v_texSize = a_texInfo.xy;
|
|
127
|
+
}
|
|
128
|
+
`),Z=d(J,J.FRAGMENT_SHADER,`#version 300 es
|
|
129
|
+
precision highp float;
|
|
130
|
+
precision highp sampler2DArray;
|
|
131
|
+
|
|
132
|
+
uniform sampler2DArray u_texArray;
|
|
133
|
+
uniform ivec2 u_texArraySize;
|
|
134
|
+
|
|
135
|
+
in vec2 v_uv;
|
|
136
|
+
flat in int v_texIndex;
|
|
137
|
+
flat in vec2 v_texSize;
|
|
138
|
+
|
|
139
|
+
out vec4 fragColor;
|
|
140
|
+
|
|
141
|
+
void main() {
|
|
142
|
+
vec2 normalizedCoord = v_uv * v_texSize / vec2(u_texArraySize);
|
|
143
|
+
vec4 color = texture(u_texArray, vec3(normalizedCoord, float(v_texIndex)));
|
|
144
|
+
// Premultiplied alpha output (matches WebGPU renderer behaviour)
|
|
145
|
+
fragColor = vec4(color.rgb * color.a, color.a);
|
|
146
|
+
}
|
|
147
|
+
`),N=J.createProgram();if(J.attachShader(N,K),J.attachShader(N,Z),J.linkProgram(N),!J.getProgramParameter(N,J.LINK_STATUS))throw Error(`WebGL2 program link failed: ${J.getProgramInfoLog(N)}`);J.deleteShader(K),J.deleteShader(Z),this._program=N,this._resolutionLoc=J.getUniformLocation(N,"u_resolution"),this._texArraySizeLoc=J.getUniformLocation(N,"u_texArraySize"),this._vao=J.createVertexArray(),J.bindVertexArray(this._vao),this._instanceBuffer=J.createBuffer(),J.bindBuffer(J.ARRAY_BUFFER,this._instanceBuffer),J.bufferData(J.ARRAY_BUFFER,8192,J.DYNAMIC_DRAW);let Q=J.getAttribLocation(N,"a_destRect");J.enableVertexAttribArray(Q),J.vertexAttribPointer(Q,4,J.FLOAT,!1,32,0),J.vertexAttribDivisor(Q,1);let F=J.getAttribLocation(N,"a_texInfo");J.enableVertexAttribArray(F),J.vertexAttribPointer(F,4,J.FLOAT,!1,32,16),J.vertexAttribDivisor(F,1),J.bindVertexArray(null),this._texArray=J.createTexture(),this._allocateTextureArray(256,256,32),J.enable(J.BLEND),J.blendEquation(J.FUNC_ADD),J.blendFunc(J.ONE,J.ONE_MINUS_SRC_ALPHA),this._initialized=!0}_nextPow2(J){return J--,J|=J>>1,J|=J>>2,J|=J>>4,J|=J>>8,J|=J>>16,J+1}_allocateTextureArray(J,K,Z){let N=this._gl,Q=this._nextPow2(Math.max(J,64)),F=this._nextPow2(Math.max(K,64)),$=Math.min(this._nextPow2(Math.max(Z,16)),256);N.bindTexture(N.TEXTURE_2D_ARRAY,this._texArray),N.texImage3D(N.TEXTURE_2D_ARRAY,0,N.RGBA8,Q,F,$,0,N.RGBA,N.UNSIGNED_BYTE,null),N.texParameteri(N.TEXTURE_2D_ARRAY,N.TEXTURE_MIN_FILTER,N.NEAREST),N.texParameteri(N.TEXTURE_2D_ARRAY,N.TEXTURE_MAG_FILTER,N.NEAREST),N.texParameteri(N.TEXTURE_2D_ARRAY,N.TEXTURE_WRAP_S,N.CLAMP_TO_EDGE),N.texParameteri(N.TEXTURE_2D_ARRAY,N.TEXTURE_WRAP_T,N.CLAMP_TO_EDGE),this._texWidth=Q,this._texHeight=F,this._texLayers=$}_ensureTextureArray(J,K,Z){let N=Math.min(Z,256);if(J<=this._texWidth&&K<=this._texHeight&&N<=this._texLayers)return;let Q=this._nextPow2(Math.max(this._texWidth,J)),F=this._nextPow2(Math.max(this._texHeight,K)),$=Math.min(this._nextPow2(Math.max(this._texLayers,N,N+16)),256);this._allocateTextureArray(Q,F,$)}async setCanvas(J,K,Z){if(await this.init(),K<=0||Z<=0)return;this._canvas=J,J.width=K,J.height=Z,this._initGL(),this._gl.viewport(0,0,K,Z),this._lastCanvasWidth=K,this._lastCanvasHeight=Z}updateSize(J,K){if(!this._gl||!this._canvas||J<=0||K<=0)return;if(J===this._lastCanvasWidth&&K===this._lastCanvasHeight)return;this._canvas.width=J,this._canvas.height=K,this._gl.viewport(0,0,J,K),this._lastCanvasWidth=J,this._lastCanvasHeight=K}renderBitmaps(J,K,Z){if(!this._gl||!this._initialized)return;let N=J.length;if(N===0){this.clear();return}let Q=0,F=0;for(let E=0;E<N;E++){let{image:q}=J[E];if(q.width>Q)Q=q.width;if(q.height>F)F=q.height}this._ensureTextureArray(Q,F,Math.min(N,256));let $=this._gl;$.clearColor(0,0,0,0),$.clear($.COLOR_BUFFER_BIT),$.useProgram(this._program),$.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),$.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),$.activeTexture($.TEXTURE0),$.bindTexture($.TEXTURE_2D_ARRAY,this._texArray),$.pixelStorei($.UNPACK_FLIP_Y_WEBGL,!1);let U=this._instanceData,O=0;while(O<N){let E=0;while(O<N&&E<256){let q=J[O++],L=q.image.width,_=q.image.height;if(L<=0||_<=0)continue;$.texSubImage3D($.TEXTURE_2D_ARRAY,0,0,0,E,L,_,1,$.RGBA,$.UNSIGNED_BYTE,q.image);let Y=E<<3;U[Y]=q.x,U[Y+1]=q.y,U[Y+2]=L,U[Y+3]=_,U[Y+4]=L,U[Y+5]=_,U[Y+6]=E,U[Y+7]=0,E++}if(E===0)continue;$.bindBuffer($.ARRAY_BUFFER,this._instanceBuffer),$.bufferSubData($.ARRAY_BUFFER,0,U,0,E<<3),$.bindVertexArray(this._vao),$.drawArraysInstanced($.TRIANGLES,0,6,E),$.bindVertexArray(null)}}render(J,K,Z){if(!this._gl||!this._initialized)return;let N=J.length;if(N===0){this.clear();return}let Q=0,F=0;for(let E=0;E<N;E++){let{w:q,h:L}=J[E];if(q>Q)Q=q;if(L>F)F=L}this._ensureTextureArray(Q,F,Math.min(N,256));let $=this._gl;$.clearColor(0,0,0,0),$.clear($.COLOR_BUFFER_BIT),$.useProgram(this._program),$.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),$.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),$.activeTexture($.TEXTURE0),$.bindTexture($.TEXTURE_2D_ARRAY,this._texArray),$.pixelStorei($.UNPACK_FLIP_Y_WEBGL,!1);let U=this._instanceData,O=0;while(O<N){let E=0;while(O<N&&E<256){let q=J[O++],L=q.w,_=q.h;if(L<=0||_<=0)continue;let Y=q.image;if(Y instanceof ImageBitmap)$.texSubImage3D($.TEXTURE_2D_ARRAY,0,0,0,E,L,_,1,$.RGBA,$.UNSIGNED_BYTE,Y);else if(Y instanceof ArrayBuffer)$.texSubImage3D($.TEXTURE_2D_ARRAY,0,0,0,E,L,_,1,$.RGBA,$.UNSIGNED_BYTE,new Uint8Array(Y));let T=E<<3;U[T]=q.x,U[T+1]=q.y,U[T+2]=L,U[T+3]=_,U[T+4]=L,U[T+5]=_,U[T+6]=E,U[T+7]=0,E++}if(E===0)continue;$.bindBuffer($.ARRAY_BUFFER,this._instanceBuffer),$.bufferSubData($.ARRAY_BUFFER,0,U,0,E<<3),$.bindVertexArray(this._vao),$.drawArraysInstanced($.TRIANGLES,0,6,E),$.bindVertexArray(null)}}clear(){if(!this._gl)return;this._gl.clearColor(0,0,0,0),this._gl.clear(this._gl.COLOR_BUFFER_BIT)}get initialized(){return this._initialized}destroy(){let J=this._gl;if(J)J.deleteProgram(this._program),J.deleteVertexArray(this._vao),J.deleteBuffer(this._instanceBuffer),J.deleteTexture(this._texArray);this._gl=null,this._program=null,this._vao=null,this._instanceBuffer=null,this._texArray=null,this._canvas=null,this._initialized=!1,this._initPromise=null}}class R extends EventTarget{static MAX_PENDING_DEMANDS=3;static _hasAlphaBug=null;static _hasBitmapBug=null;_loaded;_init;_onDemandRender;_offscreenRender;_video;_videoWidth=0;_videoHeight=0;_videoColorSpace=null;_canvas;_canvasParent;_bufferCanvas;_bufferCtx;_canvasctrl;_ctx=null;_lastRenderTime=0;_playstate=!0;_destroyed=!1;_workerReady=!1;_ro;_worker;_pendingDemandTimes=[];_boundResize;_boundTimeUpdate;_boundSetRate;_boundUpdateColorSpace;_boundHandleRVFC;_gpuRenderer=null;_rendererType="canvas2d";_onCanvasFallback;_lastRenderWidth=0;_lastRenderHeight=0;timeOffset;debug;prescaleFactor;prescaleHeightLimit;maxRenderHeight;busy=!1;renderAhead;constructor(J){super();if(!globalThis.Worker)throw this.destroy(Error("Worker not supported"));if(!J)throw this.destroy(Error("No options provided"));this._loaded=new Promise((Q)=>{this._init=Q});let K=R._test();this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(J.onDemandRender??!0),this._onCanvasFallback=J.onCanvasFallback;let Z=!J.canvas&&(f()||p());if(this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!J.canvas&&!Z&&(J.offscreenRender??!0),this.timeOffset=J.timeOffset||0,this._video=J.video,this._canvas=J.canvas,this._video&&!this._canvas)this._canvasParent=document.createElement("div"),this._canvasParent.className="AkariSub",this._canvasParent.style.position="relative",this._canvas=this._createCanvas(),this._video.insertAdjacentElement("afterend",this._canvasParent);else if(!this._canvas)throw this.destroy(Error("Don't know where to render: you should give video or canvas in options."));this._bufferCanvas=document.createElement("canvas");let N=this._bufferCanvas.getContext("2d");if(!N)throw this.destroy(Error("Canvas rendering not supported"));if(this._bufferCtx=N,Z)this._initGPURenderer();else if(!this._offscreenRender)this._ctx=this._canvas.getContext("2d");if(this._canvasctrl=this._offscreenRender?this._canvas.transferControlToOffscreen():this._canvas,this._lastRenderTime=0,this.debug=!!J.debug,this.prescaleFactor=J.prescaleFactor||1,this.prescaleHeightLimit=J.prescaleHeightLimit||1080,this.maxRenderHeight=J.maxRenderHeight||0,this.renderAhead=J.renderAhead??0,this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=()=>this.setRate(this._video.playbackRate),this._boundUpdateColorSpace=this._updateColorSpace.bind(this),this._boundHandleRVFC=this._handleRVFC.bind(this),this._video)this.setVideo(this._video);if(this._onDemandRender)this.busy=!1,this._pendingDemandTimes.length=0;this._worker=new Worker(J.workerUrl||"akarisub-worker.js"),this._worker.onmessage=(Q)=>this._onmessage(Q),this._worker.onerror=(Q)=>this._error(Q),K.then(()=>{if(this._worker.postMessage({target:"init",wasmUrl:J.wasmUrl??"akarisub-worker.wasm",asyncRender:typeof createImageBitmap<"u"&&(J.asyncRender??!0),onDemandRender:this._onDemandRender,initialTime:(this._video?.currentTime??0)+this.timeOffset,width:this._canvasctrl.width||0,height:this._canvasctrl.height||0,blendMode:J.blendMode??"wasm",subUrl:J.subUrl,subContent:J.subContent||null,fonts:J.fonts||[],availableFonts:J.availableFonts||{"liberation sans":"./default.woff2"},fallbackFonts:J.fallbackFonts||["liberation sans"],debug:this.debug,targetFps:J.targetFps||24,dropAllAnimations:J.dropAllAnimations,dropAllBlur:J.dropAllBlur,clampPos:J.clampPos,libassMemoryLimit:J.libassMemoryLimit??128,libassGlyphLimit:J.libassGlyphLimit??2048,useLocalFonts:typeof globalThis.queryLocalFonts<"u"&&(J.useLocalFonts??!0),hasBitmapBug:R._hasBitmapBug}),this._offscreenRender)this.sendMessage("offscreenCanvas",{},[this._canvasctrl])})}static async _testImageBugs(){if(R._hasBitmapBug!==null)return;let J=document.createElement("canvas"),K=J.getContext("2d",{willReadFrequently:!0});if(!K)throw Error("Canvas rendering not supported");if(typeof ImageData.prototype.constructor==="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("Detected that ImageData is not constructable despite browser saying so")}let Z=document.createElement("canvas"),N=Z.getContext("2d",{willReadFrequently:!0});if(!N)throw Error("Canvas rendering not supported");J.width=Z.width=1,J.height=Z.height=1,K.clearRect(0,0,1,1),N.clearRect(0,0,1,1);let Q=N.getImageData(0,0,1,1).data;K.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),N.drawImage(J,0,0);let F=N.getImageData(0,0,1,1).data;if(R._hasAlphaBug=Q[1]!==F[1],R._hasAlphaBug)console.log("Detected a browser having issue with transparent pixels, applying workaround");if(typeof createImageBitmap<"u"){let $=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);N.drawImage(await createImageBitmap(new ImageData($,1)),0,0);let{data:U}=N.getImageData(0,0,1,1);R._hasBitmapBug=!1;for(let O=0;O<U.length;O++)if(Math.abs($[O]-U[O])>15){R._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else R._hasBitmapBug=!1;J.remove(),Z.remove()}static async _test(){await R._testImageBugs()}async _initGPURenderer(){if(f())try{let J=new v;if(await J.init(),!this._canvas)return;await J.setCanvas(this._canvas,Math.max(1,this._canvas.width||1),Math.max(1,this._canvas.height||1)),this._gpuRenderer=J,this._rendererType="webgpu",console.log("[AkariSub] Using WebGPU renderer");return}catch(J){console.warn("[AkariSub] WebGPU init failed, trying WebGL2:",J)}if(p())try{let J=new x;if(await J.init(),!this._canvas)return;await J.setCanvas(this._canvas,Math.max(1,this._canvas.width||1),Math.max(1,this._canvas.height||1)),this._gpuRenderer=J,this._rendererType="webgl2",console.log("[AkariSub] Using WebGL2 renderer");return}catch(J){console.warn("[AkariSub] WebGL2 init failed, falling back to Canvas2D:",J)}if(this._rendererType="canvas2d",!this._offscreenRender&&!this._ctx)this._ctx=this._canvas.getContext("2d");this.sendMessage("setAsyncRender",{value:!1}),this._onCanvasFallback?.()}get rendererType(){return this._rendererType}get isUsingWebGPU(){return this._rendererType==="webgpu"}get isUsingGPURenderer(){return this._gpuRenderer!==null}_createCanvas(){return this._canvas=document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._canvas}resize(J=0,K=0,Z=0,N=0,Q=this._video?.paused??!1){if((!J||!K)&&this._video){let F=c(this._video),$;if(this._videoWidth){let U=this._video.videoWidth/this._videoWidth,O=this._video.videoHeight/this._videoHeight;$=w((F.width||0)/U,(F.height||0)/O,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight)}else $=w(F.width||0,F.height||0,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight);if(J=$.width,K=$.height,this._canvasParent)Z=F.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),N=F.x;this._canvas.style.width=F.width+"px",this._canvas.style.height=F.height+"px"}if(this._canvas.style.top=Z+"px",this._canvas.style.left=N+"px",J>0&&K>0)this._canvasctrl.width=J,this._canvasctrl.height=K;if(this._gpuRenderer&&J>0&&K>0)this._gpuRenderer.updateSize(J,K);if(Q&&this.busy===!1)this.busy=!0;else Q=!1;this.sendMessage("canvas",{width:J,height:K,videoWidth:this._videoWidth||this._video?.videoWidth||0,videoHeight:this._videoHeight||this._video?.videoHeight||0,force:Q})}_timeupdate(J){let Z={seeking:!0,waiting:!0,playing:!1}[J.type];if(Z!=null)this._playstate=Z;this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(J){if(J instanceof HTMLVideoElement){if(this._removeListeners(),this._video=J,this._onDemandRender){if(!this._destroyed&&this._video===J)J.requestVideoFrameCallback(this._boundHandleRVFC)}else this._playstate=J.paused,J.addEventListener("timeupdate",this._boundTimeUpdate,!1),J.addEventListener("progress",this._boundTimeUpdate,!1),J.addEventListener("waiting",this._boundTimeUpdate,!1),J.addEventListener("seeking",this._boundTimeUpdate,!1),J.addEventListener("playing",this._boundTimeUpdate,!1),J.addEventListener("ratechange",this._boundSetRate,!1),J.addEventListener("resize",this._boundResize,!1);if("VideoFrame"in window){if(J.addEventListener("loadedmetadata",this._boundUpdateColorSpace,!1),J.readyState>2)this._updateColorSpace()}if(J.videoWidth>0)this.resize();if(typeof ResizeObserver<"u"){if(!this._ro)this._ro=new ResizeObserver(()=>this.resize());this._ro.observe(J)}}else this._error(Error("Video element invalid!"))}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(J){if(this.sendMessage("setTrackByUrl",{url:J}),this._reAttachOffscreen(),this._ctx)this._ctx.filter="none"}setTrack(J){if(this.sendMessage("setTrack",{content:J}),this._reAttachOffscreen(),this._ctx)this._ctx.filter="none"}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(J){this.sendMessage("video",{isPaused:J})}setRate(J){this.sendMessage("video",{rate:J})}setCurrentTime(J,K,Z){this.sendMessage("video",{isPaused:J,currentTime:K,rate:Z,colorSpace:this._videoColorSpace})}createEvent(J){this.sendMessage("createEvent",{event:J})}setEvent(J,K){this.sendMessage("setEvent",{event:J,index:K})}removeEvent(J){this.sendMessage("removeEvent",{index:J})}async getEvents(){return(await this._fetchFromWorker({target:"getEvents"})).events??[]}styleOverride(J){this.sendMessage("styleOverride",{style:J})}disableStyleOverride(){this.sendMessage("disableStyleOverride")}createStyle(J){this.sendMessage("createStyle",{style:J})}setStyle(J,K){this.sendMessage("setStyle",{style:J,index:K})}removeStyle(J){this.sendMessage("removeStyle",{index:J})}async getStyles(){return(await this._fetchFromWorker({target:"getStyles"})).styles??[]}addFont(J){this.sendMessage("addFont",{font:J})}setDefaultFont(J){this.sendMessage("defaultFont",{font:J})}async getStats(){let K=(await this._fetchFromWorker({target:"getStats"})).stats;return{framesRendered:K.framesRendered??0,framesDropped:K.framesDropped??0,avgRenderTime:K.avgRenderTime??0,maxRenderTime:K.maxRenderTime??0,minRenderTime:K.minRenderTime??0,lastRenderTime:K.lastRenderTime??0,pendingRenders:K.pendingRenders??0,totalEvents:K.totalEvents??0,cacheHits:K.cacheHits??0,cacheMisses:K.cacheMisses??0,renderFps:K.avgRenderTime&&K.avgRenderTime>0?Math.round(1000/K.avgRenderTime):0,usingWorker:!0,offscreenRender:this._offscreenRender,onDemandRender:this._onDemandRender}}async resetStats(){await this._fetchFromWorker({target:"resetStats"})}async getEventCount(){return(await this._fetchFromWorker({target:"getEventCount"})).count}async getStyleCount(){return(await this._fetchFromWorker({target:"getStyleCount"})).count}_sendLocalFont(J){try{globalThis.queryLocalFonts().then((K)=>{let Z=K?.find((N)=>N.fullName.toLowerCase()===J);if(Z)Z.blob().then((N)=>{N.arrayBuffer().then((Q)=>{this.addFont(new Uint8Array(Q))})})})}catch(K){console.warn("Local fonts API:",K)}}_getLocalFont(J){try{if(navigator?.permissions?.query)navigator.permissions.query({name:"local-fonts"}).then((K)=>{if(K.state==="granted")this._sendLocalFont(J.font)});else this._sendLocalFont(J.font)}catch(K){console.warn("Local fonts API:",K)}}_unbusy(){if(this._pendingDemandTimes.length>0){if(this._pendingDemandTimes.length>1){let K=this._pendingDemandTimes[this._pendingDemandTimes.length-1];this._pendingDemandTimes.length=0,this._pendingDemandTimes.push(K)}let J=this._pendingDemandTimes.shift();if(J){this._demandRender(J);return}}this.busy=!1}_enqueueDemand(J){let K=this._pendingDemandTimes;if(K.length>0){let Z=K[K.length-1];if(Math.abs(Z.mediaTime-J.mediaTime)>0.25)K.length=0}if(K.length>=R.MAX_PENDING_DEMANDS)K.shift();K.push(J)}_handleRVFC(J,K){if(this._destroyed)return;let Z=this._video?.playbackRate??1,Q={mediaTime:K.mediaTime+this.renderAhead*Z,width:K.width,height:K.height};if(!this._workerReady){this._enqueueDemand(Q),this._video.requestVideoFrameCallback(this._boundHandleRVFC);return}if(this.busy)this._enqueueDemand(Q);else this.busy=!0,this._demandRender(Q);this._video.requestVideoFrameCallback(this._boundHandleRVFC)}_demandRender(J){if(J.width!==this._videoWidth||J.height!==this._videoHeight)this._videoWidth=J.width,this._videoHeight=J.height,this.resize();this.sendMessage("demand",{time:J.mediaTime+this.timeOffset})}_detachOffscreen(){if(!this._offscreenRender||this._ctx)return;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas,this._ctx=this._canvasctrl.getContext("2d"),this.sendMessage("detachOffscreen"),this.busy=!1,this._pendingDemandTimes.length=0,this.resize(0,0,0,0,!0)}_reAttachOffscreen(){if(!this._offscreenRender||!this._ctx)return;this._canvas.remove(),this._createCanvas(),this._canvasctrl=this._canvas.transferControlToOffscreen(),this._ctx=!1,this.sendMessage("offscreenCanvas",{},[this._canvasctrl]),this.resize(0,0,0,0,!0)}_updateColorSpace(){this._video.requestVideoFrameCallback(()=>{try{let J=new globalThis.VideoFrame(this._video);this._videoColorSpace=u[J.colorSpace.matrix]??null,J.close(),this.sendMessage("getColorSpace")}catch(J){console.warn(J)}})}_verifyColorSpace(J){let{subtitleColorSpace:K,videoColorSpace:Z=this._videoColorSpace}=J;if(!K||!Z)return;if(K===Z)return;this._detachOffscreen();let N=y[K]?.[Z];if(N&&this._ctx)this._ctx.filter=`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${N} 0 0 0 0 0 1 0'/></filter></svg>#f")`}_render(J){this._unbusy();let{width:K,height:Z}=J;if(this.debug)J.times.IPCTime=Date.now()-(J.times.JSRenderTime||0);if(this._canvasctrl.width!==K||this._canvasctrl.height!==Z){if(this._canvasctrl.width=K,this._canvasctrl.height=Z,this._lastRenderWidth=K,this._lastRenderHeight=Z,this._gpuRenderer)this._gpuRenderer.updateSize(K,Z);this._verifyColorSpace({subtitleColorSpace:J.colorSpace})}if(this._gpuRenderer){this._renderGPU(J);return}if(!this._ctx)return;let Q=this._ctx,F=J.images,$=F.length;if(Q.clearRect(0,0,K,Z),J.asyncRender)for(let U=0;U<$;U++){let O=F[U];if(O.image)Q.drawImage(O.image,O.x,O.y),O.image.close()}else{let U=R._hasAlphaBug??!1;for(let O=0;O<$;O++){let E=F[O];if(E.image){let{w:q,h:L}=E,_=new Uint8ClampedArray(E.image),Y=m(_,U);Q.putImageData(new ImageData(Y,q,L),E.x,E.y)}}}if(this.debug){J.times.JSRenderTime=Date.now()-(J.times.JSRenderTime||0)-(J.times.IPCTime||0);let U=0,O=J.times.bitmaps||$;delete J.times.bitmaps;for(let E in J.times)U+=J.times[E]||0;console.log("Bitmaps: "+O+" Total: "+(U|0)+"ms",J.times)}}_renderGPU(J){let K=this._gpuRenderer;if(!K)return;if(J.images.length===0){K.clear();return}if(J.asyncRender){let Z=J.images.filter((N)=>N.image instanceof ImageBitmap).map((N)=>({image:N.image,x:N.x,y:N.y}));K.renderBitmaps(Z,this._canvasctrl.width,this._canvasctrl.height);for(let N of J.images)if(N.image instanceof ImageBitmap)N.image.close()}else K.render(J.images,this._canvasctrl.width,this._canvasctrl.height);if(this.debug){J.times.JSRenderTime=Date.now()-(J.times.JSRenderTime||0)-(J.times.IPCTime||0);let Z=0,N=J.times.bitmaps||J.images.length;delete J.times.bitmaps;for(let Q in J.times)Z+=J.times[Q]||0;console.log(`[${this._rendererType.toUpperCase()}] Bitmaps: `+N+" Total: "+(Z|0)+"ms",J.times)}}_ready(){if(this._workerReady=!0,this._init(),this._onDemandRender&&this._video){this.setCurrentTime(this._video.paused,this._video.currentTime+this.timeOffset,this._video.playbackRate);let J=this._pendingDemandTimes.length>0?this._pendingDemandTimes[this._pendingDemandTimes.length-1]:{mediaTime:this._video.currentTime+this.renderAhead*(this._video.playbackRate||1),width:this._video.videoWidth,height:this._video.videoHeight};this._pendingDemandTimes.length=0,this.busy=!0,this._demandRender(J)}this.dispatchEvent(new CustomEvent("ready"))}_partial_ready(){this.dispatchEvent(new CustomEvent("partial_ready"))}async sendMessage(J,K={},Z){if(await this._loaded,Z)this._worker.postMessage({target:J,transferable:Z,...K},[...Z]);else this._worker.postMessage({target:J,...K})}_fetchFromWorker(J){return new Promise((K,Z)=>{try{let N=J.target,Q=setTimeout(()=>{U(),Z(Error("Error: Timeout while trying to fetch "+N))},5000),F=(O)=>{if(O.data.target===N)U(),K(O.data)},$=(O)=>{U(),Z(O instanceof Error?O:O.error||Error("Worker error"))},U=()=>{this._worker.removeEventListener("message",F),this._worker.removeEventListener("error",$),clearTimeout(Q)};this._worker.addEventListener("message",F),this._worker.addEventListener("error",$),this._worker.postMessage(J)}catch(N){Z(N)}})}_console(J){console[J.command].apply(console,JSON.parse(J.content))}_onmessage(J){let K=J.data.target;if(K==="error"){this._error(J.data.error||"Unknown worker error");return}let Z=this["_"+K];if(Z)Z.call(this,J.data)}_error(J){let K=J instanceof Error?J:J instanceof ErrorEvent?J.error||Error(J.message):Error(String(J)),Z=J instanceof Event?new ErrorEvent(J.type,J):new ErrorEvent("error",{error:K});return this.dispatchEvent(Z),console.error(K),K}_removeListeners(){if(this._video){if(this._ro)this._ro.unobserve(this._video);if(this._ctx)this._ctx.filter="none";this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize),this._video.removeEventListener("loadedmetadata",this._boundUpdateColorSpace)}}destroy(J){let K=J?this._error(J):void 0;if(this._video&&this._canvasParent)this._video.parentNode?.removeChild(this._canvasParent);if(this._gpuRenderer)this._gpuRenderer.destroy(),this._gpuRenderer=null,this._rendererType="canvas2d";return this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker?.terminate(),K}}export{u as webYCbCrMap,l as testImageBugs,e as runFeatureTests,o as parseAss,g as libassYCbCrMap,f as isWebGPUSupported,p as isWebGL2Supported,c as getVideoPosition,s as getColorSpaceFilterUrl,KJ as getBitmapBug,JJ as getAlphaBug,t as fixPlayRes,m as fixAlpha,n as dropBlur,R as default,w as computeCanvasSize,y as colorMatrixConversionMap,v as WebGPURenderer,x as WebGL2Renderer,R as AkariSub};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "akarisub",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "libass Subtitle Renderer and Parser library for browsers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/ts/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/ts/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./worker": {
|
|
16
|
+
"import": "./dist/akarisub-worker.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/COPYRIGHT",
|
|
21
|
+
"dist/default.woff2",
|
|
22
|
+
"dist/akarisub*",
|
|
23
|
+
"dist/ts",
|
|
24
|
+
"dist/index.js",
|
|
25
|
+
"src/ts"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build:wasm": "make worker",
|
|
29
|
+
"build:ts": "tsgo",
|
|
30
|
+
"build:bundle": "bun build.ts",
|
|
31
|
+
"build": "bun run build:wasm && bun run build:ts && bun run build:bundle",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"format": "bun run prettier . --write"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/altqx/akarisub.git"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"libass",
|
|
41
|
+
"subtitle",
|
|
42
|
+
"wasm",
|
|
43
|
+
"emscripten"
|
|
44
|
+
],
|
|
45
|
+
"author": "altqx",
|
|
46
|
+
"contributors": [
|
|
47
|
+
"ThaUnknown",
|
|
48
|
+
"SubtitlesOctopus Contributors"
|
|
49
|
+
],
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/altqx/akarisub/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/altqx/akarisub",
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/bun": "1.3.12",
|
|
57
|
+
"@types/emscripten": "^1.41.5",
|
|
58
|
+
"@typescript/native-preview": "^7.0.0-dev.20260418.1",
|
|
59
|
+
"@webgpu/types": "^0.1.69",
|
|
60
|
+
"prettier": "^3.8.3",
|
|
61
|
+
"typescript": "^6.0.3"
|
|
62
|
+
}
|
|
63
|
+
}
|