@@ -70,7 +70,8 @@ export const compositeFragment = tgpu.fragmentFn({
7070 'use gpu' ;
7171 const cropUv = d . vec2f ( 1 - uv . x , uv . y ) ;
7272 const sourcePixel = compositeUniform . $ . cropOrigin + cropUv * compositeUniform . $ . cropSize ;
73- const cameraUv = sourcePixel / d . vec2f ( compositeUniform . $ . sourceSize ) ;
73+ const sourceUv = sourcePixel / d . vec2f ( compositeUniform . $ . sourceSize ) ;
74+ const cameraUv = compositeUniform . $ . uvTransform * ( sourceUv - 0.5 ) + 0.5 ;
7475 const cameraColor = std . textureSampleBaseClampToEdge (
7576 compositeFrameLayout . $ . frame ,
7677 sampler . $ ,
@@ -108,13 +109,33 @@ video.srcObject = await navigator.mediaDevices.getUserMedia({
108109
109110let videoFrameCallbackId = 0 ;
110111let postProcessProfile : MaskPostProcessProfile = 'balanced' ;
112+ let uvTransform = d . mat2x2f . identity ( ) ;
113+
114+ const isIOS = / i P a d | i P h o n e | i P o d / . test ( navigator . userAgent ) ;
115+ function setUVTransformForIOS ( ) {
116+ const angle = screen . orientation . type ;
117+
118+ uvTransform = d . mat2x2f . identity ( ) ;
119+ if ( angle === 'portrait-primary' ) {
120+ uvTransform = d . mat2x2f ( 0 , - 1 , 1 , 0 ) ;
121+ } else if ( angle === 'portrait-secondary' ) {
122+ uvTransform = d . mat2x2f ( 0 , 1 , - 1 , 0 ) ;
123+ } else if ( angle === 'landscape-primary' ) {
124+ uvTransform = d . mat2x2f ( - 1 , 0 , 0 , - 1 ) ;
125+ }
126+ }
127+
128+ if ( isIOS ) {
129+ setUVTransformForIOS ( ) ;
130+ window . addEventListener ( 'orientationchange' , setUVTransformForIOS ) ;
131+ }
111132
112133function processVideoFrame ( _ : number , metadata : VideoFrameCallbackMetadata ) {
113134 if ( video . readyState < 2 ) {
114135 videoFrameCallbackId = video . requestVideoFrameCallback ( processVideoFrame ) ;
115136 return ;
116137 }
117- const crop = squareCrop ( metadata . width , metadata . height ) ;
138+ const crop = squareCrop ( metadata . width , metadata . height , uvTransform ) ;
118139 const externalTexture = root . device . importExternalTexture ( { source : video } ) ;
119140 const encoder = root . device . createCommandEncoder ( ) ;
120141 const pass = encoder . beginComputePass ( ) ;
@@ -175,6 +196,9 @@ export const controls = defineControls({
175196
176197export function onCleanup ( ) {
177198 video . cancelVideoFrameCallback ( videoFrameCallbackId ) ;
199+ if ( isIOS ) {
200+ window . removeEventListener ( 'orientationchange' , setUVTransformForIOS ) ;
201+ }
178202 if ( video . srcObject ) {
179203 for ( const track of ( video . srcObject as MediaStream ) . getTracks ( ) ) {
180204 track . stop ( ) ;
0 commit comments