akarisub 0.2.0 → 0.2.1
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 +3 -3
- package/dist/akarisub-worker.js +2 -2
- package/dist/akarisub-worker.wasm +0 -0
- package/dist/akarisub.umd.js +3 -3
- package/dist/index.js +3 -3
- package/dist/ts/index.d.ts +14 -0
- package/dist/ts/index.d.ts.map +1 -0
- package/dist/ts/index.js +17 -0
- package/dist/ts/index.js.map +1 -0
- package/dist/ts/ts/akarisub.d.ts +229 -0
- package/dist/ts/ts/akarisub.d.ts.map +1 -0
- package/dist/ts/ts/akarisub.js +1079 -0
- package/dist/ts/ts/akarisub.js.map +1 -0
- package/dist/ts/ts/types.d.ts +424 -0
- package/dist/ts/ts/types.d.ts.map +1 -0
- package/dist/ts/ts/types.js +5 -0
- package/dist/ts/ts/types.js.map +1 -0
- package/dist/ts/ts/utils.d.ts +78 -0
- package/dist/ts/ts/utils.d.ts.map +1 -0
- package/dist/ts/ts/utils.js +395 -0
- package/dist/ts/ts/utils.js.map +1 -0
- package/dist/ts/ts/webgl2-renderer.d.ts +51 -0
- package/dist/ts/ts/webgl2-renderer.d.ts.map +1 -0
- package/dist/ts/ts/webgl2-renderer.js +388 -0
- package/dist/ts/ts/webgl2-renderer.js.map +1 -0
- package/dist/ts/ts/webgpu-renderer.d.ts +64 -0
- package/dist/ts/ts/webgpu-renderer.d.ts.map +1 -0
- package/dist/ts/ts/webgpu-renderer.js +610 -0
- package/dist/ts/ts/webgpu-renderer.js.map +1 -0
- package/dist/ts/ts/worker.d.ts +6 -0
- package/dist/ts/ts/worker.d.ts.map +1 -0
- package/dist/ts/ts/worker.js +1695 -0
- package/dist/ts/ts/worker.js.map +1 -0
- package/dist/ts/wrapper.d.ts +8 -0
- package/dist/ts/wrapper.d.ts.map +1 -0
- package/dist/ts/wrapper.js +9 -0
- package/dist/ts/wrapper.js.map +1 -0
- package/package.json +7 -6
- package/src/ts/akarisub.ts +46 -4
- package/src/ts/types.ts +18 -4
- package/src/ts/worker.ts +177 -23
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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"}},s=[null,"BT601",null,"BT601","BT601","BT709","BT709","SMPTE240M","SMPTE240M","FCC","FCC"];function
|
|
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"}},s=[null,"BT601",null,"BT601","BT601","BT709","BT709","SMPTE240M","SMPTE240M","FCC","FCC"];function r(J,Z){if(!J||!Z)return null;if(J===Z)return null;let Q=y[J]?.[Z];if(!Q)return null;return`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix type='matrix' values='${Q} 0 0 0 0 0 1 0'/></filter></svg>#f")`}function w(J,Z,Q,$,O){let N=Q<=0?1:Q,K=globalThis.devicePixelRatio||1;if(Z<=0||J<=0)return{width:0,height:0};let F=N<1?-1:1,G=Z*K;if(F*G*N<=F*$)G*=N;else if(F*G<F*$)G=$;if(O>0&&G>O)G=O;return J*=G/Z,Z=G,{width:J,height:Z}}function c(J,Z=J.videoWidth,Q=J.videoHeight){let $=Z/Q,{offsetWidth:O,offsetHeight:N}=J,K=O/N,F=O,G=N;if(K>$)F=Math.floor(N*$);else G=Math.floor(O/$);let U=(O-F)/2,E=(N-G)/2;return{width:F,height:G,x:U,y:E}}function m(J,Z){if(!Z)return J;let Q=J.length,$=Q-Q%16,O=3;for(;O<$;O+=16){if(J[O]<2)J[O]=1;if(J[O+4]<2)J[O+4]=1;if(J[O+8]<2)J[O+8]=1;if(J[O+12]<2)J[O+12]=1}for(;O<Q;O+=4)if(J[O]<2)J[O]=1;return J}function o(J,Z=!1){let Q=[],$=J.split(/[\r\n]+/g),O=$.length,N=null,K=null;for(let F=0;F<O;F++){let G=$[F];if(!G||/^\s*$/.test(G))continue;let U=G[0];if(U==="["){let E=G.match(/^\[(.*)\]$/);if(E){if(Z&&E[1].toLowerCase()==="events")break;N=null,K={name:E[1],body:[]},Q.push(K);continue}}if(!K)continue;if(U===";")K.body.push({type:"comment",value:G.substring(1)});else{let E=G.indexOf(":");if(E===-1)continue;let T=G.substring(0,E),q=G.substring(E+1).trim();if(N||T==="Format"){let Y=q.split(",");if(N&&Y.length>N.length){let B=Y.slice(N.length-1).join(",");Y=Y.slice(0,N.length-1),Y.push(B)}let L=Y.length;for(let B=0;B<L;B++)Y[B]=Y[B].trim();if(N){let B={},j=Math.min(N.length,L);for(let P=0;P<j;P++)B[N[P]]=Y[P];q=B}else q=Y}if(T==="Format")N=q;K.body.push({key:T,value:q})}}return Q}var n=/\\blur(?:[0-9]+\.)?[0-9]+/gm;function i(J){return J.replace(n,"")}var a=[{w:7680,h:4320},{w:3840,h:2160},{w:2560,h:1440},{w:1920,h:1080},{w:1280,h:720}];function t(J,Z){let Q=[...a].sort(($,O)=>$.w-O.w);for(let $ of Q)if(J<=$.w&&Z<=$.h)return $;return{w:Math.ceil(J/100)*100,h:Math.ceil(Z/100)*100}}function k(J,Z){return Z&&Z.includes(".")?J.toFixed(2).replace(/\.?0+$/,""):Math.round(J)}function e(J){let Z=J.match(/PlayResX:\s*(\d+)/i),Q=J.match(/PlayResY:\s*(\d+)/i),$=Z?parseInt(Z[1],10):1920,O=Q?parseInt(Q[1],10):1080,N=/\\pos\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,K=/\\move\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)/g,F=/\\org\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,G=/\\i?clip\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,U=0,E=0,T=(M,X,V)=>{let H,D=new RegExp(M.source,"g");while((H=D.exec(J))!==null){for(let A of X)if(H[A]){let C=Math.abs(parseFloat(H[A]));if(C>U)U=C}for(let A of V)if(H[A]){let C=Math.abs(parseFloat(H[A]));if(C>E)E=C}}};if(T(N,[1],[2]),T(K,[1,3],[2,4]),T(F,[1],[2]),T(G,[1,3],[2,4]),U<=$&&E<=O)return J;let q=t(U,E),Y=$/q.w,L=O/q.h,B=Math.min(Y,L),j=Math.max(Y,L),P=1,z=J,W=z.match(/(\[Events\][\s\S]*)/i);if(!W)return z;let _=W[1];return _=_.replace(N,(M,X,V)=>`\\pos(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*L,V)})`),_=_.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,H,D,A,C)=>{let h=`\\move(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*L,V)},${k(parseFloat(H)*Y,H)},${k(parseFloat(D)*L,D)}`;return A?`${h},${A},${C})`:`${h})`}),_=_.replace(F,(M,X,V)=>`\\org(${k(parseFloat(X)*Y,X)},${k(parseFloat(V)*L,V)})`),_=_.replace(/\\(i?clip)\s*\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)/g,(M,X,V,H,D,A)=>`\\${X}(${k(parseFloat(V)*Y,V)},${k(parseFloat(H)*L,H)},${k(parseFloat(D)*Y,D)},${k(parseFloat(A)*L,A)})`),_=_.replace(/\\fs([\d.]+)/g,(M,X)=>`\\fs${k(parseFloat(X)*j,X)}`),_=_.replace(/\\fscx([\d.]+)/g,(M,X)=>`\\fscx${k(parseFloat(X)*P,X)}`),_=_.replace(/\\xbord([\d.]+)/g,(M,X)=>`\\xbord${k(parseFloat(X)*Y,X)}`),_=_.replace(/\\ybord([\d.]+)/g,(M,X)=>`\\ybord${k(parseFloat(X)*L,X)}`),_=_.replace(/\\xshad(-?[\d.]+)/g,(M,X)=>`\\xshad${k(parseFloat(X)*Y,X)}`),_=_.replace(/\\yshad(-?[\d.]+)/g,(M,X)=>`\\yshad${k(parseFloat(X)*L,X)}`),["fsp","bord","shad","be","blur"].forEach((M)=>{let X=new RegExp(`\\\\${M}(-?[\\d.]+)`,"g");_=_.replace(X,(V,H)=>`\\${M}${k(parseFloat(H)*B,H)}`)}),_=_.replace(/(\\i?clip\s*\([^,)]+m[^)]+\)|\\p[1-9][^}]*?)(?=[\\}]|$)/g,(M)=>{return M.replace(/(-?[\d.]+)\s+(-?[\d.]+)/g,(X,V,H)=>{return`${k(parseFloat(V)*Y,V)} ${k(parseFloat(H)*L,H)}`})}),z.substring(0,W.index)+_}var b=null,S=null;async function l(){if(b!==null&&S!==null)return{hasAlphaBug:b,hasBitmapBug:S};let J=document.createElement("canvas"),Z=J.getContext("2d",{willReadFrequently:!0});if(!Z)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 Q=document.createElement("canvas"),$=Q.getContext("2d",{willReadFrequently:!0});if(!$)throw Error("Canvas rendering not supported");J.width=Q.width=1,J.height=Q.height=1,Z.clearRect(0,0,1,1),$.clearRect(0,0,1,1);let O=$.getImageData(0,0,1,1).data;Z.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),$.drawImage(J,0,0);let N=$.getImageData(0,0,1,1).data;if(b=O[1]!==N[1],b)console.log("Detected a browser having issue with transparent pixels, applying workaround");if(typeof createImageBitmap<"u"){let K=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);$.drawImage(await createImageBitmap(new ImageData(K,1)),0,0);let{data:F}=$.getImageData(0,0,1,1);S=!1;for(let G=0;G<F.length;G++)if(Math.abs(K[G]-F[G])>15){S=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else S=!1;return J.remove(),Q.remove(),{hasAlphaBug:b,hasBitmapBug:S}}async function JJ(){return l()}function ZJ(){return b}function $J(){return S}function f(){return typeof navigator<"u"&&"gpu"in navigator}function QJ(J){if(J instanceof ArrayBuffer)return new Uint8Array(J);return new Uint8Array(J.buffer,J.byteOffset,J.byteLength)}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 Z=this.device.createShaderModule({code:`
|
|
2
2
|
struct VertexOutput {
|
|
3
3
|
@builtin(position) position: vec4f,
|
|
4
4
|
@location(0) @interpolate(flat) instanceIndex: u32,
|
|
@@ -91,7 +91,7 @@ fn fragmentMain(input: FragmentInput) -> @location(0) vec4f {
|
|
|
91
91
|
// Premultiplied alpha output
|
|
92
92
|
return vec4f(color.rgb * color.a, color.a);
|
|
93
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 $=this.device.createPipelineLayout({bindGroupLayouts:[this.bindGroupLayout]});this.pipeline=this.device.createRenderPipeline({layout:$,vertex:{module:Z,entryPoint:"vertexMain"},fragment:{module:Q,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,Z,Q){if(this.textureArray)this.pendingDestroyTextures.push(this.textureArray);let $=this.nextPowerOf2(Math.max(J,64)),
|
|
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 $=this.device.createPipelineLayout({bindGroupLayouts:[this.bindGroupLayout]});this.pipeline=this.device.createRenderPipeline({layout:$,vertex:{module:Z,entryPoint:"vertexMain"},fragment:{module:Q,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,Z,Q){if(this.textureArray)this.pendingDestroyTextures.push(this.textureArray);let $=this.nextPowerOf2(Math.max(J,64)),O=this.nextPowerOf2(Math.max(Z,64)),N=Math.min(this.nextPowerOf2(Math.max(Q,16)),256);this.textureArray=this.device.createTexture({size:[$,O,N],format:this.format,usage:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT}),this.textureArrayView=this.textureArray.createView({dimension:"2d-array"}),this.textureArrayWidth=$,this.textureArrayHeight=O,this.textureArraySize=N,this.bindGroupDirty=!0;let K=this.device.createCommandEncoder();for(let F=0;F<N;F++){let G=this.textureArray.createView({dimension:"2d",baseArrayLayer:F,arrayLayerCount:1});K.beginRenderPass({colorAttachments:[{view:G,clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}]}).end()}this.device.queue.submit([K.finish()])}ensureTextureArray(J,Z,Q){let $=Math.min(Q,256);if(J<=this.textureArrayWidth&&Z<=this.textureArrayHeight&&$<=this.textureArraySize)return!1;let O=this.nextPowerOf2(Math.max(this.textureArrayWidth,J)),N=this.nextPowerOf2(Math.max(this.textureArrayHeight,Z)),K=Math.min(this.nextPowerOf2(Math.max(this.textureArraySize,$,$+16)),256);return this.createTextureArray(O,N,K),!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,Z,Q){if(await this.init(),!this.device)throw Error("WebGPU device not initialized");if(Z<=0||Q<=0)return;if(this._canvas=J,J.width=Z,J.height=Q,!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]=Z,this.resolutionArray[1]=Q,this.device.queue.writeBuffer(this.uniformBuffer,0,this.resolutionArray),this.lastCanvasWidth=Z,this.lastCanvasHeight=Q}updateSize(J,Z){if(!this.device||!this._canvas||J<=0||Z<=0)return;if(J===this.lastCanvasWidth&&Z===this.lastCanvasHeight)return;this._canvas.width=J,this._canvas.height=Z,this.resolutionArray[0]=J,this.resolutionArray[1]=Z,this.device.queue.writeBuffer(this.uniformBuffer,0,this.resolutionArray),this.lastCanvasWidth=J,this.lastCanvasHeight=Z}renderBitmaps(J,Z,Q){if(!this.device||!this.context||!this.pipeline)return;let $=J.length;if($===0){this.clear();return}let O=this.context.getCurrentTexture();if(O.width===0||O.height===0)return;let N=0,K=0,F=0;for(let j=0;j<$;j++){let{image:P}=J[j],z=P.width,W=P.height;if(z>0&&W>0){if(z>N)N=z;if(W>K)K=W;F++}}if(F===0){this.clear();return}let G=Math.min(F,256);this.ensureTextureArray(N,K,G),this.updateBindGroup();let U=this.device,E=U.queue,T=this.textureArray,q=this.imageDataArray,Y=O.createView(),L=0,B=!0;while(L<$){let j=0;while(L<$&&j<256){let W=J[L++],_=W.image,I=_.width,M=_.height;if(I<=0||M<=0)continue;E.copyExternalImageToTexture({source:_,flipY:!1},{texture:T,origin:[0,0,j],premultipliedAlpha:!1},{width:I,height:M});let X=j<<3;q[X]=W.x,q[X+1]=W.y,q[X+2]=I,q[X+3]=M,q[X+4]=I,q[X+5]=M,q[X+6]=j,q[X+7]=0,j++}if(j===0)continue;E.writeBuffer(this.imageDataBuffer,0,q.buffer,0,j<<5);let P=U.createCommandEncoder(),z=P.beginRenderPass({colorAttachments:[{view:Y,clearValue:{r:0,g:0,b:0,a:0},loadOp:B?"clear":"load",storeOp:"store"}]});z.setPipeline(this.pipeline),z.setBindGroup(0,this.bindGroup),z.draw(6,j),z.end(),E.submit([P.finish()]),B=!1}this.cleanupPendingTextures()}render(J,Z,Q){if(!this.device||!this.context||!this.pipeline)return;let $=J.length;if($===0){this.clear();return}let O=this.context.getCurrentTexture();if(O.width===0||O.height===0)return;let N=0,K=0,F=0;for(let P=0;P<$;P++){let{w:z,h:W}=J[P];if(z>0&&W>0){if(z>N)N=z;if(W>K)K=W;F++}}if(F===0){this.clear();return}let G=Math.min(F,256);this.ensureTextureArray(N,K,G),this.updateBindGroup();let U=this.device,E=U.queue,T=this.textureArray,q=this.imageDataArray,Y=this.format==="bgra8unorm",L=O.createView(),B=0,j=!0;while(B<$){let P=0;while(B<$&&P<256){let _=J[B++],I=_.w,M=_.h;if(I<=0||M<=0)continue;let X=_.image;if(X instanceof ImageBitmap)E.copyExternalImageToTexture({source:X,flipY:!1},{texture:T,origin:[0,0,P],premultipliedAlpha:!1},{width:I,height:M});else if(X instanceof ArrayBuffer||X instanceof Uint8Array||X instanceof Uint8ClampedArray)this.uploadTextureData(P,X,I,M,Y);let V=P<<3;q[V]=_.x,q[V+1]=_.y,q[V+2]=I,q[V+3]=M,q[V+4]=I,q[V+5]=M,q[V+6]=P,q[V+7]=0,P++}if(P===0)continue;E.writeBuffer(this.imageDataBuffer,0,q.buffer,0,P<<5);let z=U.createCommandEncoder(),W=z.beginRenderPass({colorAttachments:[{view:L,clearValue:{r:0,g:0,b:0,a:0},loadOp:j?"clear":"load",storeOp:"store"}]});W.setPipeline(this.pipeline),W.setBindGroup(0,this.bindGroup),W.draw(6,P),W.end(),E.submit([z.finish()]),j=!1}this.cleanupPendingTextures()}uploadTextureData(J,Z,Q,$,O){let N=Q*$*4,K=QJ(Z);if(O){let F=this.ensureConversionBuffer(N);for(let G=0;G<N;G+=4)F[G]=K[G+2],F[G+1]=K[G+1],F[G+2]=K[G],F[G+3]=K[G+3];this.device.queue.writeTexture({texture:this.textureArray,origin:[0,0,J]},F.buffer,{bytesPerRow:Q*4},{width:Q,height:$})}else this.device.queue.writeTexture({texture:this.textureArray,origin:[0,0,J]},K,{bytesPerRow:Q*4},{width:Q,height:$})}cleanupPendingTextures(){let J=this.pendingDestroyTextures,Z=J.length;if(Z===0)return;for(let Q=0;Q<Z;Q++)J[Q].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 Z=this.device.createCommandEncoder();Z.beginRenderPass({colorAttachments:[{view:J.createView(),clearValue:{r:0,g:0,b:0,a:0},loadOp:"clear",storeOp:"store"}]}).end(),this.device.queue.submit([Z.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 KJ(J){return J instanceof Uint8Array||J instanceof Uint8ClampedArray}function g(J,Z,Q){let $=J.createShader(Z);if(J.shaderSource($,Q),J.compileShader($),!J.getShaderParameter($,J.COMPILE_STATUS)){let O=J.getShaderInfoLog($);throw J.deleteShader($),Error(`WebGL2 shader compilation failed: ${O}`)}return $}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 Z=g(J,J.VERTEX_SHADER,`#version 300 es
|
|
95
95
|
precision highp float;
|
|
96
96
|
|
|
97
97
|
in vec4 a_destRect;
|
|
@@ -144,4 +144,4 @@ void main() {
|
|
|
144
144
|
// Premultiplied alpha output (matches WebGPU renderer behaviour)
|
|
145
145
|
fragColor = vec4(color.rgb * color.a, color.a);
|
|
146
146
|
}
|
|
147
|
-
`),$=J.createProgram();if(J.attachShader($,Z),J.attachShader($,Q),J.linkProgram($),!J.getProgramParameter($,J.LINK_STATUS))throw Error(`WebGL2 program link failed: ${J.getProgramInfoLog($)}`);J.deleteShader(Z),J.deleteShader(Q),this._program=$,this._resolutionLoc=J.getUniformLocation($,"u_resolution"),this._texArraySizeLoc=J.getUniformLocation($,"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 N=J.getAttribLocation($,"a_destRect");J.enableVertexAttribArray(N),J.vertexAttribPointer(N,4,J.FLOAT,!1,32,0),J.vertexAttribDivisor(N,1);let O=J.getAttribLocation($,"a_texInfo");J.enableVertexAttribArray(O),J.vertexAttribPointer(O,4,J.FLOAT,!1,32,16),J.vertexAttribDivisor(O,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,Z,Q){let $=this._gl,N=this._nextPow2(Math.max(J,64)),O=this._nextPow2(Math.max(Z,64)),K=Math.min(this._nextPow2(Math.max(Q,16)),256);$.bindTexture($.TEXTURE_2D_ARRAY,this._texArray),$.texImage3D($.TEXTURE_2D_ARRAY,0,$.RGBA8,N,O,K,0,$.RGBA,$.UNSIGNED_BYTE,null),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_MIN_FILTER,$.NEAREST),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_MAG_FILTER,$.NEAREST),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_WRAP_S,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_WRAP_T,$.CLAMP_TO_EDGE),this._texWidth=N,this._texHeight=O,this._texLayers=K}_ensureTextureArray(J,Z,Q){let $=Math.min(Q,256);if(J<=this._texWidth&&Z<=this._texHeight&&$<=this._texLayers)return;let N=this._nextPow2(Math.max(this._texWidth,J)),O=this._nextPow2(Math.max(this._texHeight,Z)),K=Math.min(this._nextPow2(Math.max(this._texLayers,$,$+16)),256);this._allocateTextureArray(N,O,K)}async setCanvas(J,Z,Q){if(await this.init(),Z<=0||Q<=0)return;this._canvas=J,J.width=Z,J.height=Q,this._initGL(),this._gl.viewport(0,0,Z,Q),this._lastCanvasWidth=Z,this._lastCanvasHeight=Q}updateSize(J,Z){if(!this._gl||!this._canvas||J<=0||Z<=0)return;if(J===this._lastCanvasWidth&&Z===this._lastCanvasHeight)return;this._canvas.width=J,this._canvas.height=Z,this._gl.viewport(0,0,J,Z),this._lastCanvasWidth=J,this._lastCanvasHeight=Z}renderBitmaps(J,Z,Q){if(!this._gl||!this._initialized)return;let $=J.length;if($===0){this.clear();return}let N=0,O=0;for(let U=0;U<$;U++){let{image:E}=J[U];if(E.width>N)N=E.width;if(E.height>O)O=E.height}this._ensureTextureArray(N,O,Math.min($,256));let K=this._gl;K.clearColor(0,0,0,0),K.clear(K.COLOR_BUFFER_BIT),K.useProgram(this._program),K.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),K.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),K.activeTexture(K.TEXTURE0),K.bindTexture(K.TEXTURE_2D_ARRAY,this._texArray),K.pixelStorei(K.UNPACK_FLIP_Y_WEBGL,!1);let F=this._instanceData,G=0;while(G<$){let U=0;while(G<$&&U<256){let E=J[G++],M=E.image.width,q=E.image.height;if(M<=0||q<=0)continue;K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,M,q,1,K.RGBA,K.UNSIGNED_BYTE,E.image);let Y=U<<3;F[Y]=E.x,F[Y+1]=E.y,F[Y+2]=M,F[Y+3]=q,F[Y+4]=M,F[Y+5]=q,F[Y+6]=U,F[Y+7]=0,U++}if(U===0)continue;K.bindBuffer(K.ARRAY_BUFFER,this._instanceBuffer),K.bufferSubData(K.ARRAY_BUFFER,0,F,0,U<<3),K.bindVertexArray(this._vao),K.drawArraysInstanced(K.TRIANGLES,0,6,U),K.bindVertexArray(null)}}render(J,Z,Q){if(!this._gl||!this._initialized)return;let $=J.length;if($===0){this.clear();return}let N=0,O=0;for(let U=0;U<$;U++){let{w:E,h:M}=J[U];if(E>N)N=E;if(M>O)O=M}this._ensureTextureArray(N,O,Math.min($,256));let K=this._gl;K.clearColor(0,0,0,0),K.clear(K.COLOR_BUFFER_BIT),K.useProgram(this._program),K.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),K.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),K.activeTexture(K.TEXTURE0),K.bindTexture(K.TEXTURE_2D_ARRAY,this._texArray),K.pixelStorei(K.UNPACK_FLIP_Y_WEBGL,!1);let F=this._instanceData,G=0;while(G<$){let U=0;while(G<$&&U<256){let E=J[G++],M=E.w,q=E.h;if(M<=0||q<=0)continue;let Y=E.image;if(Y instanceof ImageBitmap)K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,M,q,1,K.RGBA,K.UNSIGNED_BYTE,Y);else if(Y instanceof ArrayBuffer||KJ(Y)){let B=Y instanceof ArrayBuffer?new Uint8Array(Y):Y;K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,M,q,1,K.RGBA,K.UNSIGNED_BYTE,B)}let L=U<<3;F[L]=E.x,F[L+1]=E.y,F[L+2]=M,F[L+3]=q,F[L+4]=M,F[L+5]=q,F[L+6]=U,F[L+7]=0,U++}if(U===0)continue;K.bindBuffer(K.ARRAY_BUFFER,this._instanceBuffer),K.bufferSubData(K.ARRAY_BUFFER,0,F,0,U<<3),K.bindVertexArray(this._vao),K.drawArraysInstanced(K.TRIANGLES,0,6,U),K.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}}var d=0.008,NJ=d*1000,OJ=16,GJ=()=>{if(typeof navigator>"u")return!1;let J=navigator.userAgent||"",Z=navigator.vendor||"",Q=/\b(iPhone|iPad|iPod)\b/i.test(J);if(!/AppleWebKit/i.test(J))return!1;if(Q)return!0;if(/\b(Chrome|Chromium|Edg|OPR|SamsungBrowser|Firefox)\b/i.test(J))return!1;return Z.includes("Apple")};class H 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=[];_isLikelyWebKit;_activeDemandStartedAt=0;_smoothedDemandLatencyMs;_boundResize;_boundTimeUpdate;_boundSetRate;_boundUpdateColorSpace;_boundHandleRVFC;_gpuRenderer=null;_rendererType="canvas2d";_onCanvasFallback;_lastRenderWidth=0;_lastRenderHeight=0;_gpuBitmapImages=[];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((O)=>{this._init=O}),this._isLikelyWebKit=GJ(),this._smoothedDemandLatencyMs=this._isLikelyWebKit?OJ:NJ;let Z=H._test();this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(J.onDemandRender??!0),this._onCanvasFallback=J.onCanvasFallback;let Q=!this._isLikelyWebKit&&!J.canvas&&(f()||p()),$=typeof createImageBitmap<"u"&&(J.asyncRender??!this._isLikelyWebKit);if(this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!J.canvas&&!Q&&(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,Q)this._initGPURenderer();else if(!this._offscreenRender)this._ctx=this._canvas.getContext("2d",{alpha:!0,desynchronized:!0});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??d,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=(O)=>this._onmessage(O),this._worker.onerror=(O)=>this._error(O),Z.then(()=>{if(this._worker.postMessage({target:"init",wasmUrl:J.wasmUrl??"akarisub-worker.wasm",asyncRender:$,fullTrackWarmup:J.fullTrackWarmup??!1,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:H._hasBitmapBug}),this._offscreenRender)this.sendMessage("offscreenCanvas",{},[this._canvasctrl])})}static async _testImageBugs(){if(H._hasBitmapBug!==null)return;let J=document.createElement("canvas"),Z=J.getContext("2d",{willReadFrequently:!0});if(!Z)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 Q=document.createElement("canvas"),$=Q.getContext("2d",{willReadFrequently:!0});if(!$)throw Error("Canvas rendering not supported");J.width=Q.width=1,J.height=Q.height=1,Z.clearRect(0,0,1,1),$.clearRect(0,0,1,1);let N=$.getImageData(0,0,1,1).data;Z.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),$.drawImage(J,0,0);let O=$.getImageData(0,0,1,1).data;if(H._hasAlphaBug=N[1]!==O[1],H._hasAlphaBug)console.log("Detected a browser having issue with transparent pixels, applying workaround");if(typeof createImageBitmap<"u"){let K=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);$.drawImage(await createImageBitmap(new ImageData(K,1)),0,0);let{data:F}=$.getImageData(0,0,1,1);H._hasBitmapBug=!1;for(let G=0;G<F.length;G++)if(Math.abs(K[G]-F[G])>15){H._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else H._hasBitmapBug=!1;J.remove(),Q.remove()}static async _test(){await H._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",{alpha:!0,desynchronized:!0});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,Z=0,Q=0,$=0,N=this._video?.paused??!1){if((!J||!Z)&&this._video){let O=c(this._video),K;if(this._videoWidth){let F=this._video.videoWidth/this._videoWidth,G=this._video.videoHeight/this._videoHeight;K=w((O.width||0)/F,(O.height||0)/G,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight)}else K=w(O.width||0,O.height||0,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight);if(J=K.width,Z=K.height,this._canvasParent)Q=O.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),$=O.x;this._canvas.style.width=O.width+"px",this._canvas.style.height=O.height+"px"}if(this._canvas.style.top=Q+"px",this._canvas.style.left=$+"px",J>0&&Z>0)this._canvasctrl.width=J,this._canvasctrl.height=Z;if(this._gpuRenderer&&J>0&&Z>0)this._gpuRenderer.updateSize(J,Z);if(N&&this.busy===!1)this.busy=!0;else N=!1;this.sendMessage("canvas",{width:J,height:Z,videoWidth:this._videoWidth||this._video?.videoWidth||0,videoHeight:this._videoHeight||this._video?.videoHeight||0,force:N})}_timeupdate(J){let Q={seeking:!0,waiting:!0,playing:!1}[J.type];if(Q!=null)this._playstate=Q;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,Z,Q){this.sendMessage("video",{isPaused:J,currentTime:Z,rate:Q,colorSpace:this._videoColorSpace})}createEvent(J){this.sendMessage("createEvent",{event:J})}setEvent(J,Z){this.sendMessage("setEvent",{event:J,index:Z})}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,Z){this.sendMessage("setStyle",{style:J,index:Z})}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 Z=(await this._fetchFromWorker({target:"getStats"})).stats;return{framesRendered:Z.framesRendered??0,framesDropped:Z.framesDropped??0,avgRenderTime:Z.avgRenderTime??0,maxRenderTime:Z.maxRenderTime??0,minRenderTime:Z.minRenderTime??0,lastRenderTime:Z.lastRenderTime??0,pendingRenders:Z.pendingRenders??0,totalEvents:Z.totalEvents??0,cacheHits:Z.cacheHits??0,cacheMisses:Z.cacheMisses??0,renderFps:Z.avgRenderTime&&Z.avgRenderTime>0?Math.round(1000/Z.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((Z)=>{let Q=Z?.find(($)=>$.fullName.toLowerCase()===J);if(Q)Q.blob().then(($)=>{$.arrayBuffer().then((N)=>{this.addFont(new Uint8Array(N))})})})}catch(Z){console.warn("Local fonts API:",Z)}}_getLocalFont(J){try{if(navigator?.permissions?.query)navigator.permissions.query({name:"local-fonts"}).then((Z)=>{if(Z.state==="granted")this._sendLocalFont(J.font)});else this._sendLocalFont(J.font)}catch(Z){console.warn("Local fonts API:",Z)}}_unbusy(){if(this._observeDemandCompletion(),this._pendingDemandTimes.length>0){if(this._pendingDemandTimes.length>1){let Z=this._pendingDemandTimes[this._pendingDemandTimes.length-1];this._pendingDemandTimes.length=0,this._pendingDemandTimes.push(Z)}let J=this._pendingDemandTimes.shift();if(J){this._demandRender(J);return}}this.busy=!1}_markDemandDispatched(){if(!this._onDemandRender)return;this._activeDemandStartedAt=performance.now()}_observeDemandCompletion(){if(!this._onDemandRender||this._activeDemandStartedAt===0)return;let J=performance.now()-this._activeDemandStartedAt;if(this._activeDemandStartedAt=0,!Number.isFinite(J)||J<=0)return;this._smoothedDemandLatencyMs=this._smoothedDemandLatencyMs<=0?J:this._smoothedDemandLatencyMs*0.75+J*0.25}_getDemandPipelineLeadSeconds(J,Z){let Q=Z.expectedDisplayTime??Z.presentationTime??J,$=Math.max(0,Q-J)/1000;return Math.max(0,this._smoothedDemandLatencyMs/1000-$)}_enqueueDemand(J){let Z=this._pendingDemandTimes;if(Z.length>0){let Q=Z[Z.length-1];if(Math.abs(Q.mediaTime-J.mediaTime)>0.25)Z.length=0}if(Z.length>=H.MAX_PENDING_DEMANDS)Z.shift();Z.push(J)}_handleRVFC(J,Z){if(this._destroyed)return;let Q=this._video?.playbackRate??1,$=this._getDemandPipelineLeadSeconds(J,Z),O={mediaTime:Z.mediaTime+($+this.renderAhead)*Q,width:Z.width,height:Z.height};if(!this._workerReady){this._enqueueDemand(O),this._video.requestVideoFrameCallback(this._boundHandleRVFC);return}if(this.busy)this._enqueueDemand(O);else this.busy=!0,this._demandRender(O);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._markDemandDispatched(),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",{alpha:!0,desynchronized:!0}),this.sendMessage("detachOffscreen"),this.busy=!1,this._activeDemandStartedAt=0,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:Z,videoColorSpace:Q=this._videoColorSpace}=J;if(!Z||!Q)return;if(Z===Q)return;this._detachOffscreen();let $=y[Z]?.[Q];if($&&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='${$} 0 0 0 0 0 1 0'/></filter></svg>#f")`}_render(J){try{let{width:Z,height:Q}=J;if(this.debug)J.times.IPCTime=Date.now()-(J.times.JSRenderTime||0);if(this._canvasctrl.width!==Z||this._canvasctrl.height!==Q){if(this._canvasctrl.width=Z,this._canvasctrl.height=Q,this._lastRenderWidth=Z,this._lastRenderHeight=Q,this._gpuRenderer)this._gpuRenderer.updateSize(Z,Q);this._verifyColorSpace({subtitleColorSpace:J.colorSpace})}if(this._gpuRenderer){this._renderGPU(J);return}if(!this._ctx)return;let N=this._ctx,O=J.images,K=O.length;if(N.clearRect(0,0,Z,Q),J.asyncRender)for(let F=0;F<K;F++){let G=O[F];if(G.image)N.drawImage(G.image,G.x,G.y),G.image.close()}else{let F=H._hasAlphaBug??!1;for(let G=0;G<K;G++){let U=O[G];if(U.image){let{w:E,h:M,image:q}=U,Y=q instanceof Uint8ClampedArray?q:q instanceof Uint8Array?new Uint8ClampedArray(q.buffer,q.byteOffset,q.byteLength):new Uint8ClampedArray(q),L=m(Y,F);N.putImageData(new ImageData(L,E,M),U.x,U.y)}}}if(this.debug){J.times.JSRenderTime=Date.now()-(J.times.JSRenderTime||0)-(J.times.IPCTime||0);let F=0,G=J.times.bitmaps||K;delete J.times.bitmaps;for(let U in J.times)F+=J.times[U]||0;console.log("Bitmaps: "+G+" Total: "+(F|0)+"ms",J.times)}}finally{this._unbusy()}}_renderGPU(J){let Z=this._gpuRenderer;if(!Z)return;if(J.images.length===0){Z.clear();return}if(J.asyncRender){let Q=this._gpuBitmapImages,$=0;for(let N=0;N<J.images.length;N++){let O=J.images[N];if(!(O.image instanceof ImageBitmap))continue;let K=Q[$]||(Q[$]={image:O.image,x:0,y:0});K.image=O.image,K.x=O.x,K.y=O.y,$++}Q.length=$,Z.renderBitmaps(Q,this._canvasctrl.width,this._canvasctrl.height);for(let N of J.images)if(N.image instanceof ImageBitmap)N.image.close()}else Z.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 Q=0,$=J.times.bitmaps||J.images.length;delete J.times.bitmaps;for(let N in J.times)Q+=J.times[N]||0;console.log(`[${this._rendererType.toUpperCase()}] Bitmaps: `+$+" Total: "+(Q|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,Z={},Q){if(await this._loaded,Q)this._worker.postMessage({target:J,transferable:Q,...Z},[...Q]);else this._worker.postMessage({target:J,...Z})}_fetchFromWorker(J){return new Promise((Z,Q)=>{try{let $=J.target,N=setTimeout(()=>{F(),Q(Error("Error: Timeout while trying to fetch "+$))},5000),O=(G)=>{if(G.data.target===$)F(),Z(G.data)},K=(G)=>{F(),Q(G instanceof Error?G:G.error||Error("Worker error"))},F=()=>{this._worker.removeEventListener("message",O),this._worker.removeEventListener("error",K),clearTimeout(N)};this._worker.addEventListener("message",O),this._worker.addEventListener("error",K),this._worker.postMessage(J)}catch($){Q($)}})}_console(J){console[J.command].apply(console,JSON.parse(J.content))}_onmessage(J){let Z=J.data.target;if(Z==="error"){this._error(J.data.error||"Unknown worker error");return}let Q=this["_"+Z];if(Q)Q.call(this,J.data)}_error(J){let Z=J instanceof Error?J:J instanceof ErrorEvent?J.error||Error(J.message):Error(String(J)),Q=J instanceof Event?new ErrorEvent(J.type,J):new ErrorEvent("error",{error:Z});return this.dispatchEvent(Q),console.error(Z),Z}_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 Z=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(),Z}}export{u as webYCbCrMap,l as testImageBugs,JJ as runFeatureTests,r as parseAss,s as libassYCbCrMap,f as isWebGPUSupported,p as isWebGL2Supported,c as getVideoPosition,o as getColorSpaceFilterUrl,$J as getBitmapBug,ZJ as getAlphaBug,e as fixPlayRes,m as fixAlpha,i as dropBlur,H as default,w as computeCanvasSize,y as colorMatrixConversionMap,v as WebGPURenderer,x as WebGL2Renderer,H as AkariSub};
|
|
147
|
+
`),$=J.createProgram();if(J.attachShader($,Z),J.attachShader($,Q),J.linkProgram($),!J.getProgramParameter($,J.LINK_STATUS))throw Error(`WebGL2 program link failed: ${J.getProgramInfoLog($)}`);J.deleteShader(Z),J.deleteShader(Q),this._program=$,this._resolutionLoc=J.getUniformLocation($,"u_resolution"),this._texArraySizeLoc=J.getUniformLocation($,"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 O=J.getAttribLocation($,"a_destRect");J.enableVertexAttribArray(O),J.vertexAttribPointer(O,4,J.FLOAT,!1,32,0),J.vertexAttribDivisor(O,1);let N=J.getAttribLocation($,"a_texInfo");J.enableVertexAttribArray(N),J.vertexAttribPointer(N,4,J.FLOAT,!1,32,16),J.vertexAttribDivisor(N,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,Z,Q){let $=this._gl,O=this._nextPow2(Math.max(J,64)),N=this._nextPow2(Math.max(Z,64)),K=Math.min(this._nextPow2(Math.max(Q,16)),256);$.bindTexture($.TEXTURE_2D_ARRAY,this._texArray),$.texImage3D($.TEXTURE_2D_ARRAY,0,$.RGBA8,O,N,K,0,$.RGBA,$.UNSIGNED_BYTE,null),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_MIN_FILTER,$.NEAREST),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_MAG_FILTER,$.NEAREST),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_WRAP_S,$.CLAMP_TO_EDGE),$.texParameteri($.TEXTURE_2D_ARRAY,$.TEXTURE_WRAP_T,$.CLAMP_TO_EDGE),this._texWidth=O,this._texHeight=N,this._texLayers=K}_ensureTextureArray(J,Z,Q){let $=Math.min(Q,256);if(J<=this._texWidth&&Z<=this._texHeight&&$<=this._texLayers)return;let O=this._nextPow2(Math.max(this._texWidth,J)),N=this._nextPow2(Math.max(this._texHeight,Z)),K=Math.min(this._nextPow2(Math.max(this._texLayers,$,$+16)),256);this._allocateTextureArray(O,N,K)}async setCanvas(J,Z,Q){if(await this.init(),Z<=0||Q<=0)return;this._canvas=J,J.width=Z,J.height=Q,this._initGL(),this._gl.viewport(0,0,Z,Q),this._lastCanvasWidth=Z,this._lastCanvasHeight=Q}updateSize(J,Z){if(!this._gl||!this._canvas||J<=0||Z<=0)return;if(J===this._lastCanvasWidth&&Z===this._lastCanvasHeight)return;this._canvas.width=J,this._canvas.height=Z,this._gl.viewport(0,0,J,Z),this._lastCanvasWidth=J,this._lastCanvasHeight=Z}renderBitmaps(J,Z,Q){if(!this._gl||!this._initialized)return;let $=J.length;if($===0){this.clear();return}let O=0,N=0;for(let U=0;U<$;U++){let{image:E}=J[U];if(E.width>O)O=E.width;if(E.height>N)N=E.height}this._ensureTextureArray(O,N,Math.min($,256));let K=this._gl;K.clearColor(0,0,0,0),K.clear(K.COLOR_BUFFER_BIT),K.useProgram(this._program),K.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),K.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),K.activeTexture(K.TEXTURE0),K.bindTexture(K.TEXTURE_2D_ARRAY,this._texArray),K.pixelStorei(K.UNPACK_FLIP_Y_WEBGL,!1);let F=this._instanceData,G=0;while(G<$){let U=0;while(G<$&&U<256){let E=J[G++],T=E.image.width,q=E.image.height;if(T<=0||q<=0)continue;K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,T,q,1,K.RGBA,K.UNSIGNED_BYTE,E.image);let Y=U<<3;F[Y]=E.x,F[Y+1]=E.y,F[Y+2]=T,F[Y+3]=q,F[Y+4]=T,F[Y+5]=q,F[Y+6]=U,F[Y+7]=0,U++}if(U===0)continue;K.bindBuffer(K.ARRAY_BUFFER,this._instanceBuffer),K.bufferSubData(K.ARRAY_BUFFER,0,F,0,U<<3),K.bindVertexArray(this._vao),K.drawArraysInstanced(K.TRIANGLES,0,6,U),K.bindVertexArray(null)}}render(J,Z,Q){if(!this._gl||!this._initialized)return;let $=J.length;if($===0){this.clear();return}let O=0,N=0;for(let U=0;U<$;U++){let{w:E,h:T}=J[U];if(E>O)O=E;if(T>N)N=T}this._ensureTextureArray(O,N,Math.min($,256));let K=this._gl;K.clearColor(0,0,0,0),K.clear(K.COLOR_BUFFER_BIT),K.useProgram(this._program),K.uniform2f(this._resolutionLoc,this._lastCanvasWidth,this._lastCanvasHeight),K.uniform2i(this._texArraySizeLoc,this._texWidth,this._texHeight),K.activeTexture(K.TEXTURE0),K.bindTexture(K.TEXTURE_2D_ARRAY,this._texArray),K.pixelStorei(K.UNPACK_FLIP_Y_WEBGL,!1);let F=this._instanceData,G=0;while(G<$){let U=0;while(G<$&&U<256){let E=J[G++],T=E.w,q=E.h;if(T<=0||q<=0)continue;let Y=E.image;if(Y instanceof ImageBitmap)K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,T,q,1,K.RGBA,K.UNSIGNED_BYTE,Y);else if(Y instanceof ArrayBuffer||KJ(Y)){let B=Y instanceof ArrayBuffer?new Uint8Array(Y):Y;K.texSubImage3D(K.TEXTURE_2D_ARRAY,0,0,0,U,T,q,1,K.RGBA,K.UNSIGNED_BYTE,B)}let L=U<<3;F[L]=E.x,F[L+1]=E.y,F[L+2]=T,F[L+3]=q,F[L+4]=T,F[L+5]=q,F[L+6]=U,F[L+7]=0,U++}if(U===0)continue;K.bindBuffer(K.ARRAY_BUFFER,this._instanceBuffer),K.bufferSubData(K.ARRAY_BUFFER,0,F,0,U<<3),K.bindVertexArray(this._vao),K.drawArraysInstanced(K.TRIANGLES,0,6,U),K.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}}var d=0.008,NJ=d*1000,OJ=16,GJ=()=>{if(typeof navigator>"u")return!1;let J=navigator.userAgent||"",Z=navigator.vendor||"",Q=/\b(iPhone|iPad|iPod)\b/i.test(J);if(!/AppleWebKit/i.test(J))return!1;if(Q)return!0;if(/\b(Chrome|Chromium|Edg|OPR|SamsungBrowser|Firefox)\b/i.test(J))return!1;return Z.includes("Apple")};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=[];_isLikelyWebKit;_activeDemandStartedAt=0;_smoothedDemandLatencyMs;_boundResize;_boundTimeUpdate;_boundSetRate;_boundUpdateColorSpace;_boundHandleRVFC;_gpuRenderer=null;_rendererType="canvas2d";_onCanvasFallback;_lastRenderWidth=0;_lastRenderHeight=0;_gpuBitmapImages=[];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((N)=>{this._init=N}),this._isLikelyWebKit=GJ(),this._smoothedDemandLatencyMs=this._isLikelyWebKit?OJ:NJ;let Z=R._test();this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&(J.onDemandRender??!0),this._onCanvasFallback=J.onCanvasFallback;let Q=!this._isLikelyWebKit&&!J.canvas&&(f()||p()),$=typeof createImageBitmap<"u"&&(J.asyncRender??!this._isLikelyWebKit);if(this._offscreenRender="transferControlToOffscreen"in HTMLCanvasElement.prototype&&!J.canvas&&!Q&&(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 O=this._bufferCanvas.getContext("2d");if(!O)throw this.destroy(Error("Canvas rendering not supported"));if(this._bufferCtx=O,Q)this._initGPURenderer();else if(!this._offscreenRender)this._ctx=this._canvas.getContext("2d",{alpha:!0,desynchronized:!0});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??d,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=(N)=>this._onmessage(N),this._worker.onerror=(N)=>this._error(N),Z.then(()=>{let N={target:"init",wasmUrl:J.wasmUrl??"akarisub-worker.wasm",asyncRender:$,fullTrackWarmup:J.fullTrackWarmup??!1,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,encryptedSubContent:J.encryptedSubContent||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};if(this._worker.postMessage(N,R._getSubtitleTransfers(J.subContent,J.encryptedSubContent)),this._offscreenRender)this.sendMessage("offscreenCanvas",{},[this._canvasctrl])})}static async _testImageBugs(){if(R._hasBitmapBug!==null)return;let J=document.createElement("canvas"),Z=J.getContext("2d",{willReadFrequently:!0});if(!Z)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 Q=document.createElement("canvas"),$=Q.getContext("2d",{willReadFrequently:!0});if(!$)throw Error("Canvas rendering not supported");J.width=Q.width=1,J.height=Q.height=1,Z.clearRect(0,0,1,1),$.clearRect(0,0,1,1);let O=$.getImageData(0,0,1,1).data;Z.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),$.drawImage(J,0,0);let N=$.getImageData(0,0,1,1).data;if(R._hasAlphaBug=O[1]!==N[1],R._hasAlphaBug)console.log("Detected a browser having issue with transparent pixels, applying workaround");if(typeof createImageBitmap<"u"){let K=new Uint8ClampedArray([255,0,255,0,255]).subarray(1,5);$.drawImage(await createImageBitmap(new ImageData(K,1)),0,0);let{data:F}=$.getImageData(0,0,1,1);R._hasBitmapBug=!1;for(let G=0;G<F.length;G++)if(Math.abs(K[G]-F[G])>15){R._hasBitmapBug=!0,console.log("Detected a browser having issue with partial bitmaps, applying workaround");break}}else R._hasBitmapBug=!1;J.remove(),Q.remove()}static async _test(){await R._testImageBugs()}static _getSubtitleTransfers(J,Z){let Q=[];if(J instanceof ArrayBuffer)Q.push(J);else if(J instanceof Uint8Array)Q.push(J.buffer);if(Z?.encrypted)Q.push(Z.encrypted);for(let $ of Z?.encryptedChunks||[])Q.push($);return Q}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",{alpha:!0,desynchronized:!0});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,Z=0,Q=0,$=0,O=this._video?.paused??!1){if((!J||!Z)&&this._video){let N=c(this._video),K;if(this._videoWidth){let F=this._video.videoWidth/this._videoWidth,G=this._video.videoHeight/this._videoHeight;K=w((N.width||0)/F,(N.height||0)/G,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight)}else K=w(N.width||0,N.height||0,this.prescaleFactor,this.prescaleHeightLimit,this.maxRenderHeight);if(J=K.width,Z=K.height,this._canvasParent)Q=N.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),$=N.x;this._canvas.style.width=N.width+"px",this._canvas.style.height=N.height+"px"}if(this._canvas.style.top=Q+"px",this._canvas.style.left=$+"px",J>0&&Z>0)this._canvasctrl.width=J,this._canvasctrl.height=Z;if(this._gpuRenderer&&J>0&&Z>0)this._gpuRenderer.updateSize(J,Z);if(O&&this.busy===!1)this.busy=!0;else O=!1;this.sendMessage("canvas",{width:J,height:Z,videoWidth:this._videoWidth||this._video?.videoWidth||0,videoHeight:this._videoHeight||this._video?.videoHeight||0,force:O})}_timeupdate(J){let Q={seeking:!0,waiting:!0,playing:!1}[J.type];if(Q!=null)this._playstate=Q;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},R._getSubtitleTransfers(J)),this._reAttachOffscreen(),this._ctx)this._ctx.filter="none"}setEncryptedTrack(J){if(this.sendMessage("setEncryptedTrack",{content:J},R._getSubtitleTransfers(void 0,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,Z,Q){this.sendMessage("video",{isPaused:J,currentTime:Z,rate:Q,colorSpace:this._videoColorSpace})}createEvent(J){this.sendMessage("createEvent",{event:J})}setEvent(J,Z){this.sendMessage("setEvent",{event:J,index:Z})}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,Z){this.sendMessage("setStyle",{style:J,index:Z})}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 Z=(await this._fetchFromWorker({target:"getStats"})).stats;return{framesRendered:Z.framesRendered??0,framesDropped:Z.framesDropped??0,avgRenderTime:Z.avgRenderTime??0,maxRenderTime:Z.maxRenderTime??0,minRenderTime:Z.minRenderTime??0,lastRenderTime:Z.lastRenderTime??0,pendingRenders:Z.pendingRenders??0,totalEvents:Z.totalEvents??0,cacheHits:Z.cacheHits??0,cacheMisses:Z.cacheMisses??0,renderFps:Z.avgRenderTime&&Z.avgRenderTime>0?Math.round(1000/Z.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((Z)=>{let Q=Z?.find(($)=>$.fullName.toLowerCase()===J);if(Q)Q.blob().then(($)=>{$.arrayBuffer().then((O)=>{this.addFont(new Uint8Array(O))})})})}catch(Z){console.warn("Local fonts API:",Z)}}_getLocalFont(J){try{if(navigator?.permissions?.query)navigator.permissions.query({name:"local-fonts"}).then((Z)=>{if(Z.state==="granted")this._sendLocalFont(J.font)});else this._sendLocalFont(J.font)}catch(Z){console.warn("Local fonts API:",Z)}}_unbusy(){if(this._observeDemandCompletion(),this._pendingDemandTimes.length>0){if(this._pendingDemandTimes.length>1){let Z=this._pendingDemandTimes[this._pendingDemandTimes.length-1];this._pendingDemandTimes.length=0,this._pendingDemandTimes.push(Z)}let J=this._pendingDemandTimes.shift();if(J){this._demandRender(J);return}}this.busy=!1}_markDemandDispatched(){if(!this._onDemandRender)return;this._activeDemandStartedAt=performance.now()}_observeDemandCompletion(){if(!this._onDemandRender||this._activeDemandStartedAt===0)return;let J=performance.now()-this._activeDemandStartedAt;if(this._activeDemandStartedAt=0,!Number.isFinite(J)||J<=0)return;this._smoothedDemandLatencyMs=this._smoothedDemandLatencyMs<=0?J:this._smoothedDemandLatencyMs*0.75+J*0.25}_getDemandPipelineLeadSeconds(J,Z){let Q=Z.expectedDisplayTime??Z.presentationTime??J,$=Math.max(0,Q-J)/1000;return Math.max(0,this._smoothedDemandLatencyMs/1000-$)}_enqueueDemand(J){let Z=this._pendingDemandTimes;if(Z.length>0){let Q=Z[Z.length-1];if(Math.abs(Q.mediaTime-J.mediaTime)>0.25)Z.length=0}if(Z.length>=R.MAX_PENDING_DEMANDS)Z.shift();Z.push(J)}_handleRVFC(J,Z){if(this._destroyed)return;let Q=this._video?.playbackRate??1,$=this._getDemandPipelineLeadSeconds(J,Z),N={mediaTime:Z.mediaTime+($+this.renderAhead)*Q,width:Z.width,height:Z.height};if(!this._workerReady){this._enqueueDemand(N),this._video.requestVideoFrameCallback(this._boundHandleRVFC);return}if(this.busy)this._enqueueDemand(N);else this.busy=!0,this._demandRender(N);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._markDemandDispatched(),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",{alpha:!0,desynchronized:!0}),this.sendMessage("detachOffscreen"),this.busy=!1,this._activeDemandStartedAt=0,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:Z,videoColorSpace:Q=this._videoColorSpace}=J;if(!Z||!Q)return;if(Z===Q)return;this._detachOffscreen();let $=y[Z]?.[Q];if($&&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='${$} 0 0 0 0 0 1 0'/></filter></svg>#f")`}_render(J){try{let{width:Z,height:Q}=J;if(this.debug)J.times.IPCTime=Date.now()-(J.times.JSRenderTime||0);if(this._canvasctrl.width!==Z||this._canvasctrl.height!==Q){if(this._canvasctrl.width=Z,this._canvasctrl.height=Q,this._lastRenderWidth=Z,this._lastRenderHeight=Q,this._gpuRenderer)this._gpuRenderer.updateSize(Z,Q);this._verifyColorSpace({subtitleColorSpace:J.colorSpace})}if(this._gpuRenderer){this._renderGPU(J);return}if(!this._ctx)return;let O=this._ctx,N=J.images,K=N.length;if(O.clearRect(0,0,Z,Q),J.asyncRender)for(let F=0;F<K;F++){let G=N[F];if(G.image)O.drawImage(G.image,G.x,G.y),G.image.close()}else{let F=R._hasAlphaBug??!1;for(let G=0;G<K;G++){let U=N[G];if(U.image){let{w:E,h:T,image:q}=U,Y=q instanceof Uint8ClampedArray?q:q instanceof Uint8Array?new Uint8ClampedArray(q.buffer,q.byteOffset,q.byteLength):new Uint8ClampedArray(q),L=m(Y,F);O.putImageData(new ImageData(L,E,T),U.x,U.y)}}}if(this.debug){J.times.JSRenderTime=Date.now()-(J.times.JSRenderTime||0)-(J.times.IPCTime||0);let F=0,G=J.times.bitmaps||K;delete J.times.bitmaps;for(let U in J.times)F+=J.times[U]||0;console.log("Bitmaps: "+G+" Total: "+(F|0)+"ms",J.times)}}finally{this._unbusy()}}_renderGPU(J){let Z=this._gpuRenderer;if(!Z)return;if(J.images.length===0){Z.clear();return}if(J.asyncRender){let Q=this._gpuBitmapImages,$=0;for(let O=0;O<J.images.length;O++){let N=J.images[O];if(!(N.image instanceof ImageBitmap))continue;let K=Q[$]||(Q[$]={image:N.image,x:0,y:0});K.image=N.image,K.x=N.x,K.y=N.y,$++}Q.length=$,Z.renderBitmaps(Q,this._canvasctrl.width,this._canvasctrl.height);for(let O of J.images)if(O.image instanceof ImageBitmap)O.image.close()}else Z.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 Q=0,$=J.times.bitmaps||J.images.length;delete J.times.bitmaps;for(let O in J.times)Q+=J.times[O]||0;console.log(`[${this._rendererType.toUpperCase()}] Bitmaps: `+$+" Total: "+(Q|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"))}_trackReady(){this.dispatchEvent(new CustomEvent("trackReady"))}async sendMessage(J,Z={},Q){if(await this._loaded,Q)this._worker.postMessage({target:J,transferable:Q,...Z},[...Q]);else this._worker.postMessage({target:J,...Z})}_fetchFromWorker(J){return new Promise((Z,Q)=>{try{let $=J.target,O=setTimeout(()=>{F(),Q(Error("Error: Timeout while trying to fetch "+$))},5000),N=(G)=>{if(G.data.target===$)F(),Z(G.data)},K=(G)=>{F(),Q(G instanceof Error?G:G.error||Error("Worker error"))},F=()=>{this._worker.removeEventListener("message",N),this._worker.removeEventListener("error",K),clearTimeout(O)};this._worker.addEventListener("message",N),this._worker.addEventListener("error",K),this._worker.postMessage(J)}catch($){Q($)}})}_console(J){console[J.command].apply(console,JSON.parse(J.content))}_onmessage(J){let Z=J.data.target;if(Z==="error"){this._error(J.data.error||"Unknown worker error");return}let Q=this["_"+Z];if(Q)Q.call(this,J.data)}_error(J){let Z=J instanceof Error?J:J instanceof ErrorEvent?J.error||Error(J.message):Error(String(J)),Q=J instanceof Event?new ErrorEvent(J.type,J):new ErrorEvent("error",{error:Z});return this.dispatchEvent(Q),console.error(Z),Z}_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 Z=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(),Z}}export{u as webYCbCrMap,l as testImageBugs,JJ as runFeatureTests,o as parseAss,s as libassYCbCrMap,f as isWebGPUSupported,p as isWebGL2Supported,c as getVideoPosition,r as getColorSpaceFilterUrl,$J as getBitmapBug,ZJ as getAlphaBug,e as fixPlayRes,m as fixAlpha,i as dropBlur,R as default,w as computeCanvasSize,y as colorMatrixConversionMap,v as WebGPURenderer,x as WebGL2Renderer,R as AkariSub};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AkariSub - JavaScript ASS/SSA Subtitle Renderer
|
|
3
|
+
*
|
|
4
|
+
* High-performance ASS/SSA subtitle renderer using libass compiled to WebAssembly.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export { default } from './ts/akarisub';
|
|
9
|
+
export { default as AkariSub } from './ts/akarisub';
|
|
10
|
+
export type { ASSEvent, ASSStyle, AkariSubOptions, PerformanceStats, ASSEventCallback, ASSStyleCallback, PerformanceStatsCallback, ResetStatsCallback, RenderImage, RenderTimes, VideoFrameCallbackMetadata, SubtitleColorSpace, WebYCbCrColorSpace, EncryptedSubtitleContent } from './ts/types';
|
|
11
|
+
export { webYCbCrMap, colorMatrixConversionMap, libassYCbCrMap, computeCanvasSize, getVideoPosition, fixAlpha, parseAss, dropBlur, fixPlayRes, testImageBugs, runFeatureTests, getAlphaBug, getBitmapBug, getColorSpaceFilterUrl } from './ts/utils';
|
|
12
|
+
export { WebGPURenderer, isWebGPUSupported } from './ts/webgpu-renderer';
|
|
13
|
+
export { WebGL2Renderer, isWebGL2Supported } from './ts/webgl2-renderer';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGnD,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,sBAAsB,EACvB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAGxE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/ts/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AkariSub - JavaScript ASS/SSA Subtitle Renderer
|
|
3
|
+
*
|
|
4
|
+
* High-performance ASS/SSA subtitle renderer using libass compiled to WebAssembly.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
// Main AkariSub class
|
|
9
|
+
export { default } from './ts/akarisub';
|
|
10
|
+
export { default as AkariSub } from './ts/akarisub';
|
|
11
|
+
// Utility exports (for advanced usage)
|
|
12
|
+
export { webYCbCrMap, colorMatrixConversionMap, libassYCbCrMap, computeCanvasSize, getVideoPosition, fixAlpha, parseAss, dropBlur, fixPlayRes, testImageBugs, runFeatureTests, getAlphaBug, getBitmapBug, getColorSpaceFilterUrl } from './ts/utils';
|
|
13
|
+
// WebGPU renderer exports
|
|
14
|
+
export { WebGPURenderer, isWebGPUSupported } from './ts/webgpu-renderer';
|
|
15
|
+
// WebGL2 renderer exports
|
|
16
|
+
export { WebGL2Renderer, isWebGL2Supported } from './ts/webgl2-renderer';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,sBAAsB;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAA;AAoBnD,uCAAuC;AACvC,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,sBAAsB,EACvB,MAAM,YAAY,CAAA;AAEnB,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAExE,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main AkariSub class - TypeScript implementation.
|
|
3
|
+
* High-level ASS/SSA subtitle renderer for web browsers using libass.
|
|
4
|
+
*/
|
|
5
|
+
import type { AkariSubOptions, ASSEvent, ASSStyle, PerformanceStats } from './types';
|
|
6
|
+
import type { EncryptedSubtitleContent } from './types';
|
|
7
|
+
/**
|
|
8
|
+
* AkariSub - JavaScript ASS/SSA Subtitle Renderer
|
|
9
|
+
*
|
|
10
|
+
* Renders ASS/SSA subtitles on an HTML5 video element using libass compiled to WebAssembly.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const renderer = new AkariSub({
|
|
15
|
+
* video: document.querySelector('video'),
|
|
16
|
+
* subUrl: '/subtitles/example.ass',
|
|
17
|
+
* workerUrl: '/akarisub-worker.js'
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Later, cleanup
|
|
21
|
+
* renderer.destroy();
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export default class AkariSub extends EventTarget {
|
|
25
|
+
private static readonly MAX_PENDING_DEMANDS;
|
|
26
|
+
private static _hasAlphaBug;
|
|
27
|
+
private static _hasBitmapBug;
|
|
28
|
+
private _loaded;
|
|
29
|
+
private _init;
|
|
30
|
+
private _onDemandRender;
|
|
31
|
+
private _offscreenRender;
|
|
32
|
+
private _video?;
|
|
33
|
+
private _videoWidth;
|
|
34
|
+
private _videoHeight;
|
|
35
|
+
private _videoColorSpace;
|
|
36
|
+
private _canvas;
|
|
37
|
+
private _canvasParent?;
|
|
38
|
+
private _bufferCanvas;
|
|
39
|
+
private _bufferCtx;
|
|
40
|
+
private _canvasctrl;
|
|
41
|
+
private _ctx;
|
|
42
|
+
private _lastRenderTime;
|
|
43
|
+
private _playstate;
|
|
44
|
+
private _destroyed;
|
|
45
|
+
private _workerReady;
|
|
46
|
+
private _ro?;
|
|
47
|
+
private _worker;
|
|
48
|
+
private _pendingDemandTimes;
|
|
49
|
+
private readonly _isLikelyWebKit;
|
|
50
|
+
private _activeDemandStartedAt;
|
|
51
|
+
private _smoothedDemandLatencyMs;
|
|
52
|
+
private _boundResize;
|
|
53
|
+
private _boundTimeUpdate;
|
|
54
|
+
private _boundSetRate;
|
|
55
|
+
private _boundUpdateColorSpace;
|
|
56
|
+
private _boundHandleRVFC;
|
|
57
|
+
private _gpuRenderer;
|
|
58
|
+
private _rendererType;
|
|
59
|
+
private _onCanvasFallback?;
|
|
60
|
+
private _lastRenderWidth;
|
|
61
|
+
private _lastRenderHeight;
|
|
62
|
+
private _gpuBitmapImages;
|
|
63
|
+
timeOffset: number;
|
|
64
|
+
debug: boolean;
|
|
65
|
+
prescaleFactor: number;
|
|
66
|
+
prescaleHeightLimit: number;
|
|
67
|
+
maxRenderHeight: number;
|
|
68
|
+
busy: boolean;
|
|
69
|
+
renderAhead: number;
|
|
70
|
+
constructor(options: AkariSubOptions);
|
|
71
|
+
private static _testImageBugs;
|
|
72
|
+
private static _test;
|
|
73
|
+
private static _getSubtitleTransfers;
|
|
74
|
+
/**
|
|
75
|
+
* Attempt to initialise the best available GPU renderer.
|
|
76
|
+
*/
|
|
77
|
+
private _initGPURenderer;
|
|
78
|
+
/** Returns which renderer backend is currently active. */
|
|
79
|
+
get rendererType(): 'webgpu' | 'webgl2' | 'canvas2d';
|
|
80
|
+
/** @deprecated Use rendererType === 'webgpu' */
|
|
81
|
+
get isUsingWebGPU(): boolean;
|
|
82
|
+
/** Returns true when a hardware-accelerated GPU renderer is active. */
|
|
83
|
+
get isUsingGPURenderer(): boolean;
|
|
84
|
+
private _createCanvas;
|
|
85
|
+
/**
|
|
86
|
+
* Resize the canvas to given parameters. Auto-generated if values are omitted.
|
|
87
|
+
*/
|
|
88
|
+
resize(width?: number, height?: number, top?: number, left?: number, force?: boolean): void;
|
|
89
|
+
private _timeupdate;
|
|
90
|
+
/**
|
|
91
|
+
* Change the video to use as target for event listeners.
|
|
92
|
+
*/
|
|
93
|
+
setVideo(video: HTMLVideoElement): void;
|
|
94
|
+
/**
|
|
95
|
+
* Run a benchmark on the worker.
|
|
96
|
+
*/
|
|
97
|
+
runBenchmark(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Overwrites the current subtitle content by URL.
|
|
100
|
+
*/
|
|
101
|
+
setTrackByUrl(url: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Overwrites the current subtitle content.
|
|
104
|
+
*/
|
|
105
|
+
setTrack(content: string | Uint8Array | ArrayBuffer): void;
|
|
106
|
+
/**
|
|
107
|
+
* Overwrites the current subtitle content with encrypted v2 payloads.
|
|
108
|
+
* Decryption happens inside the AkariSub worker so plaintext ASS text is not
|
|
109
|
+
* materialized in the main thread.
|
|
110
|
+
*/
|
|
111
|
+
setEncryptedTrack(content: EncryptedSubtitleContent): void;
|
|
112
|
+
/**
|
|
113
|
+
* Free currently used subtitle track.
|
|
114
|
+
*/
|
|
115
|
+
freeTrack(): void;
|
|
116
|
+
/**
|
|
117
|
+
* Sets the playback state of the media.
|
|
118
|
+
*/
|
|
119
|
+
setIsPaused(isPaused: boolean): void;
|
|
120
|
+
/**
|
|
121
|
+
* Sets the playback rate of the media.
|
|
122
|
+
*/
|
|
123
|
+
setRate(rate: number): void;
|
|
124
|
+
/**
|
|
125
|
+
* Sets the current time, playback state and rate of the subtitles.
|
|
126
|
+
*/
|
|
127
|
+
setCurrentTime(isPaused?: boolean, currentTime?: number, rate?: number): void;
|
|
128
|
+
/**
|
|
129
|
+
* Create a new ASS event directly.
|
|
130
|
+
*/
|
|
131
|
+
createEvent(event: Partial<ASSEvent>): void;
|
|
132
|
+
/**
|
|
133
|
+
* Overwrite the data of the event with the specified index.
|
|
134
|
+
*/
|
|
135
|
+
setEvent(event: Partial<ASSEvent>, index: number): void;
|
|
136
|
+
/**
|
|
137
|
+
* Remove the event with the specified index.
|
|
138
|
+
*/
|
|
139
|
+
removeEvent(index: number): void;
|
|
140
|
+
/**
|
|
141
|
+
* Get all ASS events.
|
|
142
|
+
*/
|
|
143
|
+
getEvents(): Promise<ASSEvent[]>;
|
|
144
|
+
/**
|
|
145
|
+
* Set a style override.
|
|
146
|
+
*/
|
|
147
|
+
styleOverride(style: Partial<ASSStyle>): void;
|
|
148
|
+
/**
|
|
149
|
+
* Disable style override.
|
|
150
|
+
*/
|
|
151
|
+
disableStyleOverride(): void;
|
|
152
|
+
/**
|
|
153
|
+
* Create a new ASS style directly.
|
|
154
|
+
*/
|
|
155
|
+
createStyle(style: Partial<ASSStyle>): void;
|
|
156
|
+
/**
|
|
157
|
+
* Overwrite the data of the style with the specified index.
|
|
158
|
+
*/
|
|
159
|
+
setStyle(style: Partial<ASSStyle>, index: number): void;
|
|
160
|
+
/**
|
|
161
|
+
* Remove the style with the specified index.
|
|
162
|
+
*/
|
|
163
|
+
removeStyle(index: number): void;
|
|
164
|
+
/**
|
|
165
|
+
* Get all ASS styles.
|
|
166
|
+
*/
|
|
167
|
+
getStyles(): Promise<ASSStyle[]>;
|
|
168
|
+
/**
|
|
169
|
+
* Adds a font to the renderer.
|
|
170
|
+
*/
|
|
171
|
+
addFont(font: string | Uint8Array): void;
|
|
172
|
+
/**
|
|
173
|
+
* Changes the font family of the default font.
|
|
174
|
+
*/
|
|
175
|
+
setDefaultFont(font: string): void;
|
|
176
|
+
/**
|
|
177
|
+
* Get real-time performance statistics.
|
|
178
|
+
*/
|
|
179
|
+
getStats(): Promise<PerformanceStats>;
|
|
180
|
+
/**
|
|
181
|
+
* Reset performance statistics counters.
|
|
182
|
+
*/
|
|
183
|
+
resetStats(): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Get event count
|
|
186
|
+
*/
|
|
187
|
+
getEventCount(): Promise<number>;
|
|
188
|
+
/**
|
|
189
|
+
* Get style count
|
|
190
|
+
*/
|
|
191
|
+
getStyleCount(): Promise<number>;
|
|
192
|
+
private _sendLocalFont;
|
|
193
|
+
private _getLocalFont;
|
|
194
|
+
private _unbusy;
|
|
195
|
+
private _markDemandDispatched;
|
|
196
|
+
private _observeDemandCompletion;
|
|
197
|
+
private _getDemandPipelineLeadSeconds;
|
|
198
|
+
private _enqueueDemand;
|
|
199
|
+
private _handleRVFC;
|
|
200
|
+
private _demandRender;
|
|
201
|
+
private _detachOffscreen;
|
|
202
|
+
private _reAttachOffscreen;
|
|
203
|
+
private _updateColorSpace;
|
|
204
|
+
private _verifyColorSpace;
|
|
205
|
+
private _render;
|
|
206
|
+
private _renderGPU;
|
|
207
|
+
private _ready;
|
|
208
|
+
/**
|
|
209
|
+
* Handler for partial_ready message from worker.
|
|
210
|
+
* Emitted early for large subtitle files to allow playback to start
|
|
211
|
+
* while font loading and track parsing continues.
|
|
212
|
+
*/
|
|
213
|
+
private _partial_ready;
|
|
214
|
+
private _trackReady;
|
|
215
|
+
/**
|
|
216
|
+
* Send data and execute function in the worker.
|
|
217
|
+
*/
|
|
218
|
+
sendMessage(target: string, data?: Record<string, any>, transferable?: Transferable[]): Promise<void>;
|
|
219
|
+
private _fetchFromWorker;
|
|
220
|
+
private _console;
|
|
221
|
+
private _onmessage;
|
|
222
|
+
private _error;
|
|
223
|
+
private _removeListeners;
|
|
224
|
+
/**
|
|
225
|
+
* Destroy the object, worker, listeners and all data.
|
|
226
|
+
*/
|
|
227
|
+
destroy(err?: Error | string): Error | undefined;
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=akarisub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"akarisub.d.ts","sourceRoot":"","sources":["../../../src/ts/akarisub.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAMjB,MAAM,SAAS,CAAA;AAChB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAoCvD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAI;IAG/C,OAAO,CAAC,MAAM,CAAC,YAAY,CAAuB;IAClD,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuB;IAGnD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAkB;IACjC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,IAAI,CAAgD;IAC5D,OAAO,CAAC,eAAe,CAAY;IACnC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,GAAG,CAAC,CAAgB;IAC5B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,mBAAmB,CAAkE;IAC7F,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,wBAAwB,CAAQ;IAGxC,OAAO,CAAC,YAAY,CAAY;IAChC,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,gBAAgB,CAA6D;IAGrF,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,iBAAiB,CAAC,CAAY;IAGtC,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,gBAAgB,CAA0D;IAG3E,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAQ;IACrB,WAAW,EAAE,MAAM,CAAA;IAE1B,YAAY,OAAO,EAAE,eAAe,EAmInC;mBAMoB,cAAc;mBAwDd,KAAK;IAI1B,OAAO,CAAC,MAAM,CAAC,qBAAqB;IA2BpC;;OAEG;YACW,gBAAgB;IAuC9B,0DAA0D;IAC1D,IAAI,YAAY,IAAI,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAEnD;IAED,gDAAgD;IAChD,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,uEAAuE;IACvE,IAAI,kBAAkB,IAAI,OAAO,CAEhC;IAMD,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,MAAM,CACJ,KAAK,GAAE,MAAU,EACjB,MAAM,GAAE,MAAU,EAClB,GAAG,GAAE,MAAU,EACf,IAAI,GAAE,MAAU,EAChB,KAAK,GAAE,OAAsC,GAC5C,IAAI,CA+DN;IAMD,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAmCtC;IAED;;OAEG;IACH,YAAY,IAAI,IAAI,CAEnB;IAMD;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI/B;IAED;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CAIzD;IAED;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAIzD;IAED;;OAEG;IACH,SAAS,IAAI,IAAI,CAEhB;IAMD;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAEnC;IAED;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1B;IAED;;OAEG;IACH,cAAc,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAO5E;IAMD;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAE1C;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtD;IAED;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/B;IAED;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGrC;IAMD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAE5C;IAED;;OAEG;IACH,oBAAoB,IAAI,IAAI,CAE3B;IAED;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAE1C;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtD;IAED;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/B;IAED;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGrC;IAMD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAEvC;IAED;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjC;IAMD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAmB1C;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhC;IAED;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAGrC;IAED;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAGrC;IAMD,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,OAAO;IAoBf,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,6BAA6B;IAOrC,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,WAAW;IA8BnB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,OAAO;IA4Ff,OAAO,CAAC,UAAU;IAsDlB,OAAO,CAAC,MAAM;IAuBd;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,WAAW;IAInB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EAAE,YAAY,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ9G;IAED,OAAO,CAAC,gBAAgB;IAsCxB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAoB/C;CACF"}
|