shaderpad 1.0.0-beta.64 → 1.0.0-beta.65

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.
@@ -1,4 +1,4 @@
1
- "use strict";var he=Object.create;var Y=Object.defineProperty;var xe=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var Ne=Object.getPrototypeOf,Ce=Object.prototype.hasOwnProperty;var Ie=(t,e)=>{for(var n in e)Y(t,n,{get:e[n],enumerable:!0})},ce=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Me(e))!Ce.call(t,r)&&r!==n&&Y(t,r,{get:()=>e[r],enumerable:!(i=xe(e,r))||i.enumerable});return t};var ue=(t,e,n)=>(n=t!=null?he(Ne(t)):{},ce(e||!t||!t.__esModule?Y(n,"default",{value:t,enumerable:!0}):n,t)),Oe=t=>ce(Y({},"__esModule",{value:!0}),t);var je={};Ie(je,{default:()=>ze});module.exports=Oe(je);var Je={data:new Uint8Array(4),width:1,height:1};function z(t){return t instanceof HTMLVideoElement||t instanceof HTMLImageElement||t instanceof HTMLCanvasElement||t instanceof OffscreenCanvas}function Ee(t){return JSON.stringify(t,Object.keys(t).sort())}function j(t,e,n,i,r=0){let s=1/0,d=-1/0,o=1/0,u=-1/0,E=0,l=0;for(let m of n){let F=(r+e*i+m)*4,A=t[F],R=t[F+1];s=Math.min(s,A),d=Math.max(d,A),o=Math.min(o,R),u=Math.max(u,R),E+=t[F+2],l+=t[F+3]}return[(s+d)/2,(o+u)/2,E/n.length,l/n.length]}var K=null;function me(){return K||(K=import("@mediapipe/tasks-vision").then(({FilesetResolver:t})=>t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),K}function fe(t){return{historyParams:t?", framesAgo":"",fn:t?(i,r,s,d)=>{let o=s.replace(/\w+ /g,""),u=s?`${s}, int framesAgo`:"int framesAgo",E=o?`${o}, 0`:"0";return`${i} ${r}(${u}) {
1
+ "use strict";var he=Object.create;var Y=Object.defineProperty;var xe=Object.getOwnPropertyDescriptor;var Me=Object.getOwnPropertyNames;var Ne=Object.getPrototypeOf,Ce=Object.prototype.hasOwnProperty;var Ie=(t,e)=>{for(var n in e)Y(t,n,{get:e[n],enumerable:!0})},ce=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Me(e))!Ce.call(t,r)&&r!==n&&Y(t,r,{get:()=>e[r],enumerable:!(i=xe(e,r))||i.enumerable});return t};var ue=(t,e,n)=>(n=t!=null?he(Ne(t)):{},ce(e||!t||!t.__esModule?Y(n,"default",{value:t,enumerable:!0}):n,t)),Oe=t=>ce(Y({},"__esModule",{value:!0}),t);var je={};Ie(je,{default:()=>ze});module.exports=Oe(je);var Je={data:new Uint8Array(4),width:1,height:1};function z(t){return t instanceof HTMLVideoElement||t instanceof HTMLImageElement||t instanceof HTMLCanvasElement||t instanceof OffscreenCanvas}function Ee(t){return JSON.stringify(t,Object.keys(t).sort())}function j(t,e,n,i,r=0){let s=1/0,d=-1/0,o=1/0,u=-1/0,E=0,l=0;for(let m of n){let _=(r+e*i+m)*4,F=t[_],R=t[_+1];s=Math.min(s,F),d=Math.max(d,F),o=Math.min(o,R),u=Math.max(u,R),E+=t[_+2],l+=t[_+3]}return[(s+d)/2,(o+u)/2,E/n.length,l/n.length]}var K=null;function me(){return K||(K=import("@mediapipe/tasks-vision").then(({FilesetResolver:t})=>t.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),K}function fe(t){return{historyParams:t?", framesAgo":"",fn:t?(i,r,s,d)=>{let o=s.replace(/\w+ /g,""),u=s?`${s}, int framesAgo`:"int framesAgo",E=o?`${o}, 0`:"0";return`${i} ${r}(${u}) {
2
2
  ${d}
3
3
  }
4
4
  ${i} ${r}(${s}) { return ${r}(${E}); }`}:(i,r,s,d)=>`${i} ${r}(${s}) {
@@ -18,16 +18,16 @@ precision mediump float;
18
18
  in vec2 v_uv;
19
19
  uniform sampler2D u_texture;
20
20
  out vec4 outColor;
21
- void main() { outColor = texture(u_texture, v_uv); }`,ve=new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),$=478,Be=2,g=$+Be,I=512,M=1,_e=[336,296,334,293,300,276,283,282,295,285],Te=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],Fe=[70,63,105,66,107,55,65,52,53,46],Ae=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],Re=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],J=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],De=Array.from({length:$},(t,e)=>e),S={LEFT_EYEBROW:_e,LEFT_EYE:Te,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:Fe,RIGHT_EYE:Ae,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:Re,INNER_MOUTH:J,FACE_CENTER:$,MOUTH_CENTER:$+1},ye=["OVAL","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],D=["FACE_0","FACE_1","FACE_2","FACE_3","FACE_4","FACE_5","FACE_6","FACE_7"],Z=["FACE_8","FACE_9","FACE_10","FACE_11","FACE_12","FACE_13","FACE_14","FACE_15"],y=255,q=D.length+Z.length;function Q(t){return Object.fromEntries(t.map((e,n)=>[e,1<<n]))}function ee(t){return Object.fromEntries(Object.entries(t).map(([e,n])=>[e,n/y]))}var ge=Q(ye),Ue=Q(D),Pe=Q(Z),O=ee(ge),$e=ee(Ue),He=ee(Pe),Ge={modelPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task",maxFaces:1,minFaceDetectionConfidence:.5,minFacePresenceConfidence:.5,minTrackingConfidence:.5,outputFaceBlendshapes:!1,outputFacialTransformationMatrixes:!1};function k(t){let e=[];for(let n=1;n<t.length-1;++n)e.push(t[0],t[n],t[n+1]);return e}var T=null;function Ye(t){if(!T){let e=t.FACE_LANDMARKS_TESSELATION,n=[];for(let r=0;r<e.length-2;r+=3)n.push(e[r].start,e[r+1].start,e[r+2].start);let i=t.FACE_LANDMARKS_FACE_OVAL.map(({start:r})=>r);T=Object.fromEntries(Object.entries({LEFT_EYEBROW:k(_e),RIGHT_EYEBROW:k(Fe),LEFT_EYE:k(Te),RIGHT_EYE:k(Ae),MOUTH:k(Re),INNER_MOUTH:k(J),TESSELATION:n,OVAL:k(i)}).map(([r,s])=>[r,{indices:s,vertices:new Float32Array(s.length*2)}]))}}var P=new Map;function de(t,e,n){let i=t.createShader(t.VERTEX_SHADER);t.shaderSource(i,e),t.compileShader(i);let r=t.createShader(t.FRAGMENT_SHADER);t.shaderSource(r,n),t.compileShader(r);let s=t.createProgram();return t.attachShader(s,i),t.attachShader(s,r),t.linkProgram(s),t.deleteShader(i),t.deleteShader(r),s}function we(t){let e=t.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),n=de(e,le,ke),i=de(e,le,Se),r=e.createBuffer(),s=e.getAttribLocation(n,"a_pos"),d=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,d),e.bufferData(e.ARRAY_BUFFER,ve,e.STATIC_DRAW);let o=e.getAttribLocation(i,"a_pos"),u=e.getUniformLocation(n,"u_color"),E=e.getUniformLocation(i,"u_texture"),l=e.createTexture();e.bindTexture(e.TEXTURE_2D,l),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,null);let m=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,m),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,l,0),e.bindFramebuffer(e.FRAMEBUFFER,null),e.useProgram(i),e.uniform1i(E,0),e.colorMask(!0,!0,!0,!1),{canvas:t,gl:e,regionProgram:n,blitProgram:i,regionPositionBuffer:r,quadBuffer:d,regionPositionLocation:s,blitPositionLocation:o,colorLocation:u,textureLocation:E,scratchTexture:l,scratchFramebuffer:m}}function We(t,e,n){let{gl:i,canvas:r,scratchTexture:s}=t;r.width===e&&r.height===n||(r.width=e,r.height=n,i.bindTexture(i.TEXTURE_2D,s),i.texImage2D(i.TEXTURE_2D,0,i.RGBA,e,n,0,i.RGBA,i.UNSIGNED_BYTE,null))}function h(t,e,n,i,r,s,d){let{gl:o,regionProgram:u,regionPositionBuffer:E,regionPositionLocation:l,colorLocation:m,scratchFramebuffer:F}=t,A=M+i*g,{indices:R,vertices:v}=n;o.bindFramebuffer(o.FRAMEBUFFER,F),o.viewport(0,0,t.canvas.width,t.canvas.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),o.useProgram(u),o.bindBuffer(o.ARRAY_BUFFER,E),o.enableVertexAttribArray(l),o.vertexAttribPointer(l,2,o.FLOAT,!1,0,0),o.enable(o.BLEND),o.blendEquation(o.MAX),o.blendFunc(o.ONE,o.ONE);for(let a=0;a<R.length;++a){let N=(A+R[a])*4;v[a*2]=e[N],v[a*2+1]=e[N+1]}o.bufferData(o.ARRAY_BUFFER,v,o.DYNAMIC_DRAW),o.uniform4f(m,r,s,d,1),o.drawArrays(o.TRIANGLES,0,R.length)}function x(t){let{gl:e,blitProgram:n,quadBuffer:i,blitPositionLocation:r,scratchTexture:s}=t;e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,t.canvas.width,t.canvas.height),e.useProgram(n),e.bindBuffer(e.ARRAY_BUFFER,i),e.enableVertexAttribArray(r),e.vertexAttribPointer(r,2,e.FLOAT,!1,0,0),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,s),e.enable(e.BLEND),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE),e.drawArrays(e.TRIANGLES,0,6)}function Ve(t,e){let n=t.landmarks.data,i=e.length;n[0]=i;for(let r=0;r<i;++r){let s=e[r];for(let u=0;u<$;++u){let E=s[u],l=(M+r*g+u)*4;n[l]=E.x,n[l+1]=1-E.y,n[l+2]=E.z??0,n[l+3]=E.visibility??1}let d=j(n,r,De,g,M);n.set(d,(M+r*g+S.FACE_CENTER)*4);let o=j(n,r,J,g,1);n.set(o,(M+r*g+S.MOUTH_CENTER)*4)}t.state.nFaces=i}function Xe(t,e,n){let{mask:i,maxFaces:r,landmarks:s,state:{nFaces:d}}=t,{gl:o,canvas:u}=i,{data:E}=s;if(We(i,e,n),o.bindFramebuffer(o.FRAMEBUFFER,null),o.viewport(0,0,u.width,u.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),!T)return;let l=r<=q;for(let m=0;m<d;++m){let F=l&&m<D.length?$e[D[m]]:0,A=l?m<D.length?0:He[Z[m-D.length]]:(m+1)/y;h(i,E,T.TESSELATION,m,0,F,A),x(i),h(i,E,T.OVAL,m,O.OVAL,0,0),x(i),h(i,E,T.LEFT_EYEBROW,m,O.LEFT_EYEBROW,0,0),x(i),h(i,E,T.RIGHT_EYEBROW,m,O.RIGHT_EYEBROW,0,0),x(i),h(i,E,T.LEFT_EYE,m,O.LEFT_EYE,0,0),x(i),h(i,E,T.RIGHT_EYE,m,O.RIGHT_EYE,0,0),x(i),h(i,E,T.MOUTH,m,O.MOUTH,0,0),x(i),h(i,E,T.INNER_MOUTH,m,O.INNER_MOUTH,0,0),x(i)}}function Ke(t){let{textureName:e,options:{history:n,...i}={}}=t,r={...Ge,...i},s=Ee({...r,textureName:e}),d=r.maxFaces*g+M,o=Math.ceil(d/I);return function(u,E){let{injectGLSL:l,emitHook:m,updateTexturesInternal:F}=E,A=P.get(s),R=A?.landmarks.data??new Float32Array(I*o*4),v=A?.mask.canvas??new OffscreenCanvas(1,1),a=null,N=!1,w=!1;function W(c){if(!a)return;let f=a.state.nFaces,_=f*g+M,C=Math.ceil(_/I),L=c;typeof L>"u"&&G.length>0&&(L=G,G=[]),F({u_faceLandmarksTex:{data:a.landmarks.data,width:I,height:C,isPartial:!0},u_faceMask:a.mask.canvas},n?{skipHistoryWrite:w,historyWriteIndex:L}:void 0),u.updateUniforms({u_nFaces:f}),m("face:result",a.state.result)}async function be(){if(P.has(s))a=P.get(s);else{let[c,{FaceLandmarker:f}]=await Promise.all([me(),import("@mediapipe/tasks-vision")]);if(N)return;let _=await f.createFromOptions(c,{baseOptions:{modelAssetPath:r.modelPath,delegate:"GPU"},runningMode:"VIDEO",numFaces:r.maxFaces,minFaceDetectionConfidence:r.minFaceDetectionConfidence,minFacePresenceConfidence:r.minFacePresenceConfidence,minTrackingConfidence:r.minTrackingConfidence,outputFaceBlendshapes:r.outputFaceBlendshapes,outputFacialTransformationMatrixes:r.outputFacialTransformationMatrixes});if(N){_.close();return}a={landmarker:_,mask:we(v),subscribers:new Map,maxFaces:r.maxFaces,state:{nCalls:0,runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:R,textureHeight:o}},Ye(f),P.set(s,a)}a.subscribers.set(W,!1)}let te=be();async function ne(c){let f=performance.now();if(await te,!a)return;let _=++a.state.nCalls;a.state.pending=a.state.pending.then(async()=>{if(!a||_!==a.state.nCalls)return;let C=c instanceof HTMLVideoElement?"VIDEO":"IMAGE";a.state.runningMode!==C&&(a.state.runningMode=C,await a.landmarker.setOptions({runningMode:C}));let L=!1;if(c!==a.state.source?(a.state.source=c,a.state.videoTime=-1,L=!0):c instanceof HTMLVideoElement?c.currentTime!==a.state.videoTime&&(a.state.videoTime=c.currentTime,L=!0):c instanceof HTMLImageElement||f-a.state.resultTimestamp>2&&(L=!0),L){let p,U,X;if(c instanceof HTMLVideoElement){if(c.videoWidth===0||c.videoHeight===0||c.readyState<2)return;U=c.videoWidth,X=c.videoHeight,p=a.landmarker.detectForVideo(c,f)}else{if(c.width===0||c.height===0)return;U=c.width,X=c.height,p=a.landmarker.detect(c)}if(p){a.state.resultTimestamp=f,a.state.result=p,Ve(a,p.faceLandmarks),Xe(a,U,X);for(let se of a.subscribers.keys())se(),a.subscribers.set(se,!0)}}else if(a.state.result)for(let[p,U]of a.subscribers.entries())U||(p(),a.subscribers.set(p,!0))}),await a.state.pending}u.on("_init",()=>{u.initializeUniform("u_maxFaces","int",r.maxFaces),u.initializeUniform("u_nFaces","int",0),u.initializeTexture("u_faceLandmarksTex",{data:R,width:I,height:o},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:n}),u.initializeTexture("u_faceMask",v,{minFilter:"NEAREST",magFilter:"NEAREST",history:n}),te.then(()=>{N||!a||m("face:ready")})});let H=0,G=[],re=()=>{n&&(W(H),G.push(H),H=(H+1)%(n+1))};u.on("initializeTexture",(c,f)=>{c===e&&z(f)&&(re(),ne(f))}),u.on("updateTextures",(c,f)=>{let _=c[e];z(_)&&(w=f?.skipHistoryWrite??!1,w||re(),ne(_))}),u.on("destroy",()=>{N=!0,a&&(a.subscribers.delete(W),a.subscribers.size===0&&(a.landmarker.close(),a.mask.gl.deleteProgram(a.mask.regionProgram),a.mask.gl.deleteProgram(a.mask.blitProgram),a.mask.gl.deleteBuffer(a.mask.regionPositionBuffer),a.mask.gl.deleteBuffer(a.mask.quadBuffer),a.mask.gl.deleteTexture(a.mask.scratchTexture),a.mask.gl.deleteFramebuffer(a.mask.scratchFramebuffer),P.delete(s))),a=null});let{fn:B,historyParams:V}=fe(n),ae=n?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",Le=Array.from({length:q-1},(c,f)=>`step(${2**(f+1)}.0, faceBitF)`).join(" + "),ie=r.maxFaces<=q?`uint faceBits = (uint(mask.b * ${y}.0 + 0.5) << 8) | uint(mask.g * ${y}.0 + 0.5);
21
+ void main() { outColor = texture(u_texture, v_uv); }`,ve=new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),$=478,Be=2,g=$+Be,I=512,M=1,_e=[336,296,334,293,300,276,283,282,295,285],Te=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],Fe=[70,63,105,66,107,55,65,52,53,46],Ae=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],Re=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],J=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],De=Array.from({length:$},(t,e)=>e),S={LEFT_EYEBROW:_e,LEFT_EYE:Te,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:Fe,RIGHT_EYE:Ae,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:Re,INNER_MOUTH:J,FACE_CENTER:$,MOUTH_CENTER:$+1},ye=["OVAL","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],D=["FACE_0","FACE_1","FACE_2","FACE_3","FACE_4","FACE_5","FACE_6","FACE_7"],Z=["FACE_8","FACE_9","FACE_10","FACE_11","FACE_12","FACE_13","FACE_14","FACE_15"],y=255,q=D.length+Z.length;function Q(t){return Object.fromEntries(t.map((e,n)=>[e,1<<n]))}function ee(t){return Object.fromEntries(Object.entries(t).map(([e,n])=>[e,n/y]))}var ge=Q(ye),Ue=Q(D),Pe=Q(Z),O=ee(ge),$e=ee(Ue),He=ee(Pe),Ge={modelPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task",maxFaces:1,minFaceDetectionConfidence:.5,minFacePresenceConfidence:.5,minTrackingConfidence:.5,outputFaceBlendshapes:!1,outputFacialTransformationMatrixes:!1};function k(t){let e=[];for(let n=1;n<t.length-1;++n)e.push(t[0],t[n],t[n+1]);return e}var A=null;function Ye(t){if(!A){let e=t.FACE_LANDMARKS_TESSELATION,n=[];for(let r=0;r<e.length-2;r+=3)n.push(e[r].start,e[r+1].start,e[r+2].start);let i=t.FACE_LANDMARKS_FACE_OVAL.map(({start:r})=>r);A=Object.fromEntries(Object.entries({LEFT_EYEBROW:k(_e),RIGHT_EYEBROW:k(Fe),LEFT_EYE:k(Te),RIGHT_EYE:k(Ae),MOUTH:k(Re),INNER_MOUTH:k(J),TESSELATION:n,OVAL:k(i)}).map(([r,s])=>[r,{indices:s,vertices:new Float32Array(s.length*2)}]))}}var P=new Map;function de(t,e,n){let i=t.createShader(t.VERTEX_SHADER);t.shaderSource(i,e),t.compileShader(i);let r=t.createShader(t.FRAGMENT_SHADER);t.shaderSource(r,n),t.compileShader(r);let s=t.createProgram();return t.attachShader(s,i),t.attachShader(s,r),t.linkProgram(s),t.deleteShader(i),t.deleteShader(r),s}function we(t){let e=t.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),n=de(e,le,ke),i=de(e,le,Se),r=e.createBuffer(),s=e.getAttribLocation(n,"a_pos"),d=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,d),e.bufferData(e.ARRAY_BUFFER,ve,e.STATIC_DRAW);let o=e.getAttribLocation(i,"a_pos"),u=e.getUniformLocation(n,"u_color"),E=e.getUniformLocation(i,"u_texture"),l=e.createTexture();e.bindTexture(e.TEXTURE_2D,l),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,null);let m=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,m),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,l,0),e.bindFramebuffer(e.FRAMEBUFFER,null),e.useProgram(i),e.uniform1i(E,0),e.colorMask(!0,!0,!0,!1),{canvas:t,gl:e,regionProgram:n,blitProgram:i,regionPositionBuffer:r,quadBuffer:d,regionPositionLocation:s,blitPositionLocation:o,colorLocation:u,textureLocation:E,scratchTexture:l,scratchFramebuffer:m}}function We(t,e,n){let{gl:i,canvas:r,scratchTexture:s}=t;r.width===e&&r.height===n||(r.width=e,r.height=n,i.bindTexture(i.TEXTURE_2D,s),i.texImage2D(i.TEXTURE_2D,0,i.RGBA,e,n,0,i.RGBA,i.UNSIGNED_BYTE,null))}function h(t,e,n,i,r,s,d){let{gl:o,regionProgram:u,regionPositionBuffer:E,regionPositionLocation:l,colorLocation:m,scratchFramebuffer:_}=t,F=M+i*g,{indices:R,vertices:v}=n;o.bindFramebuffer(o.FRAMEBUFFER,_),o.viewport(0,0,t.canvas.width,t.canvas.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),o.useProgram(u),o.bindBuffer(o.ARRAY_BUFFER,E),o.enableVertexAttribArray(l),o.vertexAttribPointer(l,2,o.FLOAT,!1,0,0),o.enable(o.BLEND),o.blendEquation(o.MAX),o.blendFunc(o.ONE,o.ONE);for(let a=0;a<R.length;++a){let N=(F+R[a])*4;v[a*2]=e[N],v[a*2+1]=e[N+1]}o.bufferData(o.ARRAY_BUFFER,v,o.DYNAMIC_DRAW),o.uniform4f(m,r,s,d,1),o.drawArrays(o.TRIANGLES,0,R.length)}function x(t){let{gl:e,blitProgram:n,quadBuffer:i,blitPositionLocation:r,scratchTexture:s}=t;e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,t.canvas.width,t.canvas.height),e.useProgram(n),e.bindBuffer(e.ARRAY_BUFFER,i),e.enableVertexAttribArray(r),e.vertexAttribPointer(r,2,e.FLOAT,!1,0,0),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,s),e.enable(e.BLEND),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE),e.drawArrays(e.TRIANGLES,0,6)}function Ve(t,e){let n=t.landmarks.data,i=e.length;n[0]=i;for(let r=0;r<i;++r){let s=e[r];for(let u=0;u<$;++u){let E=s[u],l=(M+r*g+u)*4;n[l]=E.x,n[l+1]=1-E.y,n[l+2]=E.z??0,n[l+3]=E.visibility??1}let d=j(n,r,De,g,M);n.set(d,(M+r*g+S.FACE_CENTER)*4);let o=j(n,r,J,g,1);n.set(o,(M+r*g+S.MOUTH_CENTER)*4)}t.state.nFaces=i}function Xe(t,e,n){let{mask:i,maxFaces:r,landmarks:s,state:{nFaces:d}}=t,{gl:o,canvas:u}=i,{data:E}=s;if(We(i,e,n),o.bindFramebuffer(o.FRAMEBUFFER,null),o.viewport(0,0,u.width,u.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),!A)return;let l=r<=q;for(let m=0;m<d;++m){let _=l&&m<D.length?$e[D[m]]:0,F=l?m<D.length?0:He[Z[m-D.length]]:(m+1)/y;h(i,E,A.TESSELATION,m,0,_,F),x(i),h(i,E,A.OVAL,m,O.OVAL,0,0),x(i),h(i,E,A.LEFT_EYEBROW,m,O.LEFT_EYEBROW,0,0),x(i),h(i,E,A.RIGHT_EYEBROW,m,O.RIGHT_EYEBROW,0,0),x(i),h(i,E,A.LEFT_EYE,m,O.LEFT_EYE,_,F),x(i),h(i,E,A.RIGHT_EYE,m,O.RIGHT_EYE,_,F),x(i),h(i,E,A.MOUTH,m,O.MOUTH,0,0),x(i),h(i,E,A.INNER_MOUTH,m,O.INNER_MOUTH,_,F),x(i)}}function Ke(t){let{textureName:e,options:{history:n,...i}={}}=t,r={...Ge,...i},s=Ee({...r,textureName:e}),d=r.maxFaces*g+M,o=Math.ceil(d/I);return function(u,E){let{injectGLSL:l,emitHook:m,updateTexturesInternal:_}=E,F=P.get(s),R=F?.landmarks.data??new Float32Array(I*o*4),v=F?.mask.canvas??new OffscreenCanvas(1,1),a=null,N=!1,w=!1;function W(c){if(!a)return;let f=a.state.nFaces,T=f*g+M,C=Math.ceil(T/I),L=c;typeof L>"u"&&G.length>0&&(L=G,G=[]),_({u_faceLandmarksTex:{data:a.landmarks.data,width:I,height:C,isPartial:!0},u_faceMask:a.mask.canvas},n?{skipHistoryWrite:w,historyWriteIndex:L}:void 0),u.updateUniforms({u_nFaces:f}),m("face:result",a.state.result)}async function be(){if(P.has(s))a=P.get(s);else{let[c,{FaceLandmarker:f}]=await Promise.all([me(),import("@mediapipe/tasks-vision")]);if(N)return;let T=await f.createFromOptions(c,{baseOptions:{modelAssetPath:r.modelPath,delegate:"GPU"},runningMode:"VIDEO",numFaces:r.maxFaces,minFaceDetectionConfidence:r.minFaceDetectionConfidence,minFacePresenceConfidence:r.minFacePresenceConfidence,minTrackingConfidence:r.minTrackingConfidence,outputFaceBlendshapes:r.outputFaceBlendshapes,outputFacialTransformationMatrixes:r.outputFacialTransformationMatrixes});if(N){T.close();return}a={landmarker:T,mask:we(v),subscribers:new Map,maxFaces:r.maxFaces,state:{nCalls:0,runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:R,textureHeight:o}},Ye(f),P.set(s,a)}a.subscribers.set(W,!1)}let te=be();async function ne(c){let f=performance.now();if(await te,!a)return;let T=++a.state.nCalls;a.state.pending=a.state.pending.then(async()=>{if(!a||T!==a.state.nCalls)return;let C=c instanceof HTMLVideoElement?"VIDEO":"IMAGE";a.state.runningMode!==C&&(a.state.runningMode=C,await a.landmarker.setOptions({runningMode:C}));let L=!1;if(c!==a.state.source?(a.state.source=c,a.state.videoTime=-1,L=!0):c instanceof HTMLVideoElement?c.currentTime!==a.state.videoTime&&(a.state.videoTime=c.currentTime,L=!0):c instanceof HTMLImageElement||f-a.state.resultTimestamp>2&&(L=!0),L){let p,U,X;if(c instanceof HTMLVideoElement){if(c.videoWidth===0||c.videoHeight===0||c.readyState<2)return;U=c.videoWidth,X=c.videoHeight,p=a.landmarker.detectForVideo(c,f)}else{if(c.width===0||c.height===0)return;U=c.width,X=c.height,p=a.landmarker.detect(c)}if(p){a.state.resultTimestamp=f,a.state.result=p,Ve(a,p.faceLandmarks),Xe(a,U,X);for(let se of a.subscribers.keys())se(),a.subscribers.set(se,!0)}}else if(a.state.result)for(let[p,U]of a.subscribers.entries())U||(p(),a.subscribers.set(p,!0))}),await a.state.pending}u.on("_init",()=>{u.initializeUniform("u_maxFaces","int",r.maxFaces),u.initializeUniform("u_nFaces","int",0),u.initializeTexture("u_faceLandmarksTex",{data:R,width:I,height:o},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:n}),u.initializeTexture("u_faceMask",v,{minFilter:"NEAREST",magFilter:"NEAREST",history:n}),te.then(()=>{N||!a||m("face:ready")})});let H=0,G=[],re=()=>{n&&(W(H),G.push(H),H=(H+1)%(n+1))};u.on("initializeTexture",(c,f)=>{c===e&&z(f)&&(re(),ne(f))}),u.on("updateTextures",(c,f)=>{let T=c[e];z(T)&&(w=f?.skipHistoryWrite??!1,w||re(),ne(T))}),u.on("destroy",()=>{N=!0,a&&(a.subscribers.delete(W),a.subscribers.size===0&&(a.landmarker.close(),a.mask.gl.deleteProgram(a.mask.regionProgram),a.mask.gl.deleteProgram(a.mask.blitProgram),a.mask.gl.deleteBuffer(a.mask.regionPositionBuffer),a.mask.gl.deleteBuffer(a.mask.quadBuffer),a.mask.gl.deleteTexture(a.mask.scratchTexture),a.mask.gl.deleteFramebuffer(a.mask.scratchFramebuffer),P.delete(s))),a=null});let{fn:B,historyParams:V}=fe(n),ae=n?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",Le=Array.from({length:q-1},(c,f)=>`step(${2**(f+1)}.0, faceBitF)`).join(" + "),ie=r.maxFaces<=q?`uint faceBits = (uint(mask.b * ${y}.0 + 0.5) << 8) | uint(mask.g * ${y}.0 + 0.5);
22
22
  uint faceBit = faceBits & (~faceBits + 1u);
23
23
  float faceBitF = float(faceBit);
24
24
  float hasFace = sign(faceBitF);
25
25
  float faceIndex = ${Le} - (1.0 - hasFace);`:`float faceIndex = float(int(uint(mask.b * ${y}.0 + 0.5)) - 1);`,b=(c,...f)=>B("vec2",`${c}At`,"vec2 pos",`vec4 mask = ${ae};
26
26
  ${ie}
27
27
  uint bits = uint(mask.r * ${y}.0 + 0.5);
28
- float hit = sign(float(bits & ${f.reduce((_,C)=>_|ge[C],0)}u));
29
- return vec2(hit, mix(-1.0, faceIndex, hit));`),oe=(c,f,_)=>B("vec2",`${c}At`,"vec2 pos",`vec2 left = ${f}(pos${V});
30
- vec2 right = ${_}(pos${V});
28
+ float hit = sign(float(bits & ${f.reduce((T,C)=>T|ge[C],0)}u));
29
+ return vec2(hit, mix(-1.0, faceIndex, hit));`),oe=(c,f,T)=>B("vec2",`${c}At`,"vec2 pos",`vec2 left = ${f}(pos${V});
30
+ vec2 right = ${T}(pos${V});
31
31
  return mix(right, left, left.x);`),pe=c=>c.map(f=>B("float",`in${f[0].toUpperCase()+f.slice(1)}`,"vec2 pos",`vec2 a = ${f}At(pos${V}); return step(0.0, a.y) * a.x;`)).join(`
32
32
  `);l(`
33
33
  uniform int u_maxFaces;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts","../../src/plugins/mediapipe-common.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n\tv_uv = a_pos;\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\nconst BLIT_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nin vec2 v_uv;\nuniform sampler2D u_texture;\nout vec4 outColor;\nvoid main() { outColor = texture(u_texture, v_uv); }`;\nconst FULLSCREEN_TRIANGLES = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tMOUTH: MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n/* Face mask channel layout:\n- R: additive bitfield for face regions. Since it’s additive, it handles overlapping regions.\n- G/B: tessellation ownership. When maxFaces is <= 16, G stores one-hot bits for\n faces 0-7 and B stores one-hot bits for faces 8-15 so overlapping faces\n remain decodable. Above 16 maxFaces, B falls back to storing faceIndex + 1 as\n an additive byte value, which will overcount on overlaps. */\nconst RED_REGION_NAMES = [\n\t'OVAL',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst GREEN_REGION_NAMES = ['FACE_0', 'FACE_1', 'FACE_2', 'FACE_3', 'FACE_4', 'FACE_5', 'FACE_6', 'FACE_7'] as const;\nconst BLUE_REGION_NAMES = [\n\t'FACE_8',\n\t'FACE_9',\n\t'FACE_10',\n\t'FACE_11',\n\t'FACE_12',\n\t'FACE_13',\n\t'FACE_14',\n\t'FACE_15',\n] as const;\nconst CHANNEL_BIT_SCALE = 255;\nconst GB_BITMASK_MAX_FACES = GREEN_REGION_NAMES.length + BLUE_REGION_NAMES.length;\n\nfunction createChannelBitValues<const T extends readonly string[]>(names: T): Record<T[number], number> {\n\treturn Object.fromEntries(names.map((name, i) => [name, 1 << i])) as Record<T[number], number>;\n}\n\nfunction normalizeChannelBitValues<T extends Record<string, number>>(bitValues: T): T {\n\treturn Object.fromEntries(\n\t\tObject.entries(bitValues).map(([name, bitValue]) => [name, bitValue / CHANNEL_BIT_SCALE]),\n\t) as T;\n}\n\nconst RED_REGION_BIT_VALUES = createChannelBitValues(RED_REGION_NAMES);\nconst GREEN_REGION_BIT_VALUES = createChannelBitValues(GREEN_REGION_NAMES);\nconst BLUE_REGION_BIT_VALUES = createChannelBitValues(BLUE_REGION_NAMES);\nconst RED_CHANNEL_VALUES = normalizeChannelBitValues(RED_REGION_BIT_VALUES);\nconst GREEN_CHANNEL_VALUES = normalizeChannelBitValues(GREEN_REGION_BIT_VALUES);\nconst BLUE_CHANNEL_VALUES = normalizeChannelBitValues(BLUE_REGION_BIT_VALUES);\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { indices: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start,\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tMOUTH: fanTriangulate(MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, indices]) => [key, { indices, vertices: new Float32Array(indices.length * 2) }]),\n\t\t);\n\t}\n}\n\ninterface MaskRenderer {\n\tcanvas: OffscreenCanvas;\n\tgl: WebGL2RenderingContext;\n\tregionProgram: WebGLProgram;\n\tblitProgram: WebGLProgram;\n\tregionPositionBuffer: WebGLBuffer;\n\tquadBuffer: WebGLBuffer;\n\tregionPositionLocation: number;\n\tblitPositionLocation: number;\n\tcolorLocation: WebGLUniformLocation;\n\ttextureLocation: WebGLUniformLocation;\n\tscratchTexture: WebGLTexture;\n\tscratchFramebuffer: WebGLFramebuffer;\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tmask: MaskRenderer;\n\tsubscribers: Map<Function, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\tnCalls: number;\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram {\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, vertexSource);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, fragmentSource);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\treturn program;\n}\n\nfunction initMaskRenderer(canvas: OffscreenCanvas): MaskRenderer {\n\tconst gl = canvas.getContext('webgl2', {\n\t\tantialias: false,\n\t\tpreserveDrawingBuffer: true,\n\t})!;\n\tconst regionProgram = createProgram(gl, MASK_VERTEX_SHADER, MASK_FRAGMENT_SHADER);\n\tconst blitProgram = createProgram(gl, MASK_VERTEX_SHADER, BLIT_FRAGMENT_SHADER);\n\n\tconst regionPositionBuffer = gl.createBuffer()!;\n\tconst regionPositionLocation = gl.getAttribLocation(regionProgram, 'a_pos');\n\n\tconst quadBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_TRIANGLES, gl.STATIC_DRAW);\n\tconst blitPositionLocation = gl.getAttribLocation(blitProgram, 'a_pos');\n\n\tconst colorLocation = gl.getUniformLocation(regionProgram, 'u_color')!;\n\tconst textureLocation = gl.getUniformLocation(blitProgram, 'u_texture')!;\n\n\tconst scratchTexture = gl.createTexture()!;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n\n\tconst scratchFramebuffer = gl.createFramebuffer()!;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, scratchTexture, 0);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n\tgl.useProgram(blitProgram);\n\tgl.uniform1i(textureLocation, 0);\n\tgl.colorMask(true, true, true, false);\n\n\treturn {\n\t\tcanvas,\n\t\tgl,\n\t\tregionProgram,\n\t\tblitProgram,\n\t\tregionPositionBuffer,\n\t\tquadBuffer,\n\t\tregionPositionLocation,\n\t\tblitPositionLocation,\n\t\tcolorLocation,\n\t\ttextureLocation,\n\t\tscratchTexture,\n\t\tscratchFramebuffer,\n\t};\n}\n\nfunction resizeMaskRenderer(mask: MaskRenderer, width: number, height: number) {\n\tconst { gl, canvas, scratchTexture } = mask;\n\tif (canvas.width === width && canvas.height === height) return;\n\tcanvas.width = width;\n\tcanvas.height = height;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n}\n\nfunction drawRegionToScratch(\n\tmask: MaskRenderer,\n\tlandmarksData: Float32Array,\n\tfaceRegion: FaceRegion,\n\tfaceIdx: number,\n\tr: number,\n\tg: number,\n\tb: number,\n) {\n\tconst { gl, regionProgram, regionPositionBuffer, regionPositionLocation, colorLocation, scratchFramebuffer } = mask;\n\tconst baseIdx = N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT;\n\tconst { indices, vertices } = faceRegion;\n\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\tgl.useProgram(regionProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, regionPositionBuffer);\n\tgl.enableVertexAttribArray(regionPositionLocation);\n\tgl.vertexAttribPointer(regionPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\n\tfor (let i = 0; i < indices.length; ++i) {\n\t\tconst landmarkIdx = (baseIdx + indices[i]) * 4;\n\t\tvertices[i * 2] = landmarksData[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksData[landmarkIdx + 1];\n\t}\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, indices.length);\n}\n\nfunction accumulateScratch(mask: MaskRenderer) {\n\tconst { gl, blitProgram, quadBuffer, blitPositionLocation, scratchTexture } = mask;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.useProgram(blitProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.enableVertexAttribArray(blitPositionLocation);\n\tgl.vertexAttribPointer(blitPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.activeTexture(gl.TEXTURE0);\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.FUNC_ADD);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS,\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4,\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMask(detector: Detector, width: number, height: number) {\n\tconst {\n\t\tmask,\n\t\tmaxFaces,\n\t\tlandmarks,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl, canvas: maskCanvas } = mask;\n\tconst { data: landmarksData } = landmarks;\n\n\tresizeMaskRenderer(mask, width, height);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\n\tif (!faceRegions) return;\n\n\tconst useTesselationBitmask = maxFaces <= GB_BITMASK_MAX_FACES;\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst g =\n\t\t\tuseTesselationBitmask && faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? GREEN_CHANNEL_VALUES[GREEN_REGION_NAMES[faceIdx]]\n\t\t\t\t: 0;\n\t\tconst b = useTesselationBitmask\n\t\t\t? faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? 0\n\t\t\t\t: BLUE_CHANNEL_VALUES[BLUE_REGION_NAMES[faceIdx - GREEN_REGION_NAMES.length]]\n\t\t\t: (faceIdx + 1) / CHANNEL_BIT_SCALE;\n\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.TESSELATION, faceIdx, 0, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.OVAL, faceIdx, RED_CHANNEL_VALUES.OVAL, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.LEFT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.LEFT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.RIGHT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.RIGHT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.MOUTH, faceIdx, RED_CHANNEL_VALUES.MOUTH, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.INNER_MOUTH,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.INNER_MOUTH,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, emitHook, updateTexturesInternal } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet destroyed = false;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult(singleHistoryWriteIndex?: number) {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlet historyWriteIndex: number | number[] | undefined = singleHistoryWriteIndex;\n\t\t\tif (typeof historyWriteIndex === 'undefined' && pendingBackfillSlots.length > 0) {\n\t\t\t\thistoryWriteIndex = pendingBackfillSlots;\n\t\t\t\tpendingBackfillSlots = [];\n\t\t\t}\n\t\t\tupdateTexturesInternal(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\thistory ? { skipHistoryWrite, historyWriteIndex } : undefined,\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tif (destroyed) return;\n\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\t\t\t\tif (destroyed) {\n\t\t\t\t\tfaceLandmarker.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tmask: initMaskRenderer(maskCanvas),\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\tnCalls: 0,\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\t\t\tconst callOrder = ++detector.state.nCalls;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (!detector || callOrder !== detector.state.nCalls) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({\n\t\t\t\t\t\trunningMode: requiredMode,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tlet width: number, height: number;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\twidth = source.videoWidth;\n\t\t\t\t\t\theight = source.videoHeight;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\twidth = source.width;\n\t\t\t\t\t\theight = source.height;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMask(detector, width, height);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result) {\n\t\t\t\t\tfor (const [cb, hasCalled] of detector.subscribers.entries()) {\n\t\t\t\t\t\tif (!hasCalled) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('_init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: textureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: 'RGBA32F',\n\t\t\t\t\ttype: 'FLOAT',\n\t\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\t\thistory,\n\t\t\t\t},\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => {\n\t\t\t\tif (destroyed || !detector) return;\n\t\t\t\temitHook('face:ready');\n\t\t\t});\n\t\t});\n\n\t\tlet historyWriteCounter = 0;\n\t\tlet pendingBackfillSlots: number[] = [];\n\t\tconst writeToHistory = () => {\n\t\t\tif (!history) return;\n\t\t\tonResult(historyWriteCounter); // Write stale data immediately.\n\t\t\tpendingBackfillSlots.push(historyWriteCounter); // Queue up backfill with more recent data.\n\t\t\thistoryWriteCounter = (historyWriteCounter + 1) % (history + 1);\n\t\t};\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) {\n\t\t\t\twriteToHistory();\n\t\t\t\tdetectFaces(source);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tif (!skipHistoryWrite) writeToHistory();\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tdestroyed = true;\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.regionProgram);\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.blitProgram);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.regionPositionBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.quadBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteTexture(detector.mask.scratchTexture);\n\t\t\t\t\tdetector.mask.gl.deleteFramebuffer(detector.mask.scratchFramebuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\t\tconst decodeFaceBitIndex = Array.from(\n\t\t\t{ length: GB_BITMASK_MAX_FACES - 1 },\n\t\t\t(_, i) => `step(${2 ** (i + 1)}.0, faceBitF)`,\n\t\t).join(' + ');\n\t\tconst decodeFaceIndex =\n\t\t\toptions.maxFaces <= GB_BITMASK_MAX_FACES\n\t\t\t\t? `uint faceBits = (uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5) << 8) | uint(mask.g * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tuint faceBit = faceBits & (~faceBits + 1u);\n\tfloat faceBitF = float(faceBit);\n\tfloat hasFace = sign(faceBitF);\n\tfloat faceIndex = ${decodeFaceBitIndex} - (1.0 - hasFace);`\n\t\t\t\t: `float faceIndex = float(int(uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5)) - 1);`;\n\n\t\tconst checkAt = (fnName: string, ...regionNames: (keyof typeof RED_REGION_BIT_VALUES)[]) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\tuint bits = uint(mask.r * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tfloat hit = sign(float(bits & ${regionNames.reduce(\n\t\t(mask, regionName) => mask | RED_REGION_BIT_VALUES[regionName],\n\t\t0,\n\t)}u));\n\treturn vec2(hit, mix(-1.0, faceIndex, hit));`,\n\t\t\t);\n\n\t\tconst combineLeftRight = (fnName: string, leftFn: string, rightFn: string) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\tvec2 right = ${rightFn}(pos${historyParams});\n\treturn mix(right, left, left.x);`,\n\t\t\t);\n\n\t\tconst checkIn = (fnNames: string[]) =>\n\t\t\tfnNames\n\t\t\t\t.map(fnName =>\n\t\t\t\t\tfn(\n\t\t\t\t\t\t'float',\n\t\t\t\t\t\t`in${fnName[0].toUpperCase() + fnName.slice(1)}`,\n\t\t\t\t\t\t'vec2 pos',\n\t\t\t\t\t\t`vec2 a = ${fnName}At(pos${historyParams}); return step(0.0, a.y) * a.x;`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.join('\\n');\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform mediump sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`,\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`,\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${checkAt('leftEyebrow', 'LEFT_EYEBROW')}\n${checkAt('rightEyebrow', 'RIGHT_EYEBROW')}\n${checkAt('leftEye', 'LEFT_EYE')}\n${checkAt('rightEye', 'RIGHT_EYE')}\n${checkAt('lips', 'MOUTH')}\n${checkAt('mouth', 'MOUTH', 'INNER_MOUTH')}\n${checkAt('innerMouth', 'INNER_MOUTH')}\n${checkAt('faceOval', 'OVAL')}\n${fn(\n\t'vec2',\n\t'faceAt',\n\t'vec2 pos',\n\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\treturn vec2(step(0.0, faceIndex), faceIndex);`,\n)}\n${combineLeftRight('eye', 'leftEyeAt', 'rightEyeAt')}\n${combineLeftRight('eyebrow', 'leftEyebrowAt', 'rightEyebrowAt')}\n${checkIn(['eyebrow', 'eye', 'mouth', 'innerMouth', 'lips', 'face'])}`);\n\t};\n}\n\nexport default face;\n","import { TextureSource } from '..';\n\nexport const dummyTexture = { data: new Uint8Array(4), width: 1, height: 1 };\n\nexport type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;\n\nexport function isMediaPipeSource(source: TextureSource): source is MediaPipeSource {\n\treturn (\n\t\tsource instanceof HTMLVideoElement ||\n\t\tsource instanceof HTMLImageElement ||\n\t\tsource instanceof HTMLCanvasElement ||\n\t\tsource instanceof OffscreenCanvas\n\t);\n}\n\nexport function hashOptions(options: object): string {\n\treturn JSON.stringify(options, Object.keys(options).sort());\n}\n\nexport function calculateBoundingBoxCenter(\n\tdata: Float32Array,\n\tentityIdx: number,\n\tlandmarkIndices: readonly number[] | number[],\n\tlandmarkCount: number,\n\toffset: number = 0,\n): [number, number, number, number] {\n\tlet minX = Infinity,\n\t\tmaxX = -Infinity,\n\t\tminY = Infinity,\n\t\tmaxY = -Infinity,\n\t\tavgZ = 0,\n\t\tavgVisibility = 0;\n\n\tfor (const idx of landmarkIndices) {\n\t\tconst dataIdx = (offset + entityIdx * landmarkCount + idx) * 4;\n\t\tconst x = data[dataIdx];\n\t\tconst y = data[dataIdx + 1];\n\t\tminX = Math.min(minX, x);\n\t\tmaxX = Math.max(maxX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxY = Math.max(maxY, y);\n\t\tavgZ += data[dataIdx + 2];\n\t\tavgVisibility += data[dataIdx + 3];\n\t}\n\n\treturn [\n\t\t(minX + maxX) / 2,\n\t\t(minY + maxY) / 2,\n\t\tavgZ / landmarkIndices.length,\n\t\tavgVisibility / landmarkIndices.length,\n\t];\n}\n\nlet filesetPromise: Promise<any> | null = null;\nexport function getSharedFileset(): Promise<any> {\n\tif (!filesetPromise) {\n\t\tfilesetPromise = import('@mediapipe/tasks-vision').then(({ FilesetResolver }) =>\n\t\t\tFilesetResolver.forVisionTasks(\n\t\t\t\t`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${__MEDIAPIPE_TASKS_VISION_VERSION__}/wasm`,\n\t\t\t),\n\t\t);\n\t}\n\treturn filesetPromise;\n}\n\nexport function generateGLSLFn(history: number | undefined) {\n\tconst historyParams = history ? ', framesAgo' : '';\n\tconst fn = history\n\t\t? (returnType: string, name: string, args: string, body: string) => {\n\t\t\t\tconst argsOnly = args.replace(/\\w+ /g, '');\n\t\t\t\tconst historyArgs = args ? `${args}, int framesAgo` : 'int framesAgo';\n\t\t\t\tconst callArgs = argsOnly ? `${argsOnly}, 0` : '0';\n\t\t\t\treturn `${returnType} ${name}(${historyArgs}) {\\n${body}\\n}\n${returnType} ${name}(${args}) { return ${name}(${callArgs}); }`;\n\t\t\t}\n\t\t: (returnType: string, name: string, args: string, body: string) =>\n\t\t\t\t`${returnType} ${name}(${args}) {\\n${body}\\n}`;\n\treturn { historyParams, fn };\n}\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","dummyTexture","isMediaPipeSource","source","hashOptions","options","calculateBoundingBoxCenter","data","entityIdx","landmarkIndices","landmarkCount","offset","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","filesetPromise","getSharedFileset","FilesetResolver","generateGLSLFn","history","returnType","name","args","body","argsOnly","historyArgs","callArgs","MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","BLIT_FRAGMENT_SHADER","FULLSCREEN_TRIANGLES","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","RED_REGION_NAMES","GREEN_REGION_NAMES","BLUE_REGION_NAMES","CHANNEL_BIT_SCALE","GB_BITMASK_MAX_FACES","createChannelBitValues","names","name","normalizeChannelBitValues","bitValues","bitValue","RED_REGION_BIT_VALUES","GREEN_REGION_BIT_VALUES","BLUE_REGION_BIT_VALUES","RED_CHANNEL_VALUES","GREEN_CHANNEL_VALUES","BLUE_CHANNEL_VALUES","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","sharedDetectors","createProgram","gl","vertexSource","fragmentSource","vertexShader","fragmentShader","program","initMaskRenderer","canvas","regionProgram","blitProgram","regionPositionBuffer","regionPositionLocation","quadBuffer","blitPositionLocation","colorLocation","textureLocation","scratchTexture","scratchFramebuffer","resizeMaskRenderer","mask","width","height","drawRegionToScratch","landmarksData","faceRegion","faceIdx","g","b","baseIdx","vertices","landmarkIdx","accumulateScratch","updateLandmarksData","detector","faces","data","nFaces","landmarks","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMask","maxFaces","maskCanvas","useTesselationBitmask","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","updateTexturesInternal","existingDetector","destroyed","skipHistoryWrite","onResult","singleHistoryWriteIndex","nSlots","rowsToUpdate","historyWriteIndex","pendingBackfillSlots","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","faceLandmarker","initPromise","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","hasCalled","historyWriteCounter","writeToHistory","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","decodeFaceBitIndex","decodeFaceIndex","checkAt","fnName","regionNames","regionName","combineLeftRight","leftFn","rightFn","checkIn","fnNames","face_default"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts","../../src/plugins/mediapipe-common.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n\tv_uv = a_pos;\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\nconst BLIT_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nin vec2 v_uv;\nuniform sampler2D u_texture;\nout vec4 outColor;\nvoid main() { outColor = texture(u_texture, v_uv); }`;\nconst FULLSCREEN_TRIANGLES = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tMOUTH: MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n/* Face mask channel layout:\n- R: additive bitfield for face regions. Since it’s additive, it handles overlapping regions.\n- G/B: tessellation ownership. When maxFaces is <= 16, G stores one-hot bits for\n faces 0-7 and B stores one-hot bits for faces 8-15 so overlapping faces\n remain decodable. Above 16 maxFaces, B falls back to storing faceIndex + 1 as\n an additive byte value, which will overcount on overlaps. */\nconst RED_REGION_NAMES = [\n\t'OVAL',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst GREEN_REGION_NAMES = ['FACE_0', 'FACE_1', 'FACE_2', 'FACE_3', 'FACE_4', 'FACE_5', 'FACE_6', 'FACE_7'] as const;\nconst BLUE_REGION_NAMES = [\n\t'FACE_8',\n\t'FACE_9',\n\t'FACE_10',\n\t'FACE_11',\n\t'FACE_12',\n\t'FACE_13',\n\t'FACE_14',\n\t'FACE_15',\n] as const;\nconst CHANNEL_BIT_SCALE = 255;\nconst GB_BITMASK_MAX_FACES = GREEN_REGION_NAMES.length + BLUE_REGION_NAMES.length;\n\nfunction createChannelBitValues<const T extends readonly string[]>(names: T): Record<T[number], number> {\n\treturn Object.fromEntries(names.map((name, i) => [name, 1 << i])) as Record<T[number], number>;\n}\n\nfunction normalizeChannelBitValues<T extends Record<string, number>>(bitValues: T): T {\n\treturn Object.fromEntries(\n\t\tObject.entries(bitValues).map(([name, bitValue]) => [name, bitValue / CHANNEL_BIT_SCALE]),\n\t) as T;\n}\n\nconst RED_REGION_BIT_VALUES = createChannelBitValues(RED_REGION_NAMES);\nconst GREEN_REGION_BIT_VALUES = createChannelBitValues(GREEN_REGION_NAMES);\nconst BLUE_REGION_BIT_VALUES = createChannelBitValues(BLUE_REGION_NAMES);\nconst RED_CHANNEL_VALUES = normalizeChannelBitValues(RED_REGION_BIT_VALUES);\nconst GREEN_CHANNEL_VALUES = normalizeChannelBitValues(GREEN_REGION_BIT_VALUES);\nconst BLUE_CHANNEL_VALUES = normalizeChannelBitValues(BLUE_REGION_BIT_VALUES);\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { indices: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start,\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tMOUTH: fanTriangulate(MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, indices]) => [key, { indices, vertices: new Float32Array(indices.length * 2) }]),\n\t\t);\n\t}\n}\n\ninterface MaskRenderer {\n\tcanvas: OffscreenCanvas;\n\tgl: WebGL2RenderingContext;\n\tregionProgram: WebGLProgram;\n\tblitProgram: WebGLProgram;\n\tregionPositionBuffer: WebGLBuffer;\n\tquadBuffer: WebGLBuffer;\n\tregionPositionLocation: number;\n\tblitPositionLocation: number;\n\tcolorLocation: WebGLUniformLocation;\n\ttextureLocation: WebGLUniformLocation;\n\tscratchTexture: WebGLTexture;\n\tscratchFramebuffer: WebGLFramebuffer;\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tmask: MaskRenderer;\n\tsubscribers: Map<Function, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\tnCalls: number;\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram {\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, vertexSource);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, fragmentSource);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\treturn program;\n}\n\nfunction initMaskRenderer(canvas: OffscreenCanvas): MaskRenderer {\n\tconst gl = canvas.getContext('webgl2', {\n\t\tantialias: false,\n\t\tpreserveDrawingBuffer: true,\n\t})!;\n\tconst regionProgram = createProgram(gl, MASK_VERTEX_SHADER, MASK_FRAGMENT_SHADER);\n\tconst blitProgram = createProgram(gl, MASK_VERTEX_SHADER, BLIT_FRAGMENT_SHADER);\n\n\tconst regionPositionBuffer = gl.createBuffer()!;\n\tconst regionPositionLocation = gl.getAttribLocation(regionProgram, 'a_pos');\n\n\tconst quadBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_TRIANGLES, gl.STATIC_DRAW);\n\tconst blitPositionLocation = gl.getAttribLocation(blitProgram, 'a_pos');\n\n\tconst colorLocation = gl.getUniformLocation(regionProgram, 'u_color')!;\n\tconst textureLocation = gl.getUniformLocation(blitProgram, 'u_texture')!;\n\n\tconst scratchTexture = gl.createTexture()!;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n\n\tconst scratchFramebuffer = gl.createFramebuffer()!;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, scratchTexture, 0);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n\tgl.useProgram(blitProgram);\n\tgl.uniform1i(textureLocation, 0);\n\tgl.colorMask(true, true, true, false);\n\n\treturn {\n\t\tcanvas,\n\t\tgl,\n\t\tregionProgram,\n\t\tblitProgram,\n\t\tregionPositionBuffer,\n\t\tquadBuffer,\n\t\tregionPositionLocation,\n\t\tblitPositionLocation,\n\t\tcolorLocation,\n\t\ttextureLocation,\n\t\tscratchTexture,\n\t\tscratchFramebuffer,\n\t};\n}\n\nfunction resizeMaskRenderer(mask: MaskRenderer, width: number, height: number) {\n\tconst { gl, canvas, scratchTexture } = mask;\n\tif (canvas.width === width && canvas.height === height) return;\n\tcanvas.width = width;\n\tcanvas.height = height;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n}\n\nfunction drawRegionToScratch(\n\tmask: MaskRenderer,\n\tlandmarksData: Float32Array,\n\tfaceRegion: FaceRegion,\n\tfaceIdx: number,\n\tr: number,\n\tg: number,\n\tb: number,\n) {\n\tconst { gl, regionProgram, regionPositionBuffer, regionPositionLocation, colorLocation, scratchFramebuffer } = mask;\n\tconst baseIdx = N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT;\n\tconst { indices, vertices } = faceRegion;\n\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\tgl.useProgram(regionProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, regionPositionBuffer);\n\tgl.enableVertexAttribArray(regionPositionLocation);\n\tgl.vertexAttribPointer(regionPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\n\tfor (let i = 0; i < indices.length; ++i) {\n\t\tconst landmarkIdx = (baseIdx + indices[i]) * 4;\n\t\tvertices[i * 2] = landmarksData[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksData[landmarkIdx + 1];\n\t}\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, indices.length);\n}\n\nfunction accumulateScratch(mask: MaskRenderer) {\n\tconst { gl, blitProgram, quadBuffer, blitPositionLocation, scratchTexture } = mask;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.useProgram(blitProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.enableVertexAttribArray(blitPositionLocation);\n\tgl.vertexAttribPointer(blitPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.activeTexture(gl.TEXTURE0);\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.FUNC_ADD);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS,\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4,\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMask(detector: Detector, width: number, height: number) {\n\tconst {\n\t\tmask,\n\t\tmaxFaces,\n\t\tlandmarks,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl, canvas: maskCanvas } = mask;\n\tconst { data: landmarksData } = landmarks;\n\n\tresizeMaskRenderer(mask, width, height);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\n\tif (!faceRegions) return;\n\n\tconst useTesselationBitmask = maxFaces <= GB_BITMASK_MAX_FACES;\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst g =\n\t\t\tuseTesselationBitmask && faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? GREEN_CHANNEL_VALUES[GREEN_REGION_NAMES[faceIdx]]\n\t\t\t\t: 0;\n\t\tconst b = useTesselationBitmask\n\t\t\t? faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? 0\n\t\t\t\t: BLUE_CHANNEL_VALUES[BLUE_REGION_NAMES[faceIdx - GREEN_REGION_NAMES.length]]\n\t\t\t: (faceIdx + 1) / CHANNEL_BIT_SCALE;\n\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.TESSELATION, faceIdx, 0, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.OVAL, faceIdx, RED_CHANNEL_VALUES.OVAL, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.LEFT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.LEFT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.RIGHT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.RIGHT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.MOUTH, faceIdx, RED_CHANNEL_VALUES.MOUTH, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.INNER_MOUTH,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.INNER_MOUTH,\n\t\t\tg,\n\t\t\tb,\n\t\t);\n\t\taccumulateScratch(mask);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, emitHook, updateTexturesInternal } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet destroyed = false;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult(singleHistoryWriteIndex?: number) {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlet historyWriteIndex: number | number[] | undefined = singleHistoryWriteIndex;\n\t\t\tif (typeof historyWriteIndex === 'undefined' && pendingBackfillSlots.length > 0) {\n\t\t\t\thistoryWriteIndex = pendingBackfillSlots;\n\t\t\t\tpendingBackfillSlots = [];\n\t\t\t}\n\t\t\tupdateTexturesInternal(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\thistory ? { skipHistoryWrite, historyWriteIndex } : undefined,\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tif (destroyed) return;\n\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\t\t\t\tif (destroyed) {\n\t\t\t\t\tfaceLandmarker.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tmask: initMaskRenderer(maskCanvas),\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\tnCalls: 0,\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\t\t\tconst callOrder = ++detector.state.nCalls;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (!detector || callOrder !== detector.state.nCalls) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({\n\t\t\t\t\t\trunningMode: requiredMode,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tlet width: number, height: number;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\twidth = source.videoWidth;\n\t\t\t\t\t\theight = source.videoHeight;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\twidth = source.width;\n\t\t\t\t\t\theight = source.height;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMask(detector, width, height);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result) {\n\t\t\t\t\tfor (const [cb, hasCalled] of detector.subscribers.entries()) {\n\t\t\t\t\t\tif (!hasCalled) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('_init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: textureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: 'RGBA32F',\n\t\t\t\t\ttype: 'FLOAT',\n\t\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\t\thistory,\n\t\t\t\t},\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => {\n\t\t\t\tif (destroyed || !detector) return;\n\t\t\t\temitHook('face:ready');\n\t\t\t});\n\t\t});\n\n\t\tlet historyWriteCounter = 0;\n\t\tlet pendingBackfillSlots: number[] = [];\n\t\tconst writeToHistory = () => {\n\t\t\tif (!history) return;\n\t\t\tonResult(historyWriteCounter); // Write stale data immediately.\n\t\t\tpendingBackfillSlots.push(historyWriteCounter); // Queue up backfill with more recent data.\n\t\t\thistoryWriteCounter = (historyWriteCounter + 1) % (history + 1);\n\t\t};\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) {\n\t\t\t\twriteToHistory();\n\t\t\t\tdetectFaces(source);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tif (!skipHistoryWrite) writeToHistory();\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tdestroyed = true;\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.regionProgram);\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.blitProgram);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.regionPositionBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.quadBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteTexture(detector.mask.scratchTexture);\n\t\t\t\t\tdetector.mask.gl.deleteFramebuffer(detector.mask.scratchFramebuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\t\tconst decodeFaceBitIndex = Array.from(\n\t\t\t{ length: GB_BITMASK_MAX_FACES - 1 },\n\t\t\t(_, i) => `step(${2 ** (i + 1)}.0, faceBitF)`,\n\t\t).join(' + ');\n\t\tconst decodeFaceIndex =\n\t\t\toptions.maxFaces <= GB_BITMASK_MAX_FACES\n\t\t\t\t? `uint faceBits = (uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5) << 8) | uint(mask.g * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tuint faceBit = faceBits & (~faceBits + 1u);\n\tfloat faceBitF = float(faceBit);\n\tfloat hasFace = sign(faceBitF);\n\tfloat faceIndex = ${decodeFaceBitIndex} - (1.0 - hasFace);`\n\t\t\t\t: `float faceIndex = float(int(uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5)) - 1);`;\n\n\t\tconst checkAt = (fnName: string, ...regionNames: (keyof typeof RED_REGION_BIT_VALUES)[]) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\tuint bits = uint(mask.r * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tfloat hit = sign(float(bits & ${regionNames.reduce(\n\t\t(mask, regionName) => mask | RED_REGION_BIT_VALUES[regionName],\n\t\t0,\n\t)}u));\n\treturn vec2(hit, mix(-1.0, faceIndex, hit));`,\n\t\t\t);\n\n\t\tconst combineLeftRight = (fnName: string, leftFn: string, rightFn: string) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\tvec2 right = ${rightFn}(pos${historyParams});\n\treturn mix(right, left, left.x);`,\n\t\t\t);\n\n\t\tconst checkIn = (fnNames: string[]) =>\n\t\t\tfnNames\n\t\t\t\t.map(fnName =>\n\t\t\t\t\tfn(\n\t\t\t\t\t\t'float',\n\t\t\t\t\t\t`in${fnName[0].toUpperCase() + fnName.slice(1)}`,\n\t\t\t\t\t\t'vec2 pos',\n\t\t\t\t\t\t`vec2 a = ${fnName}At(pos${historyParams}); return step(0.0, a.y) * a.x;`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.join('\\n');\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform mediump sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`,\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`,\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${checkAt('leftEyebrow', 'LEFT_EYEBROW')}\n${checkAt('rightEyebrow', 'RIGHT_EYEBROW')}\n${checkAt('leftEye', 'LEFT_EYE')}\n${checkAt('rightEye', 'RIGHT_EYE')}\n${checkAt('lips', 'MOUTH')}\n${checkAt('mouth', 'MOUTH', 'INNER_MOUTH')}\n${checkAt('innerMouth', 'INNER_MOUTH')}\n${checkAt('faceOval', 'OVAL')}\n${fn(\n\t'vec2',\n\t'faceAt',\n\t'vec2 pos',\n\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\treturn vec2(step(0.0, faceIndex), faceIndex);`,\n)}\n${combineLeftRight('eye', 'leftEyeAt', 'rightEyeAt')}\n${combineLeftRight('eyebrow', 'leftEyebrowAt', 'rightEyebrowAt')}\n${checkIn(['eyebrow', 'eye', 'mouth', 'innerMouth', 'lips', 'face'])}`);\n\t};\n}\n\nexport default face;\n","import { TextureSource } from '..';\n\nexport const dummyTexture = { data: new Uint8Array(4), width: 1, height: 1 };\n\nexport type MediaPipeSource = HTMLVideoElement | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas;\n\nexport function isMediaPipeSource(source: TextureSource): source is MediaPipeSource {\n\treturn (\n\t\tsource instanceof HTMLVideoElement ||\n\t\tsource instanceof HTMLImageElement ||\n\t\tsource instanceof HTMLCanvasElement ||\n\t\tsource instanceof OffscreenCanvas\n\t);\n}\n\nexport function hashOptions(options: object): string {\n\treturn JSON.stringify(options, Object.keys(options).sort());\n}\n\nexport function calculateBoundingBoxCenter(\n\tdata: Float32Array,\n\tentityIdx: number,\n\tlandmarkIndices: readonly number[] | number[],\n\tlandmarkCount: number,\n\toffset: number = 0,\n): [number, number, number, number] {\n\tlet minX = Infinity,\n\t\tmaxX = -Infinity,\n\t\tminY = Infinity,\n\t\tmaxY = -Infinity,\n\t\tavgZ = 0,\n\t\tavgVisibility = 0;\n\n\tfor (const idx of landmarkIndices) {\n\t\tconst dataIdx = (offset + entityIdx * landmarkCount + idx) * 4;\n\t\tconst x = data[dataIdx];\n\t\tconst y = data[dataIdx + 1];\n\t\tminX = Math.min(minX, x);\n\t\tmaxX = Math.max(maxX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxY = Math.max(maxY, y);\n\t\tavgZ += data[dataIdx + 2];\n\t\tavgVisibility += data[dataIdx + 3];\n\t}\n\n\treturn [\n\t\t(minX + maxX) / 2,\n\t\t(minY + maxY) / 2,\n\t\tavgZ / landmarkIndices.length,\n\t\tavgVisibility / landmarkIndices.length,\n\t];\n}\n\nlet filesetPromise: Promise<any> | null = null;\nexport function getSharedFileset(): Promise<any> {\n\tif (!filesetPromise) {\n\t\tfilesetPromise = import('@mediapipe/tasks-vision').then(({ FilesetResolver }) =>\n\t\t\tFilesetResolver.forVisionTasks(\n\t\t\t\t`https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${__MEDIAPIPE_TASKS_VISION_VERSION__}/wasm`,\n\t\t\t),\n\t\t);\n\t}\n\treturn filesetPromise;\n}\n\nexport function generateGLSLFn(history: number | undefined) {\n\tconst historyParams = history ? ', framesAgo' : '';\n\tconst fn = history\n\t\t? (returnType: string, name: string, args: string, body: string) => {\n\t\t\t\tconst argsOnly = args.replace(/\\w+ /g, '');\n\t\t\t\tconst historyArgs = args ? `${args}, int framesAgo` : 'int framesAgo';\n\t\t\t\tconst callArgs = argsOnly ? `${argsOnly}, 0` : '0';\n\t\t\t\treturn `${returnType} ${name}(${historyArgs}) {\\n${body}\\n}\n${returnType} ${name}(${args}) { return ${name}(${callArgs}); }`;\n\t\t\t}\n\t\t: (returnType: string, name: string, args: string, body: string) =>\n\t\t\t\t`${returnType} ${name}(${args}) {\\n${body}\\n}`;\n\treturn { historyParams, fn };\n}\n"],"mappings":"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","names":["face_exports","__export","face_default","__toCommonJS","dummyTexture","isMediaPipeSource","source","hashOptions","options","calculateBoundingBoxCenter","data","entityIdx","landmarkIndices","landmarkCount","offset","minX","maxX","minY","maxY","avgZ","avgVisibility","idx","dataIdx","x","y","filesetPromise","getSharedFileset","FilesetResolver","generateGLSLFn","history","returnType","name","args","body","argsOnly","historyArgs","callArgs","MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","BLIT_FRAGMENT_SHADER","FULLSCREEN_TRIANGLES","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","RED_REGION_NAMES","GREEN_REGION_NAMES","BLUE_REGION_NAMES","CHANNEL_BIT_SCALE","GB_BITMASK_MAX_FACES","createChannelBitValues","names","name","normalizeChannelBitValues","bitValues","bitValue","RED_REGION_BIT_VALUES","GREEN_REGION_BIT_VALUES","BLUE_REGION_BIT_VALUES","RED_CHANNEL_VALUES","GREEN_CHANNEL_VALUES","BLUE_CHANNEL_VALUES","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","sharedDetectors","createProgram","gl","vertexSource","fragmentSource","vertexShader","fragmentShader","program","initMaskRenderer","canvas","regionProgram","blitProgram","regionPositionBuffer","regionPositionLocation","quadBuffer","blitPositionLocation","colorLocation","textureLocation","scratchTexture","scratchFramebuffer","resizeMaskRenderer","mask","width","height","drawRegionToScratch","landmarksData","faceRegion","faceIdx","g","b","baseIdx","vertices","landmarkIdx","accumulateScratch","updateLandmarksData","detector","faces","data","nFaces","landmarks","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMask","maxFaces","maskCanvas","useTesselationBitmask","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","updateTexturesInternal","existingDetector","destroyed","skipHistoryWrite","onResult","singleHistoryWriteIndex","nSlots","rowsToUpdate","historyWriteIndex","pendingBackfillSlots","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","faceLandmarker","initPromise","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","hasCalled","historyWriteCounter","writeToHistory","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","decodeFaceBitIndex","decodeFaceIndex","checkAt","fnName","regionNames","regionName","combineLeftRight","leftFn","rightFn","checkIn","fnNames","face_default"]}
@@ -13,17 +13,17 @@ precision mediump float;
13
13
  in vec2 v_uv;
14
14
  uniform sampler2D u_texture;
15
15
  out vec4 outColor;
16
- void main() { outColor = texture(u_texture, v_uv); }`,Le=new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),H=478,pe=2,F=H+pe,x=512,p=1,fe=[336,296,334,293,300,276,283,282,295,285],le=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],me=[70,63,105,66,107,55,65,52,53,46],de=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],_e=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],j=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],he=Array.from({length:H},(i,e)=>e),I={LEFT_EYEBROW:fe,LEFT_EYE:le,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:me,RIGHT_EYE:de,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:_e,INNER_MOUTH:j,FACE_CENTER:H,MOUTH_CENTER:H+1},Ne=["OVAL","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],D=["FACE_0","FACE_1","FACE_2","FACE_3","FACE_4","FACE_5","FACE_6","FACE_7"],q=["FACE_8","FACE_9","FACE_10","FACE_11","FACE_12","FACE_13","FACE_14","FACE_15"],v=255,z=D.length+q.length;function J(i){return Object.fromEntries(i.map((e,n)=>[e,1<<n]))}function Q(i){return Object.fromEntries(Object.entries(i).map(([e,n])=>[e,n/v]))}var Te=J(Ne),xe=J(D),Ce=J(q),C=Q(Te),Me=Q(xe),Ie=Q(Ce),ke={modelPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task",maxFaces:1,minFaceDetectionConfidence:.5,minFacePresenceConfidence:.5,minTrackingConfidence:.5,outputFaceBlendshapes:!1,outputFacialTransformationMatrixes:!1};function M(i){let e=[];for(let n=1;n<i.length-1;++n)e.push(i[0],i[n],i[n+1]);return e}var _=null;function Oe(i){if(!_){let e=i.FACE_LANDMARKS_TESSELATION,n=[];for(let a=0;a<e.length-2;a+=3)n.push(e[a].start,e[a+1].start,e[a+2].start);let r=i.FACE_LANDMARKS_FACE_OVAL.map(({start:a})=>a);_=Object.fromEntries(Object.entries({LEFT_EYEBROW:M(fe),RIGHT_EYEBROW:M(me),LEFT_EYE:M(le),RIGHT_EYE:M(de),MOUTH:M(_e),INNER_MOUTH:M(j),TESSELATION:n,OVAL:M(r)}).map(([a,c])=>[a,{indices:c,vertices:new Float32Array(c.length*2)}]))}}var P=new Map;function ue(i,e,n){let r=i.createShader(i.VERTEX_SHADER);i.shaderSource(r,e),i.compileShader(r);let a=i.createShader(i.FRAGMENT_SHADER);i.shaderSource(a,n),i.compileShader(a);let c=i.createProgram();return i.attachShader(c,r),i.attachShader(c,a),i.linkProgram(c),i.deleteShader(r),i.deleteShader(a),c}function Se(i){let e=i.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),n=ue(e,Ee,ge),r=ue(e,Ee,be),a=e.createBuffer(),c=e.getAttribLocation(n,"a_pos"),T=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,T),e.bufferData(e.ARRAY_BUFFER,Le,e.STATIC_DRAW);let o=e.getAttribLocation(r,"a_pos"),f=e.getUniformLocation(n,"u_color"),l=e.getUniformLocation(r,"u_texture"),m=e.createTexture();e.bindTexture(e.TEXTURE_2D,m),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,null);let u=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,m,0),e.bindFramebuffer(e.FRAMEBUFFER,null),e.useProgram(r),e.uniform1i(l,0),e.colorMask(!0,!0,!0,!1),{canvas:i,gl:e,regionProgram:n,blitProgram:r,regionPositionBuffer:a,quadBuffer:T,regionPositionLocation:c,blitPositionLocation:o,colorLocation:f,textureLocation:l,scratchTexture:m,scratchFramebuffer:u}}function Be(i,e,n){let{gl:r,canvas:a,scratchTexture:c}=i;a.width===e&&a.height===n||(a.width=e,a.height=n,r.bindTexture(r.TEXTURE_2D,c),r.texImage2D(r.TEXTURE_2D,0,r.RGBA,e,n,0,r.RGBA,r.UNSIGNED_BYTE,null))}function b(i,e,n,r,a,c,T){let{gl:o,regionProgram:f,regionPositionBuffer:l,regionPositionLocation:m,colorLocation:u,scratchFramebuffer:U}=i,k=p+r*F,{indices:O,vertices:S}=n;o.bindFramebuffer(o.FRAMEBUFFER,U),o.viewport(0,0,i.canvas.width,i.canvas.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),o.useProgram(f),o.bindBuffer(o.ARRAY_BUFFER,l),o.enableVertexAttribArray(m),o.vertexAttribPointer(m,2,o.FLOAT,!1,0,0),o.enable(o.BLEND),o.blendEquation(o.MAX),o.blendFunc(o.ONE,o.ONE);for(let t=0;t<O.length;++t){let h=(k+O[t])*4;S[t*2]=e[h],S[t*2+1]=e[h+1]}o.bufferData(o.ARRAY_BUFFER,S,o.DYNAMIC_DRAW),o.uniform4f(u,a,c,T,1),o.drawArrays(o.TRIANGLES,0,O.length)}function L(i){let{gl:e,blitProgram:n,quadBuffer:r,blitPositionLocation:a,scratchTexture:c}=i;e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,i.canvas.width,i.canvas.height),e.useProgram(n),e.bindBuffer(e.ARRAY_BUFFER,r),e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,0,0),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,c),e.enable(e.BLEND),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE),e.drawArrays(e.TRIANGLES,0,6)}function De(i,e){let n=i.landmarks.data,r=e.length;n[0]=r;for(let a=0;a<r;++a){let c=e[a];for(let f=0;f<H;++f){let l=c[f],m=(p+a*F+f)*4;n[m]=l.x,n[m+1]=1-l.y,n[m+2]=l.z??0,n[m+3]=l.visibility??1}let T=K(n,a,he,F,p);n.set(T,(p+a*F+I.FACE_CENTER)*4);let o=K(n,a,j,F,1);n.set(o,(p+a*F+I.MOUTH_CENTER)*4)}i.state.nFaces=r}function ve(i,e,n){let{mask:r,maxFaces:a,landmarks:c,state:{nFaces:T}}=i,{gl:o,canvas:f}=r,{data:l}=c;if(Be(r,e,n),o.bindFramebuffer(o.FRAMEBUFFER,null),o.viewport(0,0,f.width,f.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),!_)return;let m=a<=z;for(let u=0;u<T;++u){let U=m&&u<D.length?Me[D[u]]:0,k=m?u<D.length?0:Ie[q[u-D.length]]:(u+1)/v;b(r,l,_.TESSELATION,u,0,U,k),L(r),b(r,l,_.OVAL,u,C.OVAL,0,0),L(r),b(r,l,_.LEFT_EYEBROW,u,C.LEFT_EYEBROW,0,0),L(r),b(r,l,_.RIGHT_EYEBROW,u,C.RIGHT_EYEBROW,0,0),L(r),b(r,l,_.LEFT_EYE,u,C.LEFT_EYE,0,0),L(r),b(r,l,_.RIGHT_EYE,u,C.RIGHT_EYE,0,0),L(r),b(r,l,_.MOUTH,u,C.MOUTH,0,0),L(r),b(r,l,_.INNER_MOUTH,u,C.INNER_MOUTH,0,0),L(r)}}function Ue(i){let{textureName:e,options:{history:n,...r}={}}=i,a={...ke,...r},c=oe({...a,textureName:e}),T=a.maxFaces*F+p,o=Math.ceil(T/x);return function(f,l){let{injectGLSL:m,emitHook:u,updateTexturesInternal:U}=l,k=P.get(c),O=k?.landmarks.data??new Float32Array(x*o*4),S=k?.mask.canvas??new OffscreenCanvas(1,1),t=null,h=!1,Y=!1;function w(s){if(!t)return;let E=t.state.nFaces,d=E*F+p,N=Math.ceil(d/x),A=s;typeof A>"u"&&$.length>0&&(A=$,$=[]),U({u_faceLandmarksTex:{data:t.landmarks.data,width:x,height:N,isPartial:!0},u_faceMask:t.mask.canvas},n?{skipHistoryWrite:Y,historyWriteIndex:A}:void 0),f.updateUniforms({u_nFaces:E}),u("face:result",t.state.result)}async function Fe(){if(P.has(c))t=P.get(c);else{let[s,{FaceLandmarker:E}]=await Promise.all([se(),import("@mediapipe/tasks-vision")]);if(h)return;let d=await E.createFromOptions(s,{baseOptions:{modelAssetPath:a.modelPath,delegate:"GPU"},runningMode:"VIDEO",numFaces:a.maxFaces,minFaceDetectionConfidence:a.minFaceDetectionConfidence,minFacePresenceConfidence:a.minFacePresenceConfidence,minTrackingConfidence:a.minTrackingConfidence,outputFaceBlendshapes:a.outputFaceBlendshapes,outputFacialTransformationMatrixes:a.outputFacialTransformationMatrixes});if(h){d.close();return}t={landmarker:d,mask:Se(S),subscribers:new Map,maxFaces:a.maxFaces,state:{nCalls:0,runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:O,textureHeight:o}},Oe(E),P.set(c,t)}t.subscribers.set(w,!1)}let Z=Fe();async function ee(s){let E=performance.now();if(await Z,!t)return;let d=++t.state.nCalls;t.state.pending=t.state.pending.then(async()=>{if(!t||d!==t.state.nCalls)return;let N=s instanceof HTMLVideoElement?"VIDEO":"IMAGE";t.state.runningMode!==N&&(t.state.runningMode=N,await t.landmarker.setOptions({runningMode:N}));let A=!1;if(s!==t.state.source?(t.state.source=s,t.state.videoTime=-1,A=!0):s instanceof HTMLVideoElement?s.currentTime!==t.state.videoTime&&(t.state.videoTime=s.currentTime,A=!0):s instanceof HTMLImageElement||E-t.state.resultTimestamp>2&&(A=!0),A){let g,y,V;if(s instanceof HTMLVideoElement){if(s.videoWidth===0||s.videoHeight===0||s.readyState<2)return;y=s.videoWidth,V=s.videoHeight,g=t.landmarker.detectForVideo(s,E)}else{if(s.width===0||s.height===0)return;y=s.width,V=s.height,g=t.landmarker.detect(s)}if(g){t.state.resultTimestamp=E,t.state.result=g,De(t,g.faceLandmarks),ve(t,y,V);for(let ie of t.subscribers.keys())ie(),t.subscribers.set(ie,!0)}}else if(t.state.result)for(let[g,y]of t.subscribers.entries())y||(g(),t.subscribers.set(g,!0))}),await t.state.pending}f.on("_init",()=>{f.initializeUniform("u_maxFaces","int",a.maxFaces),f.initializeUniform("u_nFaces","int",0),f.initializeTexture("u_faceLandmarksTex",{data:O,width:x,height:o},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:n}),f.initializeTexture("u_faceMask",S,{minFilter:"NEAREST",magFilter:"NEAREST",history:n}),Z.then(()=>{h||!t||u("face:ready")})});let G=0,$=[],te=()=>{n&&(w(G),$.push(G),G=(G+1)%(n+1))};f.on("initializeTexture",(s,E)=>{s===e&&X(E)&&(te(),ee(E))}),f.on("updateTextures",(s,E)=>{let d=s[e];X(d)&&(Y=E?.skipHistoryWrite??!1,Y||te(),ee(d))}),f.on("destroy",()=>{h=!0,t&&(t.subscribers.delete(w),t.subscribers.size===0&&(t.landmarker.close(),t.mask.gl.deleteProgram(t.mask.regionProgram),t.mask.gl.deleteProgram(t.mask.blitProgram),t.mask.gl.deleteBuffer(t.mask.regionPositionBuffer),t.mask.gl.deleteBuffer(t.mask.quadBuffer),t.mask.gl.deleteTexture(t.mask.scratchTexture),t.mask.gl.deleteFramebuffer(t.mask.scratchFramebuffer),P.delete(c))),t=null});let{fn:B,historyParams:W}=ce(n),ne=n?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",Re=Array.from({length:z-1},(s,E)=>`step(${2**(E+1)}.0, faceBitF)`).join(" + "),ae=a.maxFaces<=z?`uint faceBits = (uint(mask.b * ${v}.0 + 0.5) << 8) | uint(mask.g * ${v}.0 + 0.5);
16
+ void main() { outColor = texture(u_texture, v_uv); }`,Le=new Float32Array([0,0,1,0,0,1,0,1,1,0,1,1]),H=478,pe=2,R=H+pe,M=512,N=1,fe=[336,296,334,293,300,276,283,282,295,285],le=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],me=[70,63,105,66,107,55,65,52,53,46],de=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],_e=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],j=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],he=Array.from({length:H},(i,e)=>e),O={LEFT_EYEBROW:fe,LEFT_EYE:le,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:me,RIGHT_EYE:de,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:_e,INNER_MOUTH:j,FACE_CENTER:H,MOUTH_CENTER:H+1},Ne=["OVAL","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],v=["FACE_0","FACE_1","FACE_2","FACE_3","FACE_4","FACE_5","FACE_6","FACE_7"],q=["FACE_8","FACE_9","FACE_10","FACE_11","FACE_12","FACE_13","FACE_14","FACE_15"],U=255,z=v.length+q.length;function J(i){return Object.fromEntries(i.map((e,n)=>[e,1<<n]))}function Q(i){return Object.fromEntries(Object.entries(i).map(([e,n])=>[e,n/U]))}var Te=J(Ne),xe=J(v),Ce=J(q),I=Q(Te),Me=Q(xe),Ie=Q(Ce),ke={modelPath:"https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task",maxFaces:1,minFaceDetectionConfidence:.5,minFacePresenceConfidence:.5,minTrackingConfidence:.5,outputFaceBlendshapes:!1,outputFacialTransformationMatrixes:!1};function k(i){let e=[];for(let n=1;n<i.length-1;++n)e.push(i[0],i[n],i[n+1]);return e}var _=null;function Oe(i){if(!_){let e=i.FACE_LANDMARKS_TESSELATION,n=[];for(let a=0;a<e.length-2;a+=3)n.push(e[a].start,e[a+1].start,e[a+2].start);let r=i.FACE_LANDMARKS_FACE_OVAL.map(({start:a})=>a);_=Object.fromEntries(Object.entries({LEFT_EYEBROW:k(fe),RIGHT_EYEBROW:k(me),LEFT_EYE:k(le),RIGHT_EYE:k(de),MOUTH:k(_e),INNER_MOUTH:k(j),TESSELATION:n,OVAL:k(r)}).map(([a,c])=>[a,{indices:c,vertices:new Float32Array(c.length*2)}]))}}var P=new Map;function ue(i,e,n){let r=i.createShader(i.VERTEX_SHADER);i.shaderSource(r,e),i.compileShader(r);let a=i.createShader(i.FRAGMENT_SHADER);i.shaderSource(a,n),i.compileShader(a);let c=i.createProgram();return i.attachShader(c,r),i.attachShader(c,a),i.linkProgram(c),i.deleteShader(r),i.deleteShader(a),c}function Se(i){let e=i.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),n=ue(e,Ee,ge),r=ue(e,Ee,be),a=e.createBuffer(),c=e.getAttribLocation(n,"a_pos"),T=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,T),e.bufferData(e.ARRAY_BUFFER,Le,e.STATIC_DRAW);let o=e.getAttribLocation(r,"a_pos"),f=e.getUniformLocation(n,"u_color"),l=e.getUniformLocation(r,"u_texture"),m=e.createTexture();e.bindTexture(e.TEXTURE_2D,m),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,null);let u=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,u),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,m,0),e.bindFramebuffer(e.FRAMEBUFFER,null),e.useProgram(r),e.uniform1i(l,0),e.colorMask(!0,!0,!0,!1),{canvas:i,gl:e,regionProgram:n,blitProgram:r,regionPositionBuffer:a,quadBuffer:T,regionPositionLocation:c,blitPositionLocation:o,colorLocation:f,textureLocation:l,scratchTexture:m,scratchFramebuffer:u}}function Be(i,e,n){let{gl:r,canvas:a,scratchTexture:c}=i;a.width===e&&a.height===n||(a.width=e,a.height=n,r.bindTexture(r.TEXTURE_2D,c),r.texImage2D(r.TEXTURE_2D,0,r.RGBA,e,n,0,r.RGBA,r.UNSIGNED_BYTE,null))}function p(i,e,n,r,a,c,T){let{gl:o,regionProgram:f,regionPositionBuffer:l,regionPositionLocation:m,colorLocation:u,scratchFramebuffer:A}=i,F=N+r*R,{indices:S,vertices:B}=n;o.bindFramebuffer(o.FRAMEBUFFER,A),o.viewport(0,0,i.canvas.width,i.canvas.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),o.useProgram(f),o.bindBuffer(o.ARRAY_BUFFER,l),o.enableVertexAttribArray(m),o.vertexAttribPointer(m,2,o.FLOAT,!1,0,0),o.enable(o.BLEND),o.blendEquation(o.MAX),o.blendFunc(o.ONE,o.ONE);for(let t=0;t<S.length;++t){let x=(F+S[t])*4;B[t*2]=e[x],B[t*2+1]=e[x+1]}o.bufferData(o.ARRAY_BUFFER,B,o.DYNAMIC_DRAW),o.uniform4f(u,a,c,T,1),o.drawArrays(o.TRIANGLES,0,S.length)}function h(i){let{gl:e,blitProgram:n,quadBuffer:r,blitPositionLocation:a,scratchTexture:c}=i;e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,i.canvas.width,i.canvas.height),e.useProgram(n),e.bindBuffer(e.ARRAY_BUFFER,r),e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,0,0),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,c),e.enable(e.BLEND),e.blendEquation(e.FUNC_ADD),e.blendFunc(e.ONE,e.ONE),e.drawArrays(e.TRIANGLES,0,6)}function De(i,e){let n=i.landmarks.data,r=e.length;n[0]=r;for(let a=0;a<r;++a){let c=e[a];for(let f=0;f<H;++f){let l=c[f],m=(N+a*R+f)*4;n[m]=l.x,n[m+1]=1-l.y,n[m+2]=l.z??0,n[m+3]=l.visibility??1}let T=K(n,a,he,R,N);n.set(T,(N+a*R+O.FACE_CENTER)*4);let o=K(n,a,j,R,1);n.set(o,(N+a*R+O.MOUTH_CENTER)*4)}i.state.nFaces=r}function ve(i,e,n){let{mask:r,maxFaces:a,landmarks:c,state:{nFaces:T}}=i,{gl:o,canvas:f}=r,{data:l}=c;if(Be(r,e,n),o.bindFramebuffer(o.FRAMEBUFFER,null),o.viewport(0,0,f.width,f.height),o.clearColor(0,0,0,0),o.clear(o.COLOR_BUFFER_BIT),!_)return;let m=a<=z;for(let u=0;u<T;++u){let A=m&&u<v.length?Me[v[u]]:0,F=m?u<v.length?0:Ie[q[u-v.length]]:(u+1)/U;p(r,l,_.TESSELATION,u,0,A,F),h(r),p(r,l,_.OVAL,u,I.OVAL,0,0),h(r),p(r,l,_.LEFT_EYEBROW,u,I.LEFT_EYEBROW,0,0),h(r),p(r,l,_.RIGHT_EYEBROW,u,I.RIGHT_EYEBROW,0,0),h(r),p(r,l,_.LEFT_EYE,u,I.LEFT_EYE,A,F),h(r),p(r,l,_.RIGHT_EYE,u,I.RIGHT_EYE,A,F),h(r),p(r,l,_.MOUTH,u,I.MOUTH,0,0),h(r),p(r,l,_.INNER_MOUTH,u,I.INNER_MOUTH,A,F),h(r)}}function Ue(i){let{textureName:e,options:{history:n,...r}={}}=i,a={...ke,...r},c=oe({...a,textureName:e}),T=a.maxFaces*R+N,o=Math.ceil(T/M);return function(f,l){let{injectGLSL:m,emitHook:u,updateTexturesInternal:A}=l,F=P.get(c),S=F?.landmarks.data??new Float32Array(M*o*4),B=F?.mask.canvas??new OffscreenCanvas(1,1),t=null,x=!1,Y=!1;function w(s){if(!t)return;let E=t.state.nFaces,d=E*R+N,C=Math.ceil(d/M),b=s;typeof b>"u"&&$.length>0&&(b=$,$=[]),A({u_faceLandmarksTex:{data:t.landmarks.data,width:M,height:C,isPartial:!0},u_faceMask:t.mask.canvas},n?{skipHistoryWrite:Y,historyWriteIndex:b}:void 0),f.updateUniforms({u_nFaces:E}),u("face:result",t.state.result)}async function Fe(){if(P.has(c))t=P.get(c);else{let[s,{FaceLandmarker:E}]=await Promise.all([se(),import("@mediapipe/tasks-vision")]);if(x)return;let d=await E.createFromOptions(s,{baseOptions:{modelAssetPath:a.modelPath,delegate:"GPU"},runningMode:"VIDEO",numFaces:a.maxFaces,minFaceDetectionConfidence:a.minFaceDetectionConfidence,minFacePresenceConfidence:a.minFacePresenceConfidence,minTrackingConfidence:a.minTrackingConfidence,outputFaceBlendshapes:a.outputFaceBlendshapes,outputFacialTransformationMatrixes:a.outputFacialTransformationMatrixes});if(x){d.close();return}t={landmarker:d,mask:Se(B),subscribers:new Map,maxFaces:a.maxFaces,state:{nCalls:0,runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:S,textureHeight:o}},Oe(E),P.set(c,t)}t.subscribers.set(w,!1)}let Z=Fe();async function ee(s){let E=performance.now();if(await Z,!t)return;let d=++t.state.nCalls;t.state.pending=t.state.pending.then(async()=>{if(!t||d!==t.state.nCalls)return;let C=s instanceof HTMLVideoElement?"VIDEO":"IMAGE";t.state.runningMode!==C&&(t.state.runningMode=C,await t.landmarker.setOptions({runningMode:C}));let b=!1;if(s!==t.state.source?(t.state.source=s,t.state.videoTime=-1,b=!0):s instanceof HTMLVideoElement?s.currentTime!==t.state.videoTime&&(t.state.videoTime=s.currentTime,b=!0):s instanceof HTMLImageElement||E-t.state.resultTimestamp>2&&(b=!0),b){let L,y,V;if(s instanceof HTMLVideoElement){if(s.videoWidth===0||s.videoHeight===0||s.readyState<2)return;y=s.videoWidth,V=s.videoHeight,L=t.landmarker.detectForVideo(s,E)}else{if(s.width===0||s.height===0)return;y=s.width,V=s.height,L=t.landmarker.detect(s)}if(L){t.state.resultTimestamp=E,t.state.result=L,De(t,L.faceLandmarks),ve(t,y,V);for(let ie of t.subscribers.keys())ie(),t.subscribers.set(ie,!0)}}else if(t.state.result)for(let[L,y]of t.subscribers.entries())y||(L(),t.subscribers.set(L,!0))}),await t.state.pending}f.on("_init",()=>{f.initializeUniform("u_maxFaces","int",a.maxFaces),f.initializeUniform("u_nFaces","int",0),f.initializeTexture("u_faceLandmarksTex",{data:S,width:M,height:o},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:n}),f.initializeTexture("u_faceMask",B,{minFilter:"NEAREST",magFilter:"NEAREST",history:n}),Z.then(()=>{x||!t||u("face:ready")})});let G=0,$=[],te=()=>{n&&(w(G),$.push(G),G=(G+1)%(n+1))};f.on("initializeTexture",(s,E)=>{s===e&&X(E)&&(te(),ee(E))}),f.on("updateTextures",(s,E)=>{let d=s[e];X(d)&&(Y=E?.skipHistoryWrite??!1,Y||te(),ee(d))}),f.on("destroy",()=>{x=!0,t&&(t.subscribers.delete(w),t.subscribers.size===0&&(t.landmarker.close(),t.mask.gl.deleteProgram(t.mask.regionProgram),t.mask.gl.deleteProgram(t.mask.blitProgram),t.mask.gl.deleteBuffer(t.mask.regionPositionBuffer),t.mask.gl.deleteBuffer(t.mask.quadBuffer),t.mask.gl.deleteTexture(t.mask.scratchTexture),t.mask.gl.deleteFramebuffer(t.mask.scratchFramebuffer),P.delete(c))),t=null});let{fn:D,historyParams:W}=ce(n),ne=n?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",Re=Array.from({length:z-1},(s,E)=>`step(${2**(E+1)}.0, faceBitF)`).join(" + "),ae=a.maxFaces<=z?`uint faceBits = (uint(mask.b * ${U}.0 + 0.5) << 8) | uint(mask.g * ${U}.0 + 0.5);
17
17
  uint faceBit = faceBits & (~faceBits + 1u);
18
18
  float faceBitF = float(faceBit);
19
19
  float hasFace = sign(faceBitF);
20
- float faceIndex = ${Re} - (1.0 - hasFace);`:`float faceIndex = float(int(uint(mask.b * ${v}.0 + 0.5)) - 1);`,R=(s,...E)=>B("vec2",`${s}At`,"vec2 pos",`vec4 mask = ${ne};
20
+ float faceIndex = ${Re} - (1.0 - hasFace);`:`float faceIndex = float(int(uint(mask.b * ${U}.0 + 0.5)) - 1);`,g=(s,...E)=>D("vec2",`${s}At`,"vec2 pos",`vec4 mask = ${ne};
21
21
  ${ae}
22
- uint bits = uint(mask.r * ${v}.0 + 0.5);
23
- float hit = sign(float(bits & ${E.reduce((d,N)=>d|Te[N],0)}u));
24
- return vec2(hit, mix(-1.0, faceIndex, hit));`),re=(s,E,d)=>B("vec2",`${s}At`,"vec2 pos",`vec2 left = ${E}(pos${W});
22
+ uint bits = uint(mask.r * ${U}.0 + 0.5);
23
+ float hit = sign(float(bits & ${E.reduce((d,C)=>d|Te[C],0)}u));
24
+ return vec2(hit, mix(-1.0, faceIndex, hit));`),re=(s,E,d)=>D("vec2",`${s}At`,"vec2 pos",`vec2 left = ${E}(pos${W});
25
25
  vec2 right = ${d}(pos${W});
26
- return mix(right, left, left.x);`),Ae=s=>s.map(E=>B("float",`in${E[0].toUpperCase()+E.slice(1)}`,"vec2 pos",`vec2 a = ${E}At(pos${W}); return step(0.0, a.y) * a.x;`)).join(`
26
+ return mix(right, left, left.x);`),Ae=s=>s.map(E=>D("float",`in${E[0].toUpperCase()+E.slice(1)}`,"vec2 pos",`vec2 a = ${E}At(pos${W}); return step(0.0, a.y) * a.x;`)).join(`
27
27
  `);m(`
28
28
  uniform int u_maxFaces;
29
29
  uniform int u_nFaces;
@@ -32,19 +32,19 @@ uniform int u_faceLandmarksTexFrameOffset;`:""}
32
32
  uniform mediump sampler2D${n?"Array":""} u_faceMask;${n?`
33
33
  uniform int u_faceMaskFrameOffset;`:""}
34
34
 
35
- #define FACE_LANDMARK_L_EYE_CENTER ${I.LEFT_EYE_CENTER}
36
- #define FACE_LANDMARK_R_EYE_CENTER ${I.RIGHT_EYE_CENTER}
37
- #define FACE_LANDMARK_NOSE_TIP ${I.NOSE_TIP}
38
- #define FACE_LANDMARK_FACE_CENTER ${I.FACE_CENTER}
39
- #define FACE_LANDMARK_MOUTH_CENTER ${I.MOUTH_CENTER}
35
+ #define FACE_LANDMARK_L_EYE_CENTER ${O.LEFT_EYE_CENTER}
36
+ #define FACE_LANDMARK_R_EYE_CENTER ${O.RIGHT_EYE_CENTER}
37
+ #define FACE_LANDMARK_NOSE_TIP ${O.NOSE_TIP}
38
+ #define FACE_LANDMARK_FACE_CENTER ${O.FACE_CENTER}
39
+ #define FACE_LANDMARK_MOUTH_CENTER ${O.MOUTH_CENTER}
40
40
 
41
- ${B("int","nFacesAt","",n?`
41
+ ${D("int","nFacesAt","",n?`
42
42
  int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
43
43
  return int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
44
44
  return int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
45
- ${B("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${p} + faceIndex * ${F} + landmarkIndex;
46
- int x = i % ${x};
47
- int y = i / ${x};${n?`
45
+ ${D("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${N} + faceIndex * ${R} + landmarkIndex;
46
+ int x = i % ${M};
47
+ int y = i / ${M};${n?`
48
48
  int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
49
49
  return texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`:`
50
50
  return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`}`)}
@@ -54,15 +54,15 @@ vec4 _sampleFaceMask(vec2 pos, int framesAgo) {
54
54
  return texture(u_faceMask, vec3(pos, float(layer)));
55
55
  }
56
56
  `:""}
57
- ${R("leftEyebrow","LEFT_EYEBROW")}
58
- ${R("rightEyebrow","RIGHT_EYEBROW")}
59
- ${R("leftEye","LEFT_EYE")}
60
- ${R("rightEye","RIGHT_EYE")}
61
- ${R("lips","MOUTH")}
62
- ${R("mouth","MOUTH","INNER_MOUTH")}
63
- ${R("innerMouth","INNER_MOUTH")}
64
- ${R("faceOval","OVAL")}
65
- ${B("vec2","faceAt","vec2 pos",`vec4 mask = ${ne};
57
+ ${g("leftEyebrow","LEFT_EYEBROW")}
58
+ ${g("rightEyebrow","RIGHT_EYEBROW")}
59
+ ${g("leftEye","LEFT_EYE")}
60
+ ${g("rightEye","RIGHT_EYE")}
61
+ ${g("lips","MOUTH")}
62
+ ${g("mouth","MOUTH","INNER_MOUTH")}
63
+ ${g("innerMouth","INNER_MOUTH")}
64
+ ${g("faceOval","OVAL")}
65
+ ${D("vec2","faceAt","vec2 pos",`vec4 mask = ${ne};
66
66
  ${ae}
67
67
  return vec2(step(0.0, faceIndex), faceIndex);`)}
68
68
  ${re("eye","leftEyeAt","rightEyeAt")}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n\tv_uv = a_pos;\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\nconst BLIT_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nin vec2 v_uv;\nuniform sampler2D u_texture;\nout vec4 outColor;\nvoid main() { outColor = texture(u_texture, v_uv); }`;\nconst FULLSCREEN_TRIANGLES = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tMOUTH: MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n/* Face mask channel layout:\n- R: additive bitfield for face regions. Since it’s additive, it handles overlapping regions.\n- G/B: tessellation ownership. When maxFaces is <= 16, G stores one-hot bits for\n faces 0-7 and B stores one-hot bits for faces 8-15 so overlapping faces\n remain decodable. Above 16 maxFaces, B falls back to storing faceIndex + 1 as\n an additive byte value, which will overcount on overlaps. */\nconst RED_REGION_NAMES = [\n\t'OVAL',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst GREEN_REGION_NAMES = ['FACE_0', 'FACE_1', 'FACE_2', 'FACE_3', 'FACE_4', 'FACE_5', 'FACE_6', 'FACE_7'] as const;\nconst BLUE_REGION_NAMES = [\n\t'FACE_8',\n\t'FACE_9',\n\t'FACE_10',\n\t'FACE_11',\n\t'FACE_12',\n\t'FACE_13',\n\t'FACE_14',\n\t'FACE_15',\n] as const;\nconst CHANNEL_BIT_SCALE = 255;\nconst GB_BITMASK_MAX_FACES = GREEN_REGION_NAMES.length + BLUE_REGION_NAMES.length;\n\nfunction createChannelBitValues<const T extends readonly string[]>(names: T): Record<T[number], number> {\n\treturn Object.fromEntries(names.map((name, i) => [name, 1 << i])) as Record<T[number], number>;\n}\n\nfunction normalizeChannelBitValues<T extends Record<string, number>>(bitValues: T): T {\n\treturn Object.fromEntries(\n\t\tObject.entries(bitValues).map(([name, bitValue]) => [name, bitValue / CHANNEL_BIT_SCALE]),\n\t) as T;\n}\n\nconst RED_REGION_BIT_VALUES = createChannelBitValues(RED_REGION_NAMES);\nconst GREEN_REGION_BIT_VALUES = createChannelBitValues(GREEN_REGION_NAMES);\nconst BLUE_REGION_BIT_VALUES = createChannelBitValues(BLUE_REGION_NAMES);\nconst RED_CHANNEL_VALUES = normalizeChannelBitValues(RED_REGION_BIT_VALUES);\nconst GREEN_CHANNEL_VALUES = normalizeChannelBitValues(GREEN_REGION_BIT_VALUES);\nconst BLUE_CHANNEL_VALUES = normalizeChannelBitValues(BLUE_REGION_BIT_VALUES);\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { indices: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start,\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tMOUTH: fanTriangulate(MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, indices]) => [key, { indices, vertices: new Float32Array(indices.length * 2) }]),\n\t\t);\n\t}\n}\n\ninterface MaskRenderer {\n\tcanvas: OffscreenCanvas;\n\tgl: WebGL2RenderingContext;\n\tregionProgram: WebGLProgram;\n\tblitProgram: WebGLProgram;\n\tregionPositionBuffer: WebGLBuffer;\n\tquadBuffer: WebGLBuffer;\n\tregionPositionLocation: number;\n\tblitPositionLocation: number;\n\tcolorLocation: WebGLUniformLocation;\n\ttextureLocation: WebGLUniformLocation;\n\tscratchTexture: WebGLTexture;\n\tscratchFramebuffer: WebGLFramebuffer;\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tmask: MaskRenderer;\n\tsubscribers: Map<Function, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\tnCalls: number;\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram {\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, vertexSource);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, fragmentSource);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\treturn program;\n}\n\nfunction initMaskRenderer(canvas: OffscreenCanvas): MaskRenderer {\n\tconst gl = canvas.getContext('webgl2', {\n\t\tantialias: false,\n\t\tpreserveDrawingBuffer: true,\n\t})!;\n\tconst regionProgram = createProgram(gl, MASK_VERTEX_SHADER, MASK_FRAGMENT_SHADER);\n\tconst blitProgram = createProgram(gl, MASK_VERTEX_SHADER, BLIT_FRAGMENT_SHADER);\n\n\tconst regionPositionBuffer = gl.createBuffer()!;\n\tconst regionPositionLocation = gl.getAttribLocation(regionProgram, 'a_pos');\n\n\tconst quadBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_TRIANGLES, gl.STATIC_DRAW);\n\tconst blitPositionLocation = gl.getAttribLocation(blitProgram, 'a_pos');\n\n\tconst colorLocation = gl.getUniformLocation(regionProgram, 'u_color')!;\n\tconst textureLocation = gl.getUniformLocation(blitProgram, 'u_texture')!;\n\n\tconst scratchTexture = gl.createTexture()!;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n\n\tconst scratchFramebuffer = gl.createFramebuffer()!;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, scratchTexture, 0);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n\tgl.useProgram(blitProgram);\n\tgl.uniform1i(textureLocation, 0);\n\tgl.colorMask(true, true, true, false);\n\n\treturn {\n\t\tcanvas,\n\t\tgl,\n\t\tregionProgram,\n\t\tblitProgram,\n\t\tregionPositionBuffer,\n\t\tquadBuffer,\n\t\tregionPositionLocation,\n\t\tblitPositionLocation,\n\t\tcolorLocation,\n\t\ttextureLocation,\n\t\tscratchTexture,\n\t\tscratchFramebuffer,\n\t};\n}\n\nfunction resizeMaskRenderer(mask: MaskRenderer, width: number, height: number) {\n\tconst { gl, canvas, scratchTexture } = mask;\n\tif (canvas.width === width && canvas.height === height) return;\n\tcanvas.width = width;\n\tcanvas.height = height;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n}\n\nfunction drawRegionToScratch(\n\tmask: MaskRenderer,\n\tlandmarksData: Float32Array,\n\tfaceRegion: FaceRegion,\n\tfaceIdx: number,\n\tr: number,\n\tg: number,\n\tb: number,\n) {\n\tconst { gl, regionProgram, regionPositionBuffer, regionPositionLocation, colorLocation, scratchFramebuffer } = mask;\n\tconst baseIdx = N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT;\n\tconst { indices, vertices } = faceRegion;\n\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\tgl.useProgram(regionProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, regionPositionBuffer);\n\tgl.enableVertexAttribArray(regionPositionLocation);\n\tgl.vertexAttribPointer(regionPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\n\tfor (let i = 0; i < indices.length; ++i) {\n\t\tconst landmarkIdx = (baseIdx + indices[i]) * 4;\n\t\tvertices[i * 2] = landmarksData[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksData[landmarkIdx + 1];\n\t}\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, indices.length);\n}\n\nfunction accumulateScratch(mask: MaskRenderer) {\n\tconst { gl, blitProgram, quadBuffer, blitPositionLocation, scratchTexture } = mask;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.useProgram(blitProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.enableVertexAttribArray(blitPositionLocation);\n\tgl.vertexAttribPointer(blitPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.activeTexture(gl.TEXTURE0);\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.FUNC_ADD);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS,\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4,\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMask(detector: Detector, width: number, height: number) {\n\tconst {\n\t\tmask,\n\t\tmaxFaces,\n\t\tlandmarks,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl, canvas: maskCanvas } = mask;\n\tconst { data: landmarksData } = landmarks;\n\n\tresizeMaskRenderer(mask, width, height);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\n\tif (!faceRegions) return;\n\n\tconst useTesselationBitmask = maxFaces <= GB_BITMASK_MAX_FACES;\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst g =\n\t\t\tuseTesselationBitmask && faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? GREEN_CHANNEL_VALUES[GREEN_REGION_NAMES[faceIdx]]\n\t\t\t\t: 0;\n\t\tconst b = useTesselationBitmask\n\t\t\t? faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? 0\n\t\t\t\t: BLUE_CHANNEL_VALUES[BLUE_REGION_NAMES[faceIdx - GREEN_REGION_NAMES.length]]\n\t\t\t: (faceIdx + 1) / CHANNEL_BIT_SCALE;\n\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.TESSELATION, faceIdx, 0, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.OVAL, faceIdx, RED_CHANNEL_VALUES.OVAL, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.LEFT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.LEFT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.RIGHT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.RIGHT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.MOUTH, faceIdx, RED_CHANNEL_VALUES.MOUTH, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.INNER_MOUTH,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.INNER_MOUTH,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, emitHook, updateTexturesInternal } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet destroyed = false;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult(singleHistoryWriteIndex?: number) {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlet historyWriteIndex: number | number[] | undefined = singleHistoryWriteIndex;\n\t\t\tif (typeof historyWriteIndex === 'undefined' && pendingBackfillSlots.length > 0) {\n\t\t\t\thistoryWriteIndex = pendingBackfillSlots;\n\t\t\t\tpendingBackfillSlots = [];\n\t\t\t}\n\t\t\tupdateTexturesInternal(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\thistory ? { skipHistoryWrite, historyWriteIndex } : undefined,\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tif (destroyed) return;\n\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\t\t\t\tif (destroyed) {\n\t\t\t\t\tfaceLandmarker.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tmask: initMaskRenderer(maskCanvas),\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\tnCalls: 0,\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\t\t\tconst callOrder = ++detector.state.nCalls;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (!detector || callOrder !== detector.state.nCalls) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({\n\t\t\t\t\t\trunningMode: requiredMode,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tlet width: number, height: number;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\twidth = source.videoWidth;\n\t\t\t\t\t\theight = source.videoHeight;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\twidth = source.width;\n\t\t\t\t\t\theight = source.height;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMask(detector, width, height);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result) {\n\t\t\t\t\tfor (const [cb, hasCalled] of detector.subscribers.entries()) {\n\t\t\t\t\t\tif (!hasCalled) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('_init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: textureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: 'RGBA32F',\n\t\t\t\t\ttype: 'FLOAT',\n\t\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\t\thistory,\n\t\t\t\t},\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => {\n\t\t\t\tif (destroyed || !detector) return;\n\t\t\t\temitHook('face:ready');\n\t\t\t});\n\t\t});\n\n\t\tlet historyWriteCounter = 0;\n\t\tlet pendingBackfillSlots: number[] = [];\n\t\tconst writeToHistory = () => {\n\t\t\tif (!history) return;\n\t\t\tonResult(historyWriteCounter); // Write stale data immediately.\n\t\t\tpendingBackfillSlots.push(historyWriteCounter); // Queue up backfill with more recent data.\n\t\t\thistoryWriteCounter = (historyWriteCounter + 1) % (history + 1);\n\t\t};\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) {\n\t\t\t\twriteToHistory();\n\t\t\t\tdetectFaces(source);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tif (!skipHistoryWrite) writeToHistory();\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tdestroyed = true;\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.regionProgram);\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.blitProgram);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.regionPositionBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.quadBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteTexture(detector.mask.scratchTexture);\n\t\t\t\t\tdetector.mask.gl.deleteFramebuffer(detector.mask.scratchFramebuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\t\tconst decodeFaceBitIndex = Array.from(\n\t\t\t{ length: GB_BITMASK_MAX_FACES - 1 },\n\t\t\t(_, i) => `step(${2 ** (i + 1)}.0, faceBitF)`,\n\t\t).join(' + ');\n\t\tconst decodeFaceIndex =\n\t\t\toptions.maxFaces <= GB_BITMASK_MAX_FACES\n\t\t\t\t? `uint faceBits = (uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5) << 8) | uint(mask.g * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tuint faceBit = faceBits & (~faceBits + 1u);\n\tfloat faceBitF = float(faceBit);\n\tfloat hasFace = sign(faceBitF);\n\tfloat faceIndex = ${decodeFaceBitIndex} - (1.0 - hasFace);`\n\t\t\t\t: `float faceIndex = float(int(uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5)) - 1);`;\n\n\t\tconst checkAt = (fnName: string, ...regionNames: (keyof typeof RED_REGION_BIT_VALUES)[]) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\tuint bits = uint(mask.r * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tfloat hit = sign(float(bits & ${regionNames.reduce(\n\t\t(mask, regionName) => mask | RED_REGION_BIT_VALUES[regionName],\n\t\t0,\n\t)}u));\n\treturn vec2(hit, mix(-1.0, faceIndex, hit));`,\n\t\t\t);\n\n\t\tconst combineLeftRight = (fnName: string, leftFn: string, rightFn: string) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\tvec2 right = ${rightFn}(pos${historyParams});\n\treturn mix(right, left, left.x);`,\n\t\t\t);\n\n\t\tconst checkIn = (fnNames: string[]) =>\n\t\t\tfnNames\n\t\t\t\t.map(fnName =>\n\t\t\t\t\tfn(\n\t\t\t\t\t\t'float',\n\t\t\t\t\t\t`in${fnName[0].toUpperCase() + fnName.slice(1)}`,\n\t\t\t\t\t\t'vec2 pos',\n\t\t\t\t\t\t`vec2 a = ${fnName}At(pos${historyParams}); return step(0.0, a.y) * a.x;`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.join('\\n');\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform mediump sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`,\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`,\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${checkAt('leftEyebrow', 'LEFT_EYEBROW')}\n${checkAt('rightEyebrow', 'RIGHT_EYEBROW')}\n${checkAt('leftEye', 'LEFT_EYE')}\n${checkAt('rightEye', 'RIGHT_EYE')}\n${checkAt('lips', 'MOUTH')}\n${checkAt('mouth', 'MOUTH', 'INNER_MOUTH')}\n${checkAt('innerMouth', 'INNER_MOUTH')}\n${checkAt('faceOval', 'OVAL')}\n${fn(\n\t'vec2',\n\t'faceAt',\n\t'vec2 pos',\n\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\treturn vec2(step(0.0, faceIndex), faceIndex);`,\n)}\n${combineLeftRight('eye', 'leftEyeAt', 'rightEyeAt')}\n${combineLeftRight('eyebrow', 'leftEyebrowAt', 'rightEyebrowAt')}\n${checkIn(['eyebrow', 'eye', 'mouth', 'innerMouth', 'lips', 'face'])}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","BLIT_FRAGMENT_SHADER","FULLSCREEN_TRIANGLES","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","RED_REGION_NAMES","GREEN_REGION_NAMES","BLUE_REGION_NAMES","CHANNEL_BIT_SCALE","GB_BITMASK_MAX_FACES","createChannelBitValues","names","name","normalizeChannelBitValues","bitValues","bitValue","RED_REGION_BIT_VALUES","GREEN_REGION_BIT_VALUES","BLUE_REGION_BIT_VALUES","RED_CHANNEL_VALUES","GREEN_CHANNEL_VALUES","BLUE_CHANNEL_VALUES","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","sharedDetectors","createProgram","gl","vertexSource","fragmentSource","vertexShader","fragmentShader","program","initMaskRenderer","canvas","regionProgram","blitProgram","regionPositionBuffer","regionPositionLocation","quadBuffer","blitPositionLocation","colorLocation","textureLocation","scratchTexture","scratchFramebuffer","resizeMaskRenderer","mask","width","height","drawRegionToScratch","landmarksData","faceRegion","faceIdx","r","g","b","baseIdx","vertices","landmarkIdx","accumulateScratch","updateLandmarksData","detector","faces","data","nFaces","landmarks","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMask","maxFaces","maskCanvas","useTesselationBitmask","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","updateTexturesInternal","existingDetector","destroyed","skipHistoryWrite","onResult","singleHistoryWriteIndex","nSlots","rowsToUpdate","historyWriteIndex","pendingBackfillSlots","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","faceLandmarker","initPromise","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","hasCalled","historyWriteCounter","writeToHistory","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","decodeFaceBitIndex","decodeFaceIndex","checkAt","fnName","regionNames","regionName","combineLeftRight","leftFn","rightFn","checkIn","fnNames","face_default"]}
1
+ {"version":3,"sources":["../../src/plugins/face.ts"],"sourcesContent":["import ShaderPad, { PluginContext, TextureSource } from '..';\nimport {\n\tcalculateBoundingBoxCenter,\n\tgenerateGLSLFn,\n\tgetSharedFileset,\n\thashOptions,\n\tisMediaPipeSource,\n\tMediaPipeSource,\n} from './mediapipe-common';\nimport type { FaceLandmarker, FaceLandmarkerResult, NormalizedLandmark } from '@mediapipe/tasks-vision';\n\nexport interface FacePluginOptions {\n\tmodelPath?: string;\n\tmaxFaces?: number;\n\tminFaceDetectionConfidence?: number;\n\tminFacePresenceConfidence?: number;\n\tminTrackingConfidence?: number;\n\toutputFaceBlendshapes?: boolean;\n\toutputFacialTransformationMatrixes?: boolean;\n\thistory?: number;\n}\n\nconst MASK_VERTEX_SHADER = `#version 300 es\nin vec2 a_pos;\nout vec2 v_uv;\nvoid main() {\n\tv_uv = a_pos;\n\tgl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);\n}`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\nconst BLIT_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nin vec2 v_uv;\nuniform sampler2D u_texture;\nout vec4 outColor;\nvoid main() { outColor = texture(u_texture, v_uv); }`;\nconst FULLSCREEN_TRIANGLES = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);\n\nconst STANDARD_LANDMARK_COUNT = 478;\nconst CUSTOM_LANDMARK_COUNT = 2;\nconst LANDMARK_COUNT = STANDARD_LANDMARK_COUNT + CUSTOM_LANDMARK_COUNT;\nconst LANDMARKS_TEXTURE_WIDTH = 512;\nconst N_LANDMARK_METADATA_SLOTS = 1;\n\nconst LEFT_EYEBROW_INDICES = [336, 296, 334, 293, 300, 276, 283, 282, 295, 285] as const;\nconst LEFT_EYE_INDICES = [362, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374, 380, 381, 382] as const;\nconst RIGHT_EYEBROW_INDICES = [70, 63, 105, 66, 107, 55, 65, 52, 53, 46] as const;\nconst RIGHT_EYE_INDICES = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145, 144, 163, 7] as const;\nconst MOUTH_INDICES = [\n\t61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291, 375, 321, 405, 314, 17, 84, 181, 91, 146,\n] as const;\nconst INNER_MOUTH_INDICES = [\n\t78, 191, 80, 81, 82, 13, 312, 311, 310, 415, 308, 324, 318, 402, 317, 14, 87, 178, 88, 95,\n] as const;\nconst ALL_STANDARD_INDICES = Array.from({ length: STANDARD_LANDMARK_COUNT }, (_, i) => i);\nconst LANDMARK_INDICES = {\n\tLEFT_EYEBROW: LEFT_EYEBROW_INDICES,\n\tLEFT_EYE: LEFT_EYE_INDICES,\n\tLEFT_EYE_CENTER: 473,\n\tRIGHT_EYEBROW: RIGHT_EYEBROW_INDICES,\n\tRIGHT_EYE: RIGHT_EYE_INDICES,\n\tRIGHT_EYE_CENTER: 468,\n\tNOSE_TIP: 4,\n\tMOUTH: MOUTH_INDICES,\n\tINNER_MOUTH: INNER_MOUTH_INDICES,\n\t// Custom landmarks.\n\tFACE_CENTER: STANDARD_LANDMARK_COUNT,\n\tMOUTH_CENTER: STANDARD_LANDMARK_COUNT + 1,\n};\n\n/* Face mask channel layout:\n- R: additive bitfield for face regions. Since it’s additive, it handles overlapping regions.\n- G/B: tessellation ownership. When maxFaces is <= 16, G stores one-hot bits for\n faces 0-7 and B stores one-hot bits for faces 8-15 so overlapping faces\n remain decodable. Above 16 maxFaces, B falls back to storing faceIndex + 1 as\n an additive byte value, which will overcount on overlaps. */\nconst RED_REGION_NAMES = [\n\t'OVAL',\n\t'LEFT_EYEBROW',\n\t'RIGHT_EYEBROW',\n\t'LEFT_EYE',\n\t'RIGHT_EYE',\n\t'MOUTH',\n\t'INNER_MOUTH',\n] as const;\nconst GREEN_REGION_NAMES = ['FACE_0', 'FACE_1', 'FACE_2', 'FACE_3', 'FACE_4', 'FACE_5', 'FACE_6', 'FACE_7'] as const;\nconst BLUE_REGION_NAMES = [\n\t'FACE_8',\n\t'FACE_9',\n\t'FACE_10',\n\t'FACE_11',\n\t'FACE_12',\n\t'FACE_13',\n\t'FACE_14',\n\t'FACE_15',\n] as const;\nconst CHANNEL_BIT_SCALE = 255;\nconst GB_BITMASK_MAX_FACES = GREEN_REGION_NAMES.length + BLUE_REGION_NAMES.length;\n\nfunction createChannelBitValues<const T extends readonly string[]>(names: T): Record<T[number], number> {\n\treturn Object.fromEntries(names.map((name, i) => [name, 1 << i])) as Record<T[number], number>;\n}\n\nfunction normalizeChannelBitValues<T extends Record<string, number>>(bitValues: T): T {\n\treturn Object.fromEntries(\n\t\tObject.entries(bitValues).map(([name, bitValue]) => [name, bitValue / CHANNEL_BIT_SCALE]),\n\t) as T;\n}\n\nconst RED_REGION_BIT_VALUES = createChannelBitValues(RED_REGION_NAMES);\nconst GREEN_REGION_BIT_VALUES = createChannelBitValues(GREEN_REGION_NAMES);\nconst BLUE_REGION_BIT_VALUES = createChannelBitValues(BLUE_REGION_NAMES);\nconst RED_CHANNEL_VALUES = normalizeChannelBitValues(RED_REGION_BIT_VALUES);\nconst GREEN_CHANNEL_VALUES = normalizeChannelBitValues(GREEN_REGION_BIT_VALUES);\nconst BLUE_CHANNEL_VALUES = normalizeChannelBitValues(BLUE_REGION_BIT_VALUES);\n\nconst DEFAULT_FACE_OPTIONS: Required<Omit<FacePluginOptions, 'history'>> = {\n\tmodelPath:\n\t\t'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task',\n\tmaxFaces: 1,\n\tminFaceDetectionConfidence: 0.5,\n\tminFacePresenceConfidence: 0.5,\n\tminTrackingConfidence: 0.5,\n\toutputFaceBlendshapes: false,\n\toutputFacialTransformationMatrixes: false,\n};\n\nfunction fanTriangulate(indices: readonly number[]): number[] {\n\tconst tris: number[] = [];\n\tfor (let i = 1; i < indices.length - 1; ++i) {\n\t\ttris.push(indices[0], indices[i], indices[i + 1]);\n\t}\n\treturn tris;\n}\n\ntype FaceRegion = { indices: number[]; vertices: Float32Array };\nlet faceRegions: Record<string, FaceRegion> | null = null;\nfunction initFaceRegions(LandmarkerClass: typeof FaceLandmarker): void {\n\tif (!faceRegions) {\n\t\tconst tesselationConnections = LandmarkerClass.FACE_LANDMARKS_TESSELATION;\n\t\tconst tesselation: number[] = [];\n\t\tfor (let i = 0; i < tesselationConnections.length - 2; i += 3) {\n\t\t\ttesselation.push(\n\t\t\t\ttesselationConnections[i].start,\n\t\t\t\ttesselationConnections[i + 1].start,\n\t\t\t\ttesselationConnections[i + 2].start,\n\t\t\t);\n\t\t}\n\t\tconst ovalIndices = LandmarkerClass.FACE_LANDMARKS_FACE_OVAL.map(({ start }) => start);\n\t\tfaceRegions = Object.fromEntries(\n\t\t\tObject.entries({\n\t\t\t\tLEFT_EYEBROW: fanTriangulate(LEFT_EYEBROW_INDICES),\n\t\t\t\tRIGHT_EYEBROW: fanTriangulate(RIGHT_EYEBROW_INDICES),\n\t\t\t\tLEFT_EYE: fanTriangulate(LEFT_EYE_INDICES),\n\t\t\t\tRIGHT_EYE: fanTriangulate(RIGHT_EYE_INDICES),\n\t\t\t\tMOUTH: fanTriangulate(MOUTH_INDICES),\n\t\t\t\tINNER_MOUTH: fanTriangulate(INNER_MOUTH_INDICES),\n\t\t\t\tTESSELATION: tesselation,\n\t\t\t\tOVAL: fanTriangulate(ovalIndices),\n\t\t\t}).map(([key, indices]) => [key, { indices, vertices: new Float32Array(indices.length * 2) }]),\n\t\t);\n\t}\n}\n\ninterface MaskRenderer {\n\tcanvas: OffscreenCanvas;\n\tgl: WebGL2RenderingContext;\n\tregionProgram: WebGLProgram;\n\tblitProgram: WebGLProgram;\n\tregionPositionBuffer: WebGLBuffer;\n\tquadBuffer: WebGLBuffer;\n\tregionPositionLocation: number;\n\tblitPositionLocation: number;\n\tcolorLocation: WebGLUniformLocation;\n\ttextureLocation: WebGLUniformLocation;\n\tscratchTexture: WebGLTexture;\n\tscratchFramebuffer: WebGLFramebuffer;\n}\n\ninterface Detector {\n\tlandmarker: FaceLandmarker;\n\tmask: MaskRenderer;\n\tsubscribers: Map<Function, boolean>;\n\tmaxFaces: number;\n\tstate: {\n\t\tnCalls: number;\n\t\trunningMode: 'IMAGE' | 'VIDEO';\n\t\tsource: MediaPipeSource | null;\n\t\tvideoTime: number;\n\t\tresultTimestamp: number;\n\t\tresult: FaceLandmarkerResult | null;\n\t\tpending: Promise<void>;\n\t\tnFaces: number;\n\t};\n\tlandmarks: {\n\t\tdata: Float32Array;\n\t\ttextureHeight: number;\n\t};\n}\nconst sharedDetectors = new Map<string, Detector>();\n\nfunction createProgram(gl: WebGL2RenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram {\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, vertexSource);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, fragmentSource);\n\tgl.compileShader(fragmentShader);\n\n\tconst program = gl.createProgram()!;\n\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\treturn program;\n}\n\nfunction initMaskRenderer(canvas: OffscreenCanvas): MaskRenderer {\n\tconst gl = canvas.getContext('webgl2', {\n\t\tantialias: false,\n\t\tpreserveDrawingBuffer: true,\n\t})!;\n\tconst regionProgram = createProgram(gl, MASK_VERTEX_SHADER, MASK_FRAGMENT_SHADER);\n\tconst blitProgram = createProgram(gl, MASK_VERTEX_SHADER, BLIT_FRAGMENT_SHADER);\n\n\tconst regionPositionBuffer = gl.createBuffer()!;\n\tconst regionPositionLocation = gl.getAttribLocation(regionProgram, 'a_pos');\n\n\tconst quadBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_TRIANGLES, gl.STATIC_DRAW);\n\tconst blitPositionLocation = gl.getAttribLocation(blitProgram, 'a_pos');\n\n\tconst colorLocation = gl.getUniformLocation(regionProgram, 'u_color')!;\n\tconst textureLocation = gl.getUniformLocation(blitProgram, 'u_texture')!;\n\n\tconst scratchTexture = gl.createTexture()!;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n\n\tconst scratchFramebuffer = gl.createFramebuffer()!;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, scratchTexture, 0);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\n\tgl.useProgram(blitProgram);\n\tgl.uniform1i(textureLocation, 0);\n\tgl.colorMask(true, true, true, false);\n\n\treturn {\n\t\tcanvas,\n\t\tgl,\n\t\tregionProgram,\n\t\tblitProgram,\n\t\tregionPositionBuffer,\n\t\tquadBuffer,\n\t\tregionPositionLocation,\n\t\tblitPositionLocation,\n\t\tcolorLocation,\n\t\ttextureLocation,\n\t\tscratchTexture,\n\t\tscratchFramebuffer,\n\t};\n}\n\nfunction resizeMaskRenderer(mask: MaskRenderer, width: number, height: number) {\n\tconst { gl, canvas, scratchTexture } = mask;\n\tif (canvas.width === width && canvas.height === height) return;\n\tcanvas.width = width;\n\tcanvas.height = height;\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);\n}\n\nfunction drawRegionToScratch(\n\tmask: MaskRenderer,\n\tlandmarksData: Float32Array,\n\tfaceRegion: FaceRegion,\n\tfaceIdx: number,\n\tr: number,\n\tg: number,\n\tb: number,\n) {\n\tconst { gl, regionProgram, regionPositionBuffer, regionPositionLocation, colorLocation, scratchFramebuffer } = mask;\n\tconst baseIdx = N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT;\n\tconst { indices, vertices } = faceRegion;\n\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, scratchFramebuffer);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\tgl.useProgram(regionProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, regionPositionBuffer);\n\tgl.enableVertexAttribArray(regionPositionLocation);\n\tgl.vertexAttribPointer(regionPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\n\tfor (let i = 0; i < indices.length; ++i) {\n\t\tconst landmarkIdx = (baseIdx + indices[i]) * 4;\n\t\tvertices[i * 2] = landmarksData[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksData[landmarkIdx + 1];\n\t}\n\tgl.bufferData(gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW);\n\tgl.uniform4f(colorLocation, r, g, b, 1.0);\n\tgl.drawArrays(gl.TRIANGLES, 0, indices.length);\n}\n\nfunction accumulateScratch(mask: MaskRenderer) {\n\tconst { gl, blitProgram, quadBuffer, blitPositionLocation, scratchTexture } = mask;\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, mask.canvas.width, mask.canvas.height);\n\tgl.useProgram(blitProgram);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);\n\tgl.enableVertexAttribArray(blitPositionLocation);\n\tgl.vertexAttribPointer(blitPositionLocation, 2, gl.FLOAT, false, 0, 0);\n\tgl.activeTexture(gl.TEXTURE0);\n\tgl.bindTexture(gl.TEXTURE_2D, scratchTexture);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.FUNC_ADD);\n\tgl.blendFunc(gl.ONE, gl.ONE);\n\tgl.drawArrays(gl.TRIANGLES, 0, 6);\n}\n\nfunction updateLandmarksData(detector: Detector, faces: NormalizedLandmark[][]) {\n\tconst data = detector.landmarks.data;\n\tconst nFaces = faces.length;\n\tdata[0] = nFaces;\n\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst landmarks = faces[faceIdx];\n\t\tfor (let landmarkIdx = 0; landmarkIdx < STANDARD_LANDMARK_COUNT; ++landmarkIdx) {\n\t\t\tconst landmark = landmarks[landmarkIdx];\n\t\t\tconst dataIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + landmarkIdx) * 4;\n\t\t\tdata[dataIdx] = landmark.x;\n\t\t\tdata[dataIdx + 1] = 1 - landmark.y;\n\t\t\tdata[dataIdx + 2] = landmark.z ?? 0;\n\t\t\tdata[dataIdx + 3] = landmark.visibility ?? 1;\n\t\t}\n\n\t\tconst faceCenter = calculateBoundingBoxCenter(\n\t\t\tdata,\n\t\t\tfaceIdx,\n\t\t\tALL_STANDARD_INDICES,\n\t\t\tLANDMARK_COUNT,\n\t\t\tN_LANDMARK_METADATA_SLOTS,\n\t\t);\n\t\tdata.set(faceCenter, (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.FACE_CENTER) * 4);\n\n\t\tconst mouthCenter = calculateBoundingBoxCenter(data, faceIdx, INNER_MOUTH_INDICES, LANDMARK_COUNT, 1);\n\t\tdata.set(\n\t\t\tmouthCenter,\n\t\t\t(N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + LANDMARK_INDICES.MOUTH_CENTER) * 4,\n\t\t);\n\t}\n\n\tdetector.state.nFaces = nFaces;\n}\n\nfunction updateMask(detector: Detector, width: number, height: number) {\n\tconst {\n\t\tmask,\n\t\tmaxFaces,\n\t\tlandmarks,\n\t\tstate: { nFaces },\n\t} = detector;\n\tconst { gl, canvas: maskCanvas } = mask;\n\tconst { data: landmarksData } = landmarks;\n\n\tresizeMaskRenderer(mask, width, height);\n\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\tgl.viewport(0, 0, maskCanvas.width, maskCanvas.height);\n\tgl.clearColor(0, 0, 0, 0);\n\tgl.clear(gl.COLOR_BUFFER_BIT);\n\n\tif (!faceRegions) return;\n\n\tconst useTesselationBitmask = maxFaces <= GB_BITMASK_MAX_FACES;\n\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst g =\n\t\t\tuseTesselationBitmask && faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? GREEN_CHANNEL_VALUES[GREEN_REGION_NAMES[faceIdx]]\n\t\t\t\t: 0;\n\t\tconst b = useTesselationBitmask\n\t\t\t? faceIdx < GREEN_REGION_NAMES.length\n\t\t\t\t? 0\n\t\t\t\t: BLUE_CHANNEL_VALUES[BLUE_REGION_NAMES[faceIdx - GREEN_REGION_NAMES.length]]\n\t\t\t: (faceIdx + 1) / CHANNEL_BIT_SCALE;\n\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.TESSELATION, faceIdx, 0, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.OVAL, faceIdx, RED_CHANNEL_VALUES.OVAL, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.LEFT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.LEFT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.RIGHT_EYEBROW,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.RIGHT_EYEBROW,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, g, b);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(mask, landmarksData, faceRegions.MOUTH, faceIdx, RED_CHANNEL_VALUES.MOUTH, 0, 0);\n\t\taccumulateScratch(mask);\n\t\tdrawRegionToScratch(\n\t\t\tmask,\n\t\t\tlandmarksData,\n\t\t\tfaceRegions.INNER_MOUTH,\n\t\t\tfaceIdx,\n\t\t\tRED_CHANNEL_VALUES.INNER_MOUTH,\n\t\t\tg,\n\t\t\tb,\n\t\t);\n\t\taccumulateScratch(mask);\n\t}\n}\n\nfunction face(config: { textureName: string; options?: FacePluginOptions }) {\n\tconst { textureName, options: { history, ...mediapipeOptions } = {} } = config;\n\tconst options = { ...DEFAULT_FACE_OPTIONS, ...mediapipeOptions };\n\tconst optionsKey = hashOptions({ ...options, textureName });\n\n\tconst nLandmarksMax = options.maxFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\tconst textureHeight = Math.ceil(nLandmarksMax / LANDMARKS_TEXTURE_WIDTH);\n\n\treturn function (shaderPad: ShaderPad, context: PluginContext) {\n\t\tconst { injectGLSL, emitHook, updateTexturesInternal } = context;\n\n\t\tconst existingDetector = sharedDetectors.get(optionsKey);\n\t\tconst landmarksData =\n\t\t\texistingDetector?.landmarks.data ?? new Float32Array(LANDMARKS_TEXTURE_WIDTH * textureHeight * 4);\n\t\tconst maskCanvas = existingDetector?.mask.canvas ?? new OffscreenCanvas(1, 1);\n\t\tlet detector: Detector | null = null;\n\t\tlet destroyed = false;\n\t\tlet skipHistoryWrite = false;\n\n\t\tfunction onResult(singleHistoryWriteIndex?: number) {\n\t\t\tif (!detector) return;\n\t\t\tconst nFaces = detector.state.nFaces;\n\t\t\tconst nSlots = nFaces * LANDMARK_COUNT + N_LANDMARK_METADATA_SLOTS;\n\t\t\tconst rowsToUpdate = Math.ceil(nSlots / LANDMARKS_TEXTURE_WIDTH);\n\t\t\tlet historyWriteIndex: number | number[] | undefined = singleHistoryWriteIndex;\n\t\t\tif (typeof historyWriteIndex === 'undefined' && pendingBackfillSlots.length > 0) {\n\t\t\t\thistoryWriteIndex = pendingBackfillSlots;\n\t\t\t\tpendingBackfillSlots = [];\n\t\t\t}\n\t\t\tupdateTexturesInternal(\n\t\t\t\t{\n\t\t\t\t\tu_faceLandmarksTex: {\n\t\t\t\t\t\tdata: detector.landmarks.data,\n\t\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\t\theight: rowsToUpdate,\n\t\t\t\t\t\tisPartial: true,\n\t\t\t\t\t},\n\t\t\t\t\tu_faceMask: detector.mask.canvas,\n\t\t\t\t},\n\t\t\t\thistory ? { skipHistoryWrite, historyWriteIndex } : undefined,\n\t\t\t);\n\t\t\tshaderPad.updateUniforms({ u_nFaces: nFaces });\n\t\t\temitHook('face:result', detector.state.result);\n\t\t}\n\n\t\tasync function initializeDetector() {\n\t\t\tif (sharedDetectors.has(optionsKey)) {\n\t\t\t\tdetector = sharedDetectors.get(optionsKey)!;\n\t\t\t} else {\n\t\t\t\tconst [mediaPipe, { FaceLandmarker }] = await Promise.all([\n\t\t\t\t\tgetSharedFileset(),\n\t\t\t\t\timport('@mediapipe/tasks-vision'),\n\t\t\t\t]);\n\t\t\t\tif (destroyed) return;\n\n\t\t\t\tconst faceLandmarker = await FaceLandmarker.createFromOptions(mediaPipe, {\n\t\t\t\t\tbaseOptions: {\n\t\t\t\t\t\tmodelAssetPath: options.modelPath,\n\t\t\t\t\t\tdelegate: 'GPU',\n\t\t\t\t\t},\n\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\tnumFaces: options.maxFaces,\n\t\t\t\t\tminFaceDetectionConfidence: options.minFaceDetectionConfidence,\n\t\t\t\t\tminFacePresenceConfidence: options.minFacePresenceConfidence,\n\t\t\t\t\tminTrackingConfidence: options.minTrackingConfidence,\n\t\t\t\t\toutputFaceBlendshapes: options.outputFaceBlendshapes,\n\t\t\t\t\toutputFacialTransformationMatrixes: options.outputFacialTransformationMatrixes,\n\t\t\t\t});\n\t\t\t\tif (destroyed) {\n\t\t\t\t\tfaceLandmarker.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdetector = {\n\t\t\t\t\tlandmarker: faceLandmarker,\n\t\t\t\t\tmask: initMaskRenderer(maskCanvas),\n\t\t\t\t\tsubscribers: new Map(),\n\t\t\t\t\tmaxFaces: options.maxFaces,\n\t\t\t\t\tstate: {\n\t\t\t\t\t\tnCalls: 0,\n\t\t\t\t\t\trunningMode: 'VIDEO',\n\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\tvideoTime: -1,\n\t\t\t\t\t\tresultTimestamp: 0,\n\t\t\t\t\t\tresult: null,\n\t\t\t\t\t\tpending: Promise.resolve(),\n\t\t\t\t\t\tnFaces: 0,\n\t\t\t\t\t},\n\t\t\t\t\tlandmarks: {\n\t\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\t\ttextureHeight,\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tinitFaceRegions(FaceLandmarker);\n\t\t\t\tsharedDetectors.set(optionsKey, detector);\n\t\t\t}\n\n\t\t\tdetector.subscribers.set(onResult, false);\n\t\t}\n\t\tconst initPromise = initializeDetector();\n\n\t\tasync function detectFaces(source: MediaPipeSource) {\n\t\t\tconst now = performance.now();\n\t\t\tawait initPromise;\n\t\t\tif (!detector) return;\n\t\t\tconst callOrder = ++detector.state.nCalls;\n\n\t\t\tdetector.state.pending = detector.state.pending.then(async () => {\n\t\t\t\tif (!detector || callOrder !== detector.state.nCalls) return;\n\n\t\t\t\tconst requiredMode = source instanceof HTMLVideoElement ? 'VIDEO' : 'IMAGE';\n\t\t\t\tif (detector.state.runningMode !== requiredMode) {\n\t\t\t\t\tdetector.state.runningMode = requiredMode;\n\t\t\t\t\tawait detector.landmarker.setOptions({\n\t\t\t\t\t\trunningMode: requiredMode,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet shouldDetect = false;\n\n\t\t\t\tif (source !== detector.state.source) {\n\t\t\t\t\tdetector.state.source = source;\n\t\t\t\t\tdetector.state.videoTime = -1;\n\t\t\t\t\tshouldDetect = true;\n\t\t\t\t} else if (source instanceof HTMLVideoElement) {\n\t\t\t\t\tif (source.currentTime !== detector.state.videoTime) {\n\t\t\t\t\t\tdetector.state.videoTime = source.currentTime;\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t} else if (!(source instanceof HTMLImageElement)) {\n\t\t\t\t\tif (now - detector.state.resultTimestamp > 2) {\n\t\t\t\t\t\tshouldDetect = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldDetect) {\n\t\t\t\t\tlet result: FaceLandmarkerResult | undefined;\n\t\t\t\t\tlet width: number, height: number;\n\t\t\t\t\tif (source instanceof HTMLVideoElement) {\n\t\t\t\t\t\tif (source.videoWidth === 0 || source.videoHeight === 0 || source.readyState < 2) return;\n\t\t\t\t\t\twidth = source.videoWidth;\n\t\t\t\t\t\theight = source.videoHeight;\n\t\t\t\t\t\tresult = detector.landmarker.detectForVideo(source, now);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (source.width === 0 || source.height === 0) return;\n\t\t\t\t\t\twidth = source.width;\n\t\t\t\t\t\theight = source.height;\n\t\t\t\t\t\tresult = detector.landmarker.detect(source);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tdetector.state.resultTimestamp = now;\n\t\t\t\t\t\tdetector.state.result = result;\n\t\t\t\t\t\tupdateLandmarksData(detector, result.faceLandmarks);\n\t\t\t\t\t\tupdateMask(detector, width, height);\n\t\t\t\t\t\tfor (const cb of detector.subscribers.keys()) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (detector.state.result) {\n\t\t\t\t\tfor (const [cb, hasCalled] of detector.subscribers.entries()) {\n\t\t\t\t\t\tif (!hasCalled) {\n\t\t\t\t\t\t\tcb();\n\t\t\t\t\t\t\tdetector.subscribers.set(cb, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait detector.state.pending;\n\t\t}\n\n\t\tshaderPad.on('_init', () => {\n\t\t\tshaderPad.initializeUniform('u_maxFaces', 'int', options.maxFaces);\n\t\t\tshaderPad.initializeUniform('u_nFaces', 'int', 0);\n\t\t\tshaderPad.initializeTexture(\n\t\t\t\t'u_faceLandmarksTex',\n\t\t\t\t{\n\t\t\t\t\tdata: landmarksData,\n\t\t\t\t\twidth: LANDMARKS_TEXTURE_WIDTH,\n\t\t\t\t\theight: textureHeight,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tinternalFormat: 'RGBA32F',\n\t\t\t\t\ttype: 'FLOAT',\n\t\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\t\thistory,\n\t\t\t\t},\n\t\t\t);\n\t\t\tshaderPad.initializeTexture('u_faceMask', maskCanvas, {\n\t\t\t\tminFilter: 'NEAREST',\n\t\t\t\tmagFilter: 'NEAREST',\n\t\t\t\thistory,\n\t\t\t});\n\t\t\tinitPromise.then(() => {\n\t\t\t\tif (destroyed || !detector) return;\n\t\t\t\temitHook('face:ready');\n\t\t\t});\n\t\t});\n\n\t\tlet historyWriteCounter = 0;\n\t\tlet pendingBackfillSlots: number[] = [];\n\t\tconst writeToHistory = () => {\n\t\t\tif (!history) return;\n\t\t\tonResult(historyWriteCounter); // Write stale data immediately.\n\t\t\tpendingBackfillSlots.push(historyWriteCounter); // Queue up backfill with more recent data.\n\t\t\thistoryWriteCounter = (historyWriteCounter + 1) % (history + 1);\n\t\t};\n\n\t\tshaderPad.on('initializeTexture', (name: string, source: TextureSource) => {\n\t\t\tif (name === textureName && isMediaPipeSource(source)) {\n\t\t\t\twriteToHistory();\n\t\t\t\tdetectFaces(source);\n\t\t\t}\n\t\t});\n\n\t\tshaderPad.on(\n\t\t\t'updateTextures',\n\t\t\t(updates: Record<string, TextureSource>, options?: { skipHistoryWrite?: boolean }) => {\n\t\t\t\tconst source = updates[textureName];\n\t\t\t\tif (isMediaPipeSource(source)) {\n\t\t\t\t\tskipHistoryWrite = options?.skipHistoryWrite ?? false;\n\t\t\t\t\tif (!skipHistoryWrite) writeToHistory();\n\t\t\t\t\tdetectFaces(source);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tshaderPad.on('destroy', () => {\n\t\t\tdestroyed = true;\n\t\t\tif (detector) {\n\t\t\t\tdetector.subscribers.delete(onResult);\n\t\t\t\tif (detector.subscribers.size === 0) {\n\t\t\t\t\tdetector.landmarker.close();\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.regionProgram);\n\t\t\t\t\tdetector.mask.gl.deleteProgram(detector.mask.blitProgram);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.regionPositionBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.quadBuffer);\n\t\t\t\t\tdetector.mask.gl.deleteTexture(detector.mask.scratchTexture);\n\t\t\t\t\tdetector.mask.gl.deleteFramebuffer(detector.mask.scratchFramebuffer);\n\t\t\t\t\tsharedDetectors.delete(optionsKey);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdetector = null;\n\t\t});\n\n\t\tconst { fn, historyParams } = generateGLSLFn(history);\n\t\tconst sampleMask = history ? `_sampleFaceMask(pos, framesAgo)` : `texture(u_faceMask, pos)`;\n\t\tconst decodeFaceBitIndex = Array.from(\n\t\t\t{ length: GB_BITMASK_MAX_FACES - 1 },\n\t\t\t(_, i) => `step(${2 ** (i + 1)}.0, faceBitF)`,\n\t\t).join(' + ');\n\t\tconst decodeFaceIndex =\n\t\t\toptions.maxFaces <= GB_BITMASK_MAX_FACES\n\t\t\t\t? `uint faceBits = (uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5) << 8) | uint(mask.g * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tuint faceBit = faceBits & (~faceBits + 1u);\n\tfloat faceBitF = float(faceBit);\n\tfloat hasFace = sign(faceBitF);\n\tfloat faceIndex = ${decodeFaceBitIndex} - (1.0 - hasFace);`\n\t\t\t\t: `float faceIndex = float(int(uint(mask.b * ${CHANNEL_BIT_SCALE}.0 + 0.5)) - 1);`;\n\n\t\tconst checkAt = (fnName: string, ...regionNames: (keyof typeof RED_REGION_BIT_VALUES)[]) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\tuint bits = uint(mask.r * ${CHANNEL_BIT_SCALE}.0 + 0.5);\n\tfloat hit = sign(float(bits & ${regionNames.reduce(\n\t\t(mask, regionName) => mask | RED_REGION_BIT_VALUES[regionName],\n\t\t0,\n\t)}u));\n\treturn vec2(hit, mix(-1.0, faceIndex, hit));`,\n\t\t\t);\n\n\t\tconst combineLeftRight = (fnName: string, leftFn: string, rightFn: string) =>\n\t\t\tfn(\n\t\t\t\t'vec2',\n\t\t\t\t`${fnName}At`,\n\t\t\t\t'vec2 pos',\n\t\t\t\t`vec2 left = ${leftFn}(pos${historyParams});\n\tvec2 right = ${rightFn}(pos${historyParams});\n\treturn mix(right, left, left.x);`,\n\t\t\t);\n\n\t\tconst checkIn = (fnNames: string[]) =>\n\t\t\tfnNames\n\t\t\t\t.map(fnName =>\n\t\t\t\t\tfn(\n\t\t\t\t\t\t'float',\n\t\t\t\t\t\t`in${fnName[0].toUpperCase() + fnName.slice(1)}`,\n\t\t\t\t\t\t'vec2 pos',\n\t\t\t\t\t\t`vec2 a = ${fnName}At(pos${historyParams}); return step(0.0, a.y) * a.x;`,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.join('\\n');\n\n\t\tinjectGLSL(`\nuniform int u_maxFaces;\nuniform int u_nFaces;\nuniform highp sampler2D${history ? 'Array' : ''} u_faceLandmarksTex;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceLandmarksTexFrameOffset;`\n\t\t\t\t: ''\n\t\t}\nuniform mediump sampler2D${history ? 'Array' : ''} u_faceMask;${\n\t\t\thistory\n\t\t\t\t? `\nuniform int u_faceMaskFrameOffset;`\n\t\t\t\t: ''\n\t\t}\n\n#define FACE_LANDMARK_L_EYE_CENTER ${LANDMARK_INDICES.LEFT_EYE_CENTER}\n#define FACE_LANDMARK_R_EYE_CENTER ${LANDMARK_INDICES.RIGHT_EYE_CENTER}\n#define FACE_LANDMARK_NOSE_TIP ${LANDMARK_INDICES.NOSE_TIP}\n#define FACE_LANDMARK_FACE_CENTER ${LANDMARK_INDICES.FACE_CENTER}\n#define FACE_LANDMARK_MOUTH_CENTER ${LANDMARK_INDICES.MOUTH_CENTER}\n\n${fn(\n\t'int',\n\t'nFacesAt',\n\t'',\n\thistory\n\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`\n\t\t: `\n\treturn int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`,\n)}\n${fn(\n\t'vec4',\n\t'faceLandmark',\n\t'int faceIndex, int landmarkIndex',\n\t`int i = ${N_LANDMARK_METADATA_SLOTS} + faceIndex * ${LANDMARK_COUNT} + landmarkIndex;\n\tint x = i % ${LANDMARKS_TEXTURE_WIDTH};\n\tint y = i / ${LANDMARKS_TEXTURE_WIDTH};${\n\t\thistory\n\t\t\t? `\n\tint layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`\n\t\t\t: `\n\treturn texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`\n\t}`,\n)}\n${\n\thistory\n\t\t? `\nvec4 _sampleFaceMask(vec2 pos, int framesAgo) {\n\tint layer = (u_faceMaskFrameOffset - framesAgo + ${history + 1}) % ${history + 1};\n\treturn texture(u_faceMask, vec3(pos, float(layer)));\n}\n`\n\t\t: ''\n}\n${checkAt('leftEyebrow', 'LEFT_EYEBROW')}\n${checkAt('rightEyebrow', 'RIGHT_EYEBROW')}\n${checkAt('leftEye', 'LEFT_EYE')}\n${checkAt('rightEye', 'RIGHT_EYE')}\n${checkAt('lips', 'MOUTH')}\n${checkAt('mouth', 'MOUTH', 'INNER_MOUTH')}\n${checkAt('innerMouth', 'INNER_MOUTH')}\n${checkAt('faceOval', 'OVAL')}\n${fn(\n\t'vec2',\n\t'faceAt',\n\t'vec2 pos',\n\t`vec4 mask = ${sampleMask};\n\t${decodeFaceIndex}\n\treturn vec2(step(0.0, faceIndex), faceIndex);`,\n)}\n${combineLeftRight('eye', 'leftEyeAt', 'rightEyeAt')}\n${combineLeftRight('eyebrow', 'leftEyebrowAt', 'rightEyebrowAt')}\n${checkIn(['eyebrow', 'eye', 'mouth', 'innerMouth', 'lips', 'face'])}`);\n\t};\n}\n\nexport default face;\n"],"mappings":"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","names":["MASK_VERTEX_SHADER","MASK_FRAGMENT_SHADER","BLIT_FRAGMENT_SHADER","FULLSCREEN_TRIANGLES","STANDARD_LANDMARK_COUNT","CUSTOM_LANDMARK_COUNT","LANDMARK_COUNT","LANDMARKS_TEXTURE_WIDTH","N_LANDMARK_METADATA_SLOTS","LEFT_EYEBROW_INDICES","LEFT_EYE_INDICES","RIGHT_EYEBROW_INDICES","RIGHT_EYE_INDICES","MOUTH_INDICES","INNER_MOUTH_INDICES","ALL_STANDARD_INDICES","_","i","LANDMARK_INDICES","RED_REGION_NAMES","GREEN_REGION_NAMES","BLUE_REGION_NAMES","CHANNEL_BIT_SCALE","GB_BITMASK_MAX_FACES","createChannelBitValues","names","name","normalizeChannelBitValues","bitValues","bitValue","RED_REGION_BIT_VALUES","GREEN_REGION_BIT_VALUES","BLUE_REGION_BIT_VALUES","RED_CHANNEL_VALUES","GREEN_CHANNEL_VALUES","BLUE_CHANNEL_VALUES","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","sharedDetectors","createProgram","gl","vertexSource","fragmentSource","vertexShader","fragmentShader","program","initMaskRenderer","canvas","regionProgram","blitProgram","regionPositionBuffer","regionPositionLocation","quadBuffer","blitPositionLocation","colorLocation","textureLocation","scratchTexture","scratchFramebuffer","resizeMaskRenderer","mask","width","height","drawRegionToScratch","landmarksData","faceRegion","faceIdx","r","g","b","baseIdx","vertices","landmarkIdx","accumulateScratch","updateLandmarksData","detector","faces","data","nFaces","landmarks","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMask","maxFaces","maskCanvas","useTesselationBitmask","face","config","textureName","history","mediapipeOptions","options","optionsKey","hashOptions","nLandmarksMax","textureHeight","shaderPad","context","injectGLSL","emitHook","updateTexturesInternal","existingDetector","destroyed","skipHistoryWrite","onResult","singleHistoryWriteIndex","nSlots","rowsToUpdate","historyWriteIndex","pendingBackfillSlots","initializeDetector","mediaPipe","FaceLandmarker","getSharedFileset","faceLandmarker","initPromise","detectFaces","source","now","callOrder","requiredMode","shouldDetect","result","cb","hasCalled","historyWriteCounter","writeToHistory","isMediaPipeSource","updates","fn","historyParams","generateGLSLFn","sampleMask","decodeFaceBitIndex","decodeFaceIndex","checkAt","fnName","regionNames","regionName","combineLeftRight","leftFn","rightFn","checkIn","fnNames","face_default"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shaderpad",
3
- "version": "1.0.0-beta.64",
3
+ "version": "1.0.0-beta.65",
4
4
  "description": "A lightweight, dependency-free library to reduce boilerplate when writing fragment shaders.",
5
5
  "keywords": [
6
6
  "shaders",