23 lines
2.7 KiB
TypeScript
23 lines
2.7 KiB
TypeScript
/**
|
||
* 浏览器图片解码适配,每次执行使用专用 module Worker,外部通用 pool 控制并发。
|
||
* 无 Worker/创建失败明确不可用,不退回主线程偷偷解码;没有任何存储、上传或元素发布副作用。
|
||
*/
|
||
import {fault} from '../canvas-core/values.ts';
|
||
import {createImageDecodePool,type ImageDimensions} from './image-decoder.ts';
|
||
/** 将每次 execute 转成专用 module Worker,请求 ID 在此 decoder 实例内递增。
|
||
* 外围池使用默认并发/排队预算;此适配按响应版本/id/输入长度核对归属,尺寸合法性由池检查。
|
||
* 没有超时;Worker 无响应时要由外部取消/close 才能终止,构造失败也不回退主线程解码。 */
|
||
export function createBrowserImageDecoder(){let next=0;return createImageDecodePool({execute:(bytes,signal)=>new Promise<ImageDimensions>((resolve,reject)=>{
|
||
if(typeof Worker==='undefined'){reject(fault('image_decoder_unavailable'));return;}
|
||
// 每任务一个 Worker,取消只 terminate 自己,不会打断其它图片的解码。
|
||
let worker:Worker;try{worker=new Worker(new URL('./image.worker.ts',import.meta.url),{type:'module'});}catch{reject(fault('image_decoder_unavailable'));return;}
|
||
/** finish 幂等解除消息/错误/abort 监听并 terminate;abort 在监听安装后再检查一次覆盖竞态。 */
|
||
const id=++next;let done=false;const finish=(error?:unknown,size?:ImageDimensions)=>{if(done)return;done=true;signal.removeEventListener('abort',abort);worker.onmessage=null;worker.onerror=null;worker.onmessageerror=null;worker.terminate();if(error)reject(error);else resolve(size!);},abort=()=>finish(fault('image_decode_aborted'));
|
||
signal.addEventListener('abort',abort,{once:true});if(signal.aborted){abort();return;}
|
||
// Worker error 会 preventDefault 并转稳定错误,消息无法反序列化则拒绝;所有路径共用 finish 终止线程。
|
||
// 只采纳精确匹配请求的成功尺寸或已知错误;未知错误码/错误协议作为 invalid response 拒绝。
|
||
worker.onmessage=event=>{const value=event.data;if(!value||value.version!==1||value.id!==id||value.byteLength!==bytes.size){finish(fault('invalid_image_response'));return;}if(value.error){finish(fault(value.error==='image_decode_failed'?'image_decode_failed':'invalid_image_response'));return;}finish(undefined,{width:value.width,height:value.height});};worker.onerror=event=>{event.preventDefault();finish(fault('image_decode_failed'));};worker.onmessageerror=()=>finish(fault('invalid_image_response'));
|
||
// 直接传不可变 Blob,不先在 UI 线程转成像素或整份 ArrayBuffer。
|
||
try{worker.postMessage({version:1,id,bytes});}catch{finish(fault('image_decode_failed'));}
|
||
})});}
|