diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c56c4e9..9fed438 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@ 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 dc2b332..d170d58 100644 --- a/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt +++ b/app/src/main/java/no/naiv/tiltshift/effect/TiltShiftRenderer.kt @@ -6,7 +6,6 @@ import android.opengl.GLES11Ext import android.opengl.GLES20 import android.opengl.GLSurfaceView import android.util.Log -import android.view.Surface import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.FloatBuffer @@ -70,32 +69,26 @@ class TiltShiftRenderer( @Volatile private var vertexBufferDirty: Boolean = false - // Texture coordinates for the back camera, indexed by Surface.ROTATION_*. - // The base orientation (index 0) applies the 90° CCW rotation that maps - // the landscape sensor frame to a portrait display. Indices 1/2/3 layer - // additional CCW rotations on top so the activity's rotation is - // compensated and world-up stays at clip-space-top. - private val texCoordsBackByRotation = arrayOf( - floatArrayOf(1f, 1f, 1f, 0f, 0f, 1f, 0f, 0f), // ROTATION_0 - floatArrayOf(1f, 0f, 0f, 0f, 1f, 1f, 0f, 1f), // ROTATION_90 - floatArrayOf(0f, 0f, 0f, 1f, 1f, 0f, 1f, 1f), // ROTATION_180 - floatArrayOf(0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f) // ROTATION_270 + // Texture coordinates rotated 90° for portrait mode (back camera) + // (Camera sensors are landscape-oriented, we rotate to portrait) + private val texCoordsBack = floatArrayOf( + 1f, 1f, // Bottom left of screen -> bottom right of texture + 1f, 0f, // Bottom right of screen -> top right of texture + 0f, 1f, // Top left of screen -> bottom left of texture + 0f, 0f // Top right of screen -> top left of texture ) - // Front camera variants: same as back, but horizontally mirrored - // for the natural selfie view. - private val texCoordsFrontByRotation = arrayOf( - floatArrayOf(0f, 1f, 0f, 0f, 1f, 1f, 1f, 0f), // ROTATION_0 - floatArrayOf(0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f), // ROTATION_90 - floatArrayOf(1f, 0f, 1f, 1f, 0f, 0f, 0f, 1f), // ROTATION_180 - floatArrayOf(1f, 1f, 0f, 1f, 1f, 0f, 0f, 0f) // ROTATION_270 + // Texture coordinates for front camera (mirrored + rotated) + // Front camera needs horizontal mirror for natural selfie view + private val texCoordsFront = floatArrayOf( + 0f, 1f, // Bottom left of screen + 0f, 0f, // Bottom right of screen + 1f, 1f, // Top left of screen + 1f, 0f // Top right of screen ) @Volatile - private var displayRotation: Int = Surface.ROTATION_0 - - @Volatile - private var currentTexCoords = texCoordsBackByRotation[Surface.ROTATION_0] + private var currentTexCoords = texCoordsBack @Volatile private var updateTexCoordBuffer = false @@ -212,7 +205,8 @@ class TiltShiftRenderer( fun setFrontCamera(front: Boolean) { if (isFrontCamera != front) { isFrontCamera = front - refreshTexCoords() + currentTexCoords = if (front) texCoordsFront else texCoordsBack + updateTexCoordBuffer = true } } @@ -224,27 +218,6 @@ class TiltShiftRenderer( } } - /** - * Updates the active display rotation. The texture-coordinate buffer is - * rebuilt so the camera image stays world-aligned as the activity rotates - * with the device under screenOrientation="fullSensor", and the - * crop-to-fill math picks the correct effective aspect ratio. - */ - fun setDisplayRotation(rotation: Int) { - if (displayRotation != rotation) { - displayRotation = rotation - refreshTexCoords() - vertexBufferDirty = true - } - } - - private fun refreshTexCoords() { - val table = if (isFrontCamera) texCoordsFrontByRotation else texCoordsBackByRotation - val idx = displayRotation.coerceIn(0, table.size - 1) - currentTexCoords = table[idx] - updateTexCoordBuffer = true - } - fun release() { shader.release() surfaceTexture?.release() @@ -281,22 +254,16 @@ class TiltShiftRenderer( /** * Recomputes camera vertex positions to achieve crop-to-fill. * - * The camera sensor is landscape; after the orientation-dependent texcoord - * rotation, the effective dimensions seen on screen are either swapped - * (portrait orientations) or kept (landscape orientations). We scale the - * vertex quad so the camera frame fills the surface without stretching — - * the GPU clips the overflow. + * The camera sensor is landscape; after the 90° rotation applied via texture coordinates, + * the effective portrait dimensions are (cameraHeight × cameraWidth). We scale the vertex + * quad so the camera frame fills the surface without stretching — the GPU clips the overflow. */ private fun recomputeVertices() { var scaleX = 1f var scaleY = 1f if (cameraWidth > 0 && cameraHeight > 0 && surfaceWidth > 0 && surfaceHeight > 0) { - val isPortrait = displayRotation == Surface.ROTATION_0 || - displayRotation == Surface.ROTATION_180 - val effectiveW = if (isPortrait) cameraHeight else cameraWidth - val effectiveH = if (isPortrait) cameraWidth else cameraHeight - val cameraRatio = effectiveW.toFloat() / effectiveH + val cameraRatio = cameraHeight.toFloat() / cameraWidth val screenRatio = surfaceWidth.toFloat() / surfaceHeight if (cameraRatio > screenRatio) { diff --git a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt index 763d4e4..bbb706d 100644 --- a/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt +++ b/app/src/main/java/no/naiv/tiltshift/ui/CameraScreen.kt @@ -1,13 +1,9 @@ package no.naiv.tiltshift.ui -import android.content.Context import android.content.Intent import android.graphics.SurfaceTexture -import android.hardware.display.DisplayManager import android.opengl.GLSurfaceView import android.util.Log -import android.view.Display -import android.view.Surface import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -168,23 +164,6 @@ fun CameraScreen( } } - // Forward the activity's actual rotation (Display.rotation) to the - // renderer so the camera image stays world-aligned as the activity rotates - // with the device. Don't drive this from OrientationEventListener — its - // 45° threshold fires *before* the activity has rotated, briefly leaving - // the texcoord set out of sync with the GL surface orientation. - // LocalConfiguration triggers a recomposition on configuration change, - // which is when Display.rotation can have changed. - val configuration = androidx.compose.ui.platform.LocalConfiguration.current - val displayRotation = remember(configuration) { - val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager - displayManager.getDisplay(Display.DEFAULT_DISPLAY)?.rotation ?: Surface.ROTATION_0 - } - LaunchedEffect(displayRotation, renderer) { - renderer?.setDisplayRotation(displayRotation) - glSurfaceView?.requestRender() - } - // Start camera when surface texture is available LaunchedEffect(surfaceTexture) { surfaceTexture?.let { diff --git a/version.properties b/version.properties index 93b21cf..f37cebb 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ versionMajor=1 versionMinor=1 -versionPatch=13 -versionCode=15 +versionPatch=14 +versionCode=16