Fix one landscape orientation rendering image upside down
`SurfaceTexture.getTransformMatrix()` for a custom SurfaceProvider does not vary with `Preview.targetRotation` — it only encodes the static sensor-to-buffer transform. The v1.1.8 rebind-on-rotation fix did update Preview's target rotation, but the matrix returned to the GL renderer was identical across all four device orientations. Combined with the activity rotating under fullSensor (so the GL clip-space "up" direction tracks the device, not the world), one of the two landscape orientations rendered the image upside down on a real device. The emulator masked this because its virtual scene is roughly symmetric. Compose the missing piece on the GL thread: rotate the texcoord sampling pattern around its centre by the inverse of the activity rotation before sampling the camera texture. The four orientations now produce four distinct matrices, keeping world-up at screen-up in all of them while leaving portrait unchanged. Bump to 1.1.9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f0d81db068
commit
6d7be66341
2 changed files with 45 additions and 5 deletions
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue