shaderpad 1.0.0-beta.63 → 1.0.0-beta.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -497,13 +497,13 @@ All region functions return `vec2(confidence, faceIndex)`. faceIndex is 0-indexe
497
497
  - `mouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in full mouth area (lips + inner mouth), `vec2(0.0, -1.0)` otherwise.
498
498
  - `innerMouthAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in inner mouth region, `vec2(0.0, -1.0)` otherwise.
499
499
  - `faceOvalAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face oval contour, `vec2(0.0, -1.0)` otherwise.
500
- - `faceAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in face mesh or oval contour, `vec2(0.0, -1.0)` otherwise.
500
+ - `faceAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in the full face area, `vec2(0.0, -1.0)` otherwise.
501
501
  - `eyeAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in either eye, `vec2(0.0, -1.0)` otherwise.
502
502
  - `eyebrowAt(vec2 pos) -> vec2` - Returns `vec2(1.0, faceIndex)` if position is in either eyebrow, `vec2(0.0, -1.0)` otherwise.
503
503
 
504
504
  **Convenience functions** (return confidence 0-1 if found, `0.0` otherwise):
505
505
 
506
- - `inFace(vec2 pos) -> float` - Returns confidence (0-1) if position is in face mesh, `0.0` otherwise.
506
+ - `inFace(vec2 pos) -> float` - Returns confidence (0-1) if position is in the full face area, `0.0` otherwise.
507
507
  - `inEye(vec2 pos) -> float` - Returns confidence (0-1) if position is in either eye, `0.0` otherwise.
508
508
  - `inEyebrow(vec2 pos) -> float` - Returns confidence (0-1) if position is in either eyebrow, `0.0` otherwise.
509
509
  - `inMouth(vec2 pos) -> float` - Returns confidence (0-1) if position is in full mouth area (lips + inner mouth), `0.0` otherwise.
@@ -1,60 +1,76 @@
1
- "use strict";var Te=Object.create;var U=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var Fe=Object.getPrototypeOf,Ae=Object.prototype.hasOwnProperty;var Re=(a,e)=>{for(var t in e)U(a,t,{get:e[t],enumerable:!0})},te=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ge(e))!Ae.call(a,n)&&n!==t&&U(a,n,{get:()=>e[n],enumerable:!(s=pe(e,n))||s.enumerable});return a};var ne=(a,e,t)=>(t=a!=null?Te(Fe(a)):{},te(e||!a||!a.__esModule?U(t,"default",{value:a,enumerable:!0}):t,a)),he=a=>te(U({},"__esModule",{value:!0}),a);var ve={};Re(ve,{default:()=>Se});module.exports=he(ve);var He={data:new Uint8Array(4),width:1,height:1};function W(a){return a instanceof HTMLVideoElement||a instanceof HTMLImageElement||a instanceof HTMLCanvasElement||a instanceof OffscreenCanvas}function ae(a){return JSON.stringify(a,Object.keys(a).sort())}function V(a,e,t,s,n=0){let c=1/0,d=-1/0,u=1/0,o=-1/0,m=0,f=0;for(let E of t){let p=(n+e*s+E)*4,N=a[p],I=a[p+1];c=Math.min(c,N),d=Math.max(d,N),u=Math.min(u,I),o=Math.max(o,I),m+=a[p+2],f+=a[p+3]}return[(c+d)/2,(u+o)/2,m/t.length,f/t.length]}var G=null;function re(){return G||(G=import("@mediapipe/tasks-vision").then(({FilesetResolver:a})=>a.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.22-rc.20250304/wasm"))),G}function ie(a){return{historyParams:a?", framesAgo":"",fn:a?(s,n,c,d)=>{let u=c.replace(/\w+ /g,""),o=c?`${c}, int framesAgo`:"int framesAgo",m=u?`${u}, 0`:"0";return`${s} ${n}(${o}) {
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}) {
2
2
  ${d}
3
3
  }
4
- ${s} ${n}(${c}) { return ${n}(${m}); }`}:(s,n,c,d)=>`${s} ${n}(${c}) {
4
+ ${i} ${r}(${s}) { return ${r}(${E}); }`}:(i,r,s,d)=>`${i} ${r}(${s}) {
5
5
  ${d}
6
- }`}}var Le=`#version 300 es
6
+ }`}}var le=`#version 300 es
7
7
  in vec2 a_pos;
8
- void main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`,Me=`#version 300 es
8
+ out vec2 v_uv;
9
+ void main() {
10
+ v_uv = a_pos;
11
+ gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
12
+ }`,ke=`#version 300 es
9
13
  precision mediump float;
10
14
  uniform vec4 u_color;
11
15
  out vec4 outColor;
12
- void main() { outColor = u_color; }`,v=478,ke=2,g=v+ke,k=512,L=1,oe=[336,296,334,293,300,276,283,282,295,285],ce=[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],le=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],ue=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],K=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],xe=Array.from({length:v},(a,e)=>e),b={LEFT_EYEBROW:oe,LEFT_EYE:ce,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:me,RIGHT_EYE:le,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:ue,INNER_MOUTH:K,FACE_CENTER:v,MOUTH_CENTER:v+1},fe=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],Ee=fe.length-1,h=Object.fromEntries(fe.map((a,e)=>[a,e/Ee])),se=.5/Ee,be={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 x(a){let e=[];for(let t=1;t<a.length-1;++t)e.push(a[0],a[t],a[t+1]);return e}var T=null;function Oe(a){if(!T){let e=a.FACE_LANDMARKS_TESSELATION,t=[];for(let n=0;n<e.length-2;n+=3)t.push(e[n].start,e[n+1].start,e[n+2].start);let s=a.FACE_LANDMARKS_FACE_OVAL.map(({start:n})=>n);T=Object.fromEntries(Object.entries({LEFT_EYEBROW:x(oe),RIGHT_EYEBROW:x(me),LEFT_EYE:x(ce),RIGHT_EYE:x(le),MOUTH:x(ue),INNER_MOUTH:x(K),TESSELATION:t,OVAL:x(s)}).map(([n,c])=>[n,{triangles:c,vertices:new Float32Array(c.length*2)}]))}}var S=new Map;function Ne(a){let e=a.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),t=e.createShader(e.VERTEX_SHADER);e.shaderSource(t,Le),e.compileShader(t);let s=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(s,Me),e.compileShader(s);let n=e.createProgram();e.attachShader(n,t),e.attachShader(n,s),e.linkProgram(n),e.deleteShader(t),e.deleteShader(s);let c=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,c);let d=e.getAttribLocation(n,"a_pos");e.enableVertexAttribArray(d),e.vertexAttribPointer(d,2,e.FLOAT,!1,0,0);let u=e.getUniformLocation(n,"u_color");return e.useProgram(n),e.enable(e.BLEND),e.blendEquation(e.MAX),{canvas:a,gl:e,program:n,positionBuffer:c,colorLocation:u}}function R(a,e,t,s,n,c,d){let{triangles:u,vertices:o}=t,{gl:m,colorLocation:f}=a;for(let E=0;E<u.length;++E){let p=(L+s*g+u[E])*4;o[E*2]=e[p],o[E*2+1]=e[p+1]}m.bufferData(m.ARRAY_BUFFER,o,m.DYNAMIC_DRAW),m.uniform4f(f,n,c,d,1),m.drawArrays(m.TRIANGLES,0,u.length)}function Ie(a,e){let t=a.landmarks.data,s=e.length;t[0]=s;for(let n=0;n<s;++n){let c=e[n];for(let o=0;o<v;++o){let m=c[o],f=(L+n*g+o)*4;t[f]=m.x,t[f+1]=1-m.y,t[f+2]=m.z??0,t[f+3]=m.visibility??1}let d=V(t,n,xe,g,L);t.set(d,(L+n*g+b.FACE_CENTER)*4);let u=V(t,n,K,g,1);t.set(u,(L+n*g+b.MOUTH_CENTER)*4)}a.state.nFaces=s}function Ce(a,e,t){let{mask:s,maxFaces:n,landmarks:c,state:{nFaces:d}}=a,{gl:u,canvas:o}=s,{data:m}=c;if((o.width!==e||o.height!==t)&&(o.width=e,o.height=t),u.viewport(0,0,o.width,o.height),u.clearColor(0,0,0,0),u.clear(u.COLOR_BUFFER_BIT),!!T)for(let f=0;f<d;++f){let E=(f+1)/n;R(s,m,T.TESSELATION,f,0,.5,E),R(s,m,T.OVAL,f,0,1,E),R(s,m,T.LEFT_EYEBROW,f,h.LEFT_EYEBROW,0,E),R(s,m,T.RIGHT_EYEBROW,f,h.RIGHT_EYEBROW,0,E),R(s,m,T.LEFT_EYE,f,h.LEFT_EYE,0,E),R(s,m,T.RIGHT_EYE,f,h.RIGHT_EYE,0,E),R(s,m,T.MOUTH,f,h.MOUTH,0,E),R(s,m,T.INNER_MOUTH,f,h.INNER_MOUTH,0,E)}}function ye(a){let{textureName:e,options:{history:t,...s}={}}=a,n={...be,...s},c=ae({...n,textureName:e}),d=n.maxFaces*g+L,u=Math.ceil(d/k);return function(o,m){let{injectGLSL:f,emitHook:E,updateTexturesInternal:p}=m,N=S.get(c),I=N?.landmarks.data??new Float32Array(k*u*4),z=N?.mask.canvas??new OffscreenCanvas(1,1),r=null,$=!1,P=!1;function Y(i){if(!r)return;let l=r.state.nFaces,_=l*g+L,C=Math.ceil(_/k),F=i;typeof F>"u"&&D.length>0&&(F=D,D=[]),p({u_faceLandmarksTex:{data:r.landmarks.data,width:k,height:C,isPartial:!0},u_faceMask:r.mask.canvas},t?{skipHistoryWrite:P,historyWriteIndex:F}:void 0),o.updateUniforms({u_nFaces:l}),E("face:result",r.state.result)}async function de(){if(S.has(c))r=S.get(c);else{let[i,{FaceLandmarker:l}]=await Promise.all([re(),import("@mediapipe/tasks-vision")]);if($)return;let _=await l.createFromOptions(i,{baseOptions:{modelAssetPath:n.modelPath,delegate:"GPU"},runningMode:"VIDEO",numFaces:n.maxFaces,minFaceDetectionConfidence:n.minFaceDetectionConfidence,minFacePresenceConfidence:n.minFacePresenceConfidence,minTrackingConfidence:n.minTrackingConfidence,outputFaceBlendshapes:n.outputFaceBlendshapes,outputFacialTransformationMatrixes:n.outputFacialTransformationMatrixes});if($){_.close();return}r={landmarker:_,mask:Ne(z),subscribers:new Map,maxFaces:n.maxFaces,state:{nCalls:0,runningMode:"VIDEO",source:null,videoTime:-1,resultTimestamp:0,result:null,pending:Promise.resolve(),nFaces:0},landmarks:{data:I,textureHeight:u}},Oe(l),S.set(c,r)}r.subscribers.set(Y,!1)}let j=de();async function X(i){let l=performance.now();if(await j,!r)return;let _=++r.state.nCalls;r.state.pending=r.state.pending.then(async()=>{if(!r||_!==r.state.nCalls)return;let C=i instanceof HTMLVideoElement?"VIDEO":"IMAGE";r.state.runningMode!==C&&(r.state.runningMode=C,await r.landmarker.setOptions({runningMode:C}));let F=!1;if(i!==r.state.source?(r.state.source=i,r.state.videoTime=-1,F=!0):i instanceof HTMLVideoElement?i.currentTime!==r.state.videoTime&&(r.state.videoTime=i.currentTime,F=!0):i instanceof HTMLImageElement||l-r.state.resultTimestamp>2&&(F=!0),F){let A,y,w;if(i instanceof HTMLVideoElement){if(i.videoWidth===0||i.videoHeight===0||i.readyState<2)return;y=i.videoWidth,w=i.videoHeight,A=r.landmarker.detectForVideo(i,l)}else{if(i.width===0||i.height===0)return;y=i.width,w=i.height,A=r.landmarker.detect(i)}if(A){r.state.resultTimestamp=l,r.state.result=A,Ie(r,A.faceLandmarks),Ce(r,y,w);for(let ee of r.subscribers.keys())ee(),r.subscribers.set(ee,!0)}}else if(r.state.result)for(let[A,y]of r.subscribers.entries())y||(A(),r.subscribers.set(A,!0))}),await r.state.pending}o.on("_init",()=>{o.initializeUniform("u_maxFaces","int",n.maxFaces),o.initializeUniform("u_nFaces","int",0),o.initializeTexture("u_faceLandmarksTex",{data:I,width:k,height:u},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:t}),o.initializeTexture("u_faceMask",z,{minFilter:"NEAREST",magFilter:"NEAREST",history:t}),j.then(()=>{$||!r||E("face:ready")})});let H=0,D=[],q=()=>{t&&(Y(H),D.push(H),H=(H+1)%(t+1))};o.on("initializeTexture",(i,l)=>{i===e&&W(l)&&(q(),X(l))}),o.on("updateTextures",(i,l)=>{let _=i[e];W(_)&&(P=l?.skipHistoryWrite??!1,P||q(),X(_))}),o.on("destroy",()=>{$=!0,r&&(r.subscribers.delete(Y),r.subscribers.size===0&&(r.landmarker.close(),r.mask.gl.deleteProgram(r.mask.program),r.mask.gl.deleteBuffer(r.mask.positionBuffer),S.delete(c))),r=null});let{fn:O,historyParams:B}=ie(t),J=t?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",M=(i,l,_=l)=>O("vec2",`${i}At`,"vec2 pos",`vec4 mask = ${J};
13
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
14
- return (mask.r > ${(h[l]-se).toFixed(4)} && mask.r < ${(h[_]+se).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`),Z=(i,l)=>O("vec2",`${i}At`,"vec2 pos",`vec4 mask = ${J};
15
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
16
- return mask.g > ${l.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`),Q=(i,l,_)=>O("vec2",`${i}At`,"vec2 pos",`vec2 left = ${l}(pos${B});
17
- return left.x > 0.0 ? left : ${_}(pos${B});`),_e=i=>i.map(l=>O("float",`in${l[0].toUpperCase()+l.slice(1)}`,"vec2 pos",`vec2 a = ${l}At(pos${B}); return step(0.0, a.y) * a.x;`)).join(`
18
- `);f(`
16
+ void main() { outColor = u_color; }`,Se=`#version 300 es
17
+ precision mediump float;
18
+ in vec2 v_uv;
19
+ uniform sampler2D u_texture;
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);
22
+ uint faceBit = faceBits & (~faceBits + 1u);
23
+ float faceBitF = float(faceBit);
24
+ float hasFace = sign(faceBitF);
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
+ ${ie}
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});
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
+ `);l(`
19
33
  uniform int u_maxFaces;
20
34
  uniform int u_nFaces;
21
- uniform highp sampler2D${t?"Array":""} u_faceLandmarksTex;${t?`
35
+ uniform highp sampler2D${n?"Array":""} u_faceLandmarksTex;${n?`
22
36
  uniform int u_faceLandmarksTexFrameOffset;`:""}
23
- uniform ${t?"highp":"mediump"} sampler2D${t?"Array":""} u_faceMask;${t?`
37
+ uniform mediump sampler2D${n?"Array":""} u_faceMask;${n?`
24
38
  uniform int u_faceMaskFrameOffset;`:""}
25
39
 
26
- #define FACE_LANDMARK_L_EYE_CENTER ${b.LEFT_EYE_CENTER}
27
- #define FACE_LANDMARK_R_EYE_CENTER ${b.RIGHT_EYE_CENTER}
28
- #define FACE_LANDMARK_NOSE_TIP ${b.NOSE_TIP}
29
- #define FACE_LANDMARK_FACE_CENTER ${b.FACE_CENTER}
30
- #define FACE_LANDMARK_MOUTH_CENTER ${b.MOUTH_CENTER}
40
+ #define FACE_LANDMARK_L_EYE_CENTER ${S.LEFT_EYE_CENTER}
41
+ #define FACE_LANDMARK_R_EYE_CENTER ${S.RIGHT_EYE_CENTER}
42
+ #define FACE_LANDMARK_NOSE_TIP ${S.NOSE_TIP}
43
+ #define FACE_LANDMARK_FACE_CENTER ${S.FACE_CENTER}
44
+ #define FACE_LANDMARK_MOUTH_CENTER ${S.MOUTH_CENTER}
31
45
 
32
- ${O("int","nFacesAt","",t?`
33
- int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t+1}) % ${t+1};
46
+ ${B("int","nFacesAt","",n?`
47
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
34
48
  return int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
35
49
  return int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
36
- ${O("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${L} + faceIndex * ${g} + landmarkIndex;
37
- int x = i % ${k};
38
- int y = i / ${k};${t?`
39
- int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t+1}) % ${t+1};
50
+ ${B("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${M} + faceIndex * ${g} + landmarkIndex;
51
+ int x = i % ${I};
52
+ int y = i / ${I};${n?`
53
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
40
54
  return texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`:`
41
55
  return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`}`)}
42
- ${t?`
56
+ ${n?`
43
57
  vec4 _sampleFaceMask(vec2 pos, int framesAgo) {
44
- int layer = (u_faceMaskFrameOffset - framesAgo + ${t+1}) % ${t+1};
58
+ int layer = (u_faceMaskFrameOffset - framesAgo + ${n+1}) % ${n+1};
45
59
  return texture(u_faceMask, vec3(pos, float(layer)));
46
60
  }
47
61
  `:""}
48
- ${M("leftEyebrow","LEFT_EYEBROW")}
49
- ${M("rightEyebrow","RIGHT_EYEBROW")}
50
- ${M("leftEye","LEFT_EYE")}
51
- ${M("rightEye","RIGHT_EYE")}
52
- ${M("lips","MOUTH")}
53
- ${M("mouth","MOUTH","INNER_MOUTH")}
54
- ${M("innerMouth","INNER_MOUTH")}
55
- ${Z("faceOval",.75)}
56
- ${Z("face",.25)}
57
- ${Q("eye","leftEyeAt","rightEyeAt")}
58
- ${Q("eyebrow","leftEyebrowAt","rightEyebrowAt")}
59
- ${_e(["eyebrow","eye","mouth","innerMouth","lips","face"])}`)}}var Se=ye;
62
+ ${b("leftEyebrow","LEFT_EYEBROW")}
63
+ ${b("rightEyebrow","RIGHT_EYEBROW")}
64
+ ${b("leftEye","LEFT_EYE")}
65
+ ${b("rightEye","RIGHT_EYE")}
66
+ ${b("lips","MOUTH")}
67
+ ${b("mouth","MOUTH","INNER_MOUTH")}
68
+ ${b("innerMouth","INNER_MOUTH")}
69
+ ${b("faceOval","OVAL")}
70
+ ${B("vec2","faceAt","vec2 pos",`vec4 mask = ${ae};
71
+ ${ie}
72
+ return vec2(step(0.0, faceIndex), faceIndex);`)}
73
+ ${oe("eye","leftEyeAt","rightEyeAt")}
74
+ ${oe("eyebrow","leftEyebrowAt","rightEyebrowAt")}
75
+ ${pe(["eyebrow","eye","mouth","innerMouth","lips","face"])}`)}}var ze=Ke;
60
76
  //# sourceMappingURL=face.js.map
@@ -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;\nvoid main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`;\nconst MASK_FRAGMENT_SHADER = `#version 300 es\nprecision mediump float;\nuniform vec4 u_color;\nout vec4 outColor;\nvoid main() { outColor = u_color; }`;\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\nconst REGION_NAMES = [\n\t'BACKGROUND',\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 nFaceRegions = REGION_NAMES.length - 1;\nconst RED_CHANNEL_VALUES = Object.fromEntries(REGION_NAMES.map((name, i) => [name, i / nFaceRegions])) as Record<\n\t(typeof REGION_NAMES)[number],\n\tnumber\n>;\nconst HALF_GAP = 0.5 / nFaceRegions;\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 = { triangles: 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, triangles]) => [key, { triangles, vertices: new Float32Array(triangles.length * 2) }]),\n\t\t);\n\t}\n}\n\ninterface MaskRenderer {\n\tcanvas: OffscreenCanvas;\n\tgl: WebGL2RenderingContext;\n\tprogram: WebGLProgram;\n\tpositionBuffer: WebGLBuffer;\n\tcolorLocation: WebGLUniformLocation;\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 initMaskRenderer(canvas: OffscreenCanvas): MaskRenderer {\n\tconst gl = canvas.getContext('webgl2', {\n\t\tantialias: false,\n\t\tpreserveDrawingBuffer: true,\n\t})!;\n\n\tconst vertexShader = gl.createShader(gl.VERTEX_SHADER)!;\n\tgl.shaderSource(vertexShader, MASK_VERTEX_SHADER);\n\tgl.compileShader(vertexShader);\n\n\tconst fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)!;\n\tgl.shaderSource(fragmentShader, MASK_FRAGMENT_SHADER);\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\n\tconst positionBuffer = gl.createBuffer()!;\n\tgl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n\tconst positionLocation = gl.getAttribLocation(program, 'a_pos');\n\tgl.enableVertexAttribArray(positionLocation);\n\tgl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);\n\n\tconst colorLocation = gl.getUniformLocation(program, 'u_color')!;\n\tgl.useProgram(program);\n\tgl.enable(gl.BLEND);\n\tgl.blendEquation(gl.MAX);\n\n\treturn { canvas, gl, program, positionBuffer, colorLocation };\n}\n\nfunction drawTriangles(\n\tmask: MaskRenderer,\n\tlandmarksData: Float32Array,\n\tfaceRegion: FaceRegion,\n\tfaceIdx: number,\n\tr: number,\n\tg: number,\n\tb: number,\n) {\n\tconst { triangles, vertices } = faceRegion;\n\tconst { gl, colorLocation } = mask;\n\n\tfor (let i = 0; i < triangles.length; ++i) {\n\t\tconst landmarkIdx = (N_LANDMARK_METADATA_SLOTS + faceIdx * LANDMARK_COUNT + triangles[i]) * 4;\n\t\tvertices[i * 2] = landmarksData[landmarkIdx];\n\t\tvertices[i * 2 + 1] = landmarksData[landmarkIdx + 1];\n\t}\n\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, triangles.length);\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\tif (maskCanvas.width !== width || maskCanvas.height !== height) {\n\t\tmaskCanvas.width = width;\n\t\tmaskCanvas.height = height;\n\t}\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\tfor (let faceIdx = 0; faceIdx < nFaces; ++faceIdx) {\n\t\tconst b = (faceIdx + 1) / maxFaces;\n\n\t\t// G channel: face mesh (0.5) and oval (1.0)\n\t\tdrawTriangles(mask, landmarksData, faceRegions.TESSELATION, faceIdx, 0, 0.5, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.OVAL, faceIdx, 0, 1.0, b);\n\n\t\t// R channel: feature regions\n\t\tdrawTriangles(mask, landmarksData, faceRegions.LEFT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.LEFT_EYEBROW, 0, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.RIGHT_EYEBROW, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYEBROW, 0, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.LEFT_EYE, faceIdx, RED_CHANNEL_VALUES.LEFT_EYE, 0, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.RIGHT_EYE, faceIdx, RED_CHANNEL_VALUES.RIGHT_EYE, 0, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.MOUTH, faceIdx, RED_CHANNEL_VALUES.MOUTH, 0, b);\n\t\tdrawTriangles(mask, landmarksData, faceRegions.INNER_MOUTH, faceIdx, RED_CHANNEL_VALUES.INNER_MOUTH, 0, b);\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.program);\n\t\t\t\t\tdetector.mask.gl.deleteBuffer(detector.mask.positionBuffer);\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\n\t\tconst checkAt = (\n\t\t\tfnName: string,\n\t\t\tregionMin: keyof typeof RED_CHANNEL_VALUES,\n\t\t\tregionMax: keyof typeof RED_CHANNEL_VALUES = regionMin,\n\t\t) =>\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\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn (mask.r > ${(RED_CHANNEL_VALUES[regionMin] - HALF_GAP).toFixed(4)} && mask.r < ${(\n\t\tRED_CHANNEL_VALUES[regionMax] + HALF_GAP\n\t).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`,\n\t\t\t);\n\n\t\tconst checkMaskG = (fnName: string, threshold: number) =>\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\tfloat faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;\n\treturn mask.g > ${threshold.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`,\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\treturn left.x > 0.0 ? left : ${rightFn}(pos${historyParams});`,\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 ${history ? 'highp' : '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${checkMaskG('faceOval', 0.75)}\n${checkMaskG('face', 0.25)}\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","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","REGION_NAMES","nFaceRegions","RED_CHANNEL_VALUES","name","HALF_GAP","DEFAULT_FACE_OPTIONS","fanTriangulate","indices","tris","faceRegions","initFaceRegions","LandmarkerClass","tesselationConnections","tesselation","ovalIndices","start","key","triangles","sharedDetectors","initMaskRenderer","canvas","gl","vertexShader","fragmentShader","program","positionBuffer","positionLocation","colorLocation","drawTriangles","mask","landmarksData","faceRegion","faceIdx","r","g","b","vertices","landmarkIdx","updateLandmarksData","detector","faces","data","nFaces","landmarks","landmark","dataIdx","faceCenter","calculateBoundingBoxCenter","mouthCenter","updateMask","width","height","maxFaces","maskCanvas","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","checkAt","fnName","regionMin","regionMax","checkMaskG","threshold","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, 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,55 +1,71 @@
1
- import{b as B,c as Z,d as G,e as ee,f as te}from"../chunk-VMNWRREI.mjs";var fe=`#version 300 es
1
+ import{b as X,c as oe,d as K,e as se,f as ce}from"../chunk-VMNWRREI.mjs";var Ee=`#version 300 es
2
2
  in vec2 a_pos;
3
- void main() { gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0); }`,Ee=`#version 300 es
3
+ out vec2 v_uv;
4
+ void main() {
5
+ v_uv = a_pos;
6
+ gl_Position = vec4(a_pos * 2.0 - 1.0, 0.0, 1.0);
7
+ }`,ge=`#version 300 es
4
8
  precision mediump float;
5
9
  uniform vec4 u_color;
6
10
  out vec4 outColor;
7
- void main() { outColor = u_color; }`,I=478,de=2,F=I+de,h=512,k=1,ae=[336,296,334,293,300,276,283,282,295,285],re=[362,398,384,385,386,387,388,466,263,249,390,373,374,380,381,382],se=[70,63,105,66,107,55,65,52,53,46],ie=[33,246,161,160,159,158,157,173,133,155,154,153,145,144,163,7],oe=[61,185,40,39,37,0,267,269,270,409,291,375,321,405,314,17,84,181,91,146],w=[78,191,80,81,82,13,312,311,310,415,308,324,318,402,317,14,87,178,88,95],_e=Array.from({length:I},(c,e)=>e),N={LEFT_EYEBROW:ae,LEFT_EYE:re,LEFT_EYE_CENTER:473,RIGHT_EYEBROW:se,RIGHT_EYE:ie,RIGHT_EYE_CENTER:468,NOSE_TIP:4,MOUTH:oe,INNER_MOUTH:w,FACE_CENTER:I,MOUTH_CENTER:I+1},ce=["BACKGROUND","LEFT_EYEBROW","RIGHT_EYEBROW","LEFT_EYE","RIGHT_EYE","MOUTH","INNER_MOUTH"],le=ce.length-1,g=Object.fromEntries(ce.map((c,e)=>[c,e/le])),ne=.5/le,Te={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(c){let e=[];for(let t=1;t<c.length-1;++t)e.push(c[0],c[t],c[t+1]);return e}var _=null;function Fe(c){if(!_){let e=c.FACE_LANDMARKS_TESSELATION,t=[];for(let a=0;a<e.length-2;a+=3)t.push(e[a].start,e[a+1].start,e[a+2].start);let o=c.FACE_LANDMARKS_FACE_OVAL.map(({start:a})=>a);_=Object.fromEntries(Object.entries({LEFT_EYEBROW:M(ae),RIGHT_EYEBROW:M(se),LEFT_EYE:M(re),RIGHT_EYE:M(ie),MOUTH:M(oe),INNER_MOUTH:M(w),TESSELATION:t,OVAL:M(o)}).map(([a,u])=>[a,{triangles:u,vertices:new Float32Array(u.length*2)}]))}}var C=new Map;function pe(c){let e=c.getContext("webgl2",{antialias:!1,preserveDrawingBuffer:!0}),t=e.createShader(e.VERTEX_SHADER);e.shaderSource(t,fe),e.compileShader(t);let o=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(o,Ee),e.compileShader(o);let a=e.createProgram();e.attachShader(a,t),e.attachShader(a,o),e.linkProgram(a),e.deleteShader(t),e.deleteShader(o);let u=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,u);let T=e.getAttribLocation(a,"a_pos");e.enableVertexAttribArray(T),e.vertexAttribPointer(T,2,e.FLOAT,!1,0,0);let E=e.getUniformLocation(a,"u_color");return e.useProgram(a),e.enable(e.BLEND),e.blendEquation(e.MAX),{canvas:c,gl:e,program:a,positionBuffer:u,colorLocation:E}}function R(c,e,t,o,a,u,T){let{triangles:E,vertices:s}=t,{gl:l,colorLocation:m}=c;for(let f=0;f<E.length;++f){let S=(k+o*F+E[f])*4;s[f*2]=e[S],s[f*2+1]=e[S+1]}l.bufferData(l.ARRAY_BUFFER,s,l.DYNAMIC_DRAW),l.uniform4f(m,a,u,T,1),l.drawArrays(l.TRIANGLES,0,E.length)}function Ae(c,e){let t=c.landmarks.data,o=e.length;t[0]=o;for(let a=0;a<o;++a){let u=e[a];for(let s=0;s<I;++s){let l=u[s],m=(k+a*F+s)*4;t[m]=l.x,t[m+1]=1-l.y,t[m+2]=l.z??0,t[m+3]=l.visibility??1}let T=G(t,a,_e,F,k);t.set(T,(k+a*F+N.FACE_CENTER)*4);let E=G(t,a,w,F,1);t.set(E,(k+a*F+N.MOUTH_CENTER)*4)}c.state.nFaces=o}function Re(c,e,t){let{mask:o,maxFaces:a,landmarks:u,state:{nFaces:T}}=c,{gl:E,canvas:s}=o,{data:l}=u;if((s.width!==e||s.height!==t)&&(s.width=e,s.height=t),E.viewport(0,0,s.width,s.height),E.clearColor(0,0,0,0),E.clear(E.COLOR_BUFFER_BIT),!!_)for(let m=0;m<T;++m){let f=(m+1)/a;R(o,l,_.TESSELATION,m,0,.5,f),R(o,l,_.OVAL,m,0,1,f),R(o,l,_.LEFT_EYEBROW,m,g.LEFT_EYEBROW,0,f),R(o,l,_.RIGHT_EYEBROW,m,g.RIGHT_EYEBROW,0,f),R(o,l,_.LEFT_EYE,m,g.LEFT_EYE,0,f),R(o,l,_.RIGHT_EYE,m,g.RIGHT_EYE,0,f),R(o,l,_.MOUTH,m,g.MOUTH,0,f),R(o,l,_.INNER_MOUTH,m,g.INNER_MOUTH,0,f)}}function ge(c){let{textureName:e,options:{history:t,...o}={}}=c,a={...Te,...o},u=Z({...a,textureName:e}),T=a.maxFaces*F+k,E=Math.ceil(T/h);return function(s,l){let{injectGLSL:m,emitHook:f,updateTexturesInternal:S}=l,P=C.get(u),W=P?.landmarks.data??new Float32Array(h*E*4),K=P?.mask.canvas??new OffscreenCanvas(1,1),n=null,y=!1,H=!1;function $(r){if(!n)return;let i=n.state.nFaces,d=i*F+k,O=Math.ceil(d/h),p=r;typeof p>"u"&&D.length>0&&(p=D,D=[]),S({u_faceLandmarksTex:{data:n.landmarks.data,width:h,height:O,isPartial:!0},u_faceMask:n.mask.canvas},t?{skipHistoryWrite:H,historyWriteIndex:p}:void 0),s.updateUniforms({u_nFaces:i}),f("face:result",n.state.result)}async function me(){if(C.has(u))n=C.get(u);else{let[r,{FaceLandmarker:i}]=await Promise.all([ee(),import("@mediapipe/tasks-vision")]);if(y)return;let d=await i.createFromOptions(r,{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(y){d.close();return}n={landmarker:d,mask:pe(K),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:W,textureHeight:E}},Fe(i),C.set(u,n)}n.subscribers.set($,!1)}let V=me();async function z(r){let i=performance.now();if(await V,!n)return;let d=++n.state.nCalls;n.state.pending=n.state.pending.then(async()=>{if(!n||d!==n.state.nCalls)return;let O=r instanceof HTMLVideoElement?"VIDEO":"IMAGE";n.state.runningMode!==O&&(n.state.runningMode=O,await n.landmarker.setOptions({runningMode:O}));let p=!1;if(r!==n.state.source?(n.state.source=r,n.state.videoTime=-1,p=!0):r instanceof HTMLVideoElement?r.currentTime!==n.state.videoTime&&(n.state.videoTime=r.currentTime,p=!0):r instanceof HTMLImageElement||i-n.state.resultTimestamp>2&&(p=!0),p){let A,x,Y;if(r instanceof HTMLVideoElement){if(r.videoWidth===0||r.videoHeight===0||r.readyState<2)return;x=r.videoWidth,Y=r.videoHeight,A=n.landmarker.detectForVideo(r,i)}else{if(r.width===0||r.height===0)return;x=r.width,Y=r.height,A=n.landmarker.detect(r)}if(A){n.state.resultTimestamp=i,n.state.result=A,Ae(n,A.faceLandmarks),Re(n,x,Y);for(let Q of n.subscribers.keys())Q(),n.subscribers.set(Q,!0)}}else if(n.state.result)for(let[A,x]of n.subscribers.entries())x||(A(),n.subscribers.set(A,!0))}),await n.state.pending}s.on("_init",()=>{s.initializeUniform("u_maxFaces","int",a.maxFaces),s.initializeUniform("u_nFaces","int",0),s.initializeTexture("u_faceLandmarksTex",{data:W,width:h,height:E},{internalFormat:"RGBA32F",type:"FLOAT",minFilter:"NEAREST",magFilter:"NEAREST",history:t}),s.initializeTexture("u_faceMask",K,{minFilter:"NEAREST",magFilter:"NEAREST",history:t}),V.then(()=>{y||!n||f("face:ready")})});let v=0,D=[],j=()=>{t&&($(v),D.push(v),v=(v+1)%(t+1))};s.on("initializeTexture",(r,i)=>{r===e&&B(i)&&(j(),z(i))}),s.on("updateTextures",(r,i)=>{let d=r[e];B(d)&&(H=i?.skipHistoryWrite??!1,H||j(),z(d))}),s.on("destroy",()=>{y=!0,n&&(n.subscribers.delete($),n.subscribers.size===0&&(n.landmarker.close(),n.mask.gl.deleteProgram(n.mask.program),n.mask.gl.deleteBuffer(n.mask.positionBuffer),C.delete(u))),n=null});let{fn:b,historyParams:U}=te(t),X=t?"_sampleFaceMask(pos, framesAgo)":"texture(u_faceMask, pos)",L=(r,i,d=i)=>b("vec2",`${r}At`,"vec2 pos",`vec4 mask = ${X};
8
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
9
- return (mask.r > ${(g[i]-ne).toFixed(4)} && mask.r < ${(g[d]+ne).toFixed(4)}) ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`),q=(r,i)=>b("vec2",`${r}At`,"vec2 pos",`vec4 mask = ${X};
10
- float faceIndex = floor(mask.b * float(u_maxFaces) + 0.5) - 1.0;
11
- return mask.g > ${i.toFixed(2)} ? vec2(1.0, faceIndex) : vec2(0.0, -1.0);`),J=(r,i,d)=>b("vec2",`${r}At`,"vec2 pos",`vec2 left = ${i}(pos${U});
12
- return left.x > 0.0 ? left : ${d}(pos${U});`),ue=r=>r.map(i=>b("float",`in${i[0].toUpperCase()+i.slice(1)}`,"vec2 pos",`vec2 a = ${i}At(pos${U}); return step(0.0, a.y) * a.x;`)).join(`
11
+ void main() { outColor = u_color; }`,be=`#version 300 es
12
+ precision mediump float;
13
+ in vec2 v_uv;
14
+ uniform sampler2D u_texture;
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);
17
+ uint faceBit = faceBits & (~faceBits + 1u);
18
+ float faceBitF = float(faceBit);
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};
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});
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(`
13
27
  `);m(`
14
28
  uniform int u_maxFaces;
15
29
  uniform int u_nFaces;
16
- uniform highp sampler2D${t?"Array":""} u_faceLandmarksTex;${t?`
30
+ uniform highp sampler2D${n?"Array":""} u_faceLandmarksTex;${n?`
17
31
  uniform int u_faceLandmarksTexFrameOffset;`:""}
18
- uniform ${t?"highp":"mediump"} sampler2D${t?"Array":""} u_faceMask;${t?`
32
+ uniform mediump sampler2D${n?"Array":""} u_faceMask;${n?`
19
33
  uniform int u_faceMaskFrameOffset;`:""}
20
34
 
21
- #define FACE_LANDMARK_L_EYE_CENTER ${N.LEFT_EYE_CENTER}
22
- #define FACE_LANDMARK_R_EYE_CENTER ${N.RIGHT_EYE_CENTER}
23
- #define FACE_LANDMARK_NOSE_TIP ${N.NOSE_TIP}
24
- #define FACE_LANDMARK_FACE_CENTER ${N.FACE_CENTER}
25
- #define FACE_LANDMARK_MOUTH_CENTER ${N.MOUTH_CENTER}
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}
26
40
 
27
- ${b("int","nFacesAt","",t?`
28
- int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t+1}) % ${t+1};
41
+ ${B("int","nFacesAt","",n?`
42
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
29
43
  return int(texelFetch(u_faceLandmarksTex, ivec3(0, 0, layer), 0).r + 0.5);`:`
30
44
  return int(texelFetch(u_faceLandmarksTex, ivec2(0, 0), 0).r + 0.5);`)}
31
- ${b("vec4","faceLandmark","int faceIndex, int landmarkIndex",`int i = ${k} + faceIndex * ${F} + landmarkIndex;
32
- int x = i % ${h};
33
- int y = i / ${h};${t?`
34
- int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${t+1}) % ${t+1};
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?`
48
+ int layer = (u_faceLandmarksTexFrameOffset - framesAgo + ${n+1}) % ${n+1};
35
49
  return texelFetch(u_faceLandmarksTex, ivec3(x, y, layer), 0);`:`
36
50
  return texelFetch(u_faceLandmarksTex, ivec2(x, y), 0);`}`)}
37
- ${t?`
51
+ ${n?`
38
52
  vec4 _sampleFaceMask(vec2 pos, int framesAgo) {
39
- int layer = (u_faceMaskFrameOffset - framesAgo + ${t+1}) % ${t+1};
53
+ int layer = (u_faceMaskFrameOffset - framesAgo + ${n+1}) % ${n+1};
40
54
  return texture(u_faceMask, vec3(pos, float(layer)));
41
55
  }
42
56
  `:""}
43
- ${L("leftEyebrow","LEFT_EYEBROW")}
44
- ${L("rightEyebrow","RIGHT_EYEBROW")}
45
- ${L("leftEye","LEFT_EYE")}
46
- ${L("rightEye","RIGHT_EYE")}
47
- ${L("lips","MOUTH")}
48
- ${L("mouth","MOUTH","INNER_MOUTH")}
49
- ${L("innerMouth","INNER_MOUTH")}
50
- ${q("faceOval",.75)}
51
- ${q("face",.25)}
52
- ${J("eye","leftEyeAt","rightEyeAt")}
53
- ${J("eyebrow","leftEyebrowAt","rightEyebrowAt")}
54
- ${ue(["eyebrow","eye","mouth","innerMouth","lips","face"])}`)}}var he=ge;export{he as default};
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};
66
+ ${ae}
67
+ return vec2(step(0.0, faceIndex), faceIndex);`)}
68
+ ${re("eye","leftEyeAt","rightEyeAt")}
69
+ ${re("eyebrow","leftEyebrowAt","rightEyebrowAt")}
70
+ ${Ae(["eyebrow","eye","mouth","innerMouth","lips","face"])}`)}}var He=Ue;export{He as default};
55
71
  //# sourceMappingURL=face.mjs.map