diff --git a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt index 98c640f..e8ff4bd 100644 --- a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt +++ b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt @@ -5,6 +5,7 @@ import android.graphics.SurfaceTexture import android.opengl.GLES11Ext import android.opengl.GLES20 import android.opengl.GLSurfaceView +import android.opengl.Matrix import android.util.Log import android.view.Surface import java.nio.ByteBuffer @@ -56,6 +57,8 @@ class TiltShiftRenderer( // SurfaceTexture transform matrix, refreshed each frame on the GL thread. private val texMatrix = FloatArray(16) + // Sensor-to-buffer matrix from SurfaceTexture before display-rotation correction. + private val sensorMatrix = FloatArray(16) // Current effect parameters (updated from UI thread) @Volatile @@ -127,9 +130,11 @@ class TiltShiftRenderer( override fun onDrawFrame(gl: GL10?) { val st = surfaceTexture st?.updateTexImage() - // Pull the latest sensor-to-display transform; updated by SurfaceTexture each frame - // and reflects whatever rotation CameraX requested via Preview.targetRotation. - st?.getTransformMatrix(texMatrix) + // SurfaceTexture's transform matrix only handles the sensor-to-buffer + // orientation; for a custom SurfaceProvider it does NOT vary with + // Preview.targetRotation. Apply the display-rotation correction here. + st?.getTransformMatrix(sensorMatrix) + composeTexMatrix() if (vertexBufferDirty) { recomputeVertices() @@ -199,6 +204,41 @@ class TiltShiftRenderer( } } + /** + * Combines the sensor-to-buffer matrix with a rotation that compensates for + * the activity's current rotation. The activity rotates with the device + * (screenOrientation="fullSensor"), so the GL clip-space "up" direction + * tracks the device rather than the world. To keep world-up at screen-up + * regardless of orientation, rotate the texcoord sampling pattern by the + * inverse of the activity rotation. Without this correction, the two + * landscape orientations would render the same matrix and one would appear + * upside-down on a real device. + */ + private fun composeTexMatrix() { + // Inverse of the activity rotation: rotating the sampling pattern by + // -activityAngle puts the world-aligned point originally at screen P + // at the same screen P after the activity has rotated. + val angle = when (displayRotation) { + Surface.ROTATION_90 -> -90f + Surface.ROTATION_180 -> 180f + Surface.ROTATION_270 -> 90f + else -> 0f + } + if (angle == 0f) { + System.arraycopy(sensorMatrix, 0, texMatrix, 0, 16) + return + } + // Build rotation around the (0.5, 0.5) texcoord center. + Matrix.setIdentityM(texMatrix, 0) + Matrix.translateM(texMatrix, 0, 0.5f, 0.5f, 0f) + Matrix.rotateM(texMatrix, 0, angle, 0f, 0f, 1f) + Matrix.translateM(texMatrix, 0, -0.5f, -0.5f, 0f) + // texMatrix = sensorMatrix * rotation (rotation is applied to the + // texcoord first, then the sensor-to-buffer transform). + val rot = texMatrix.copyOf() + Matrix.multiplyMM(texMatrix, 0, sensorMatrix, 0, rot, 0) + } + fun release() { shader.release() surfaceTexture?.release() diff --git a/version.properties b/version.properties index dd2cc78..34449a3 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ versionMajor=1 versionMinor=1 -versionPatch=8 -versionCode=10 +versionPatch=9 +versionCode=11