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