Commit graph

15 commits

Author SHA1 Message Date
a2dfa7db3d Make camera image follow device rotation (4-orientation texcoord table)
Re-add landscape support, this time via four precomputed texcoord
buffers — one per Surface.ROTATION_* — instead of going through
SurfaceTexture.getTransformMatrix() (which doesn't honour
Preview.targetRotation for custom SurfaceProviders) or the manual
matrix composition attempts in v1.1.6–1.1.11.

For each device orientation the renderer picks the texcoord set that
both compensates for the 90° CW sensor mount and the activity's own
rotation under screenOrientation="fullSensor", so world-up stays at
clip-space-top. recomputeVertices swaps effective camera dimensions
between portrait and landscape so crop-to-fill picks the right aspect.

Verified empirically in the emulator across all four Display.rotation
values (sky-yellow band always lands at the top of the screen).

Bump to 1.1.12.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:12:29 +02:00
b0691adfa3 Reapply "Revert orientation tracking on the camera image"
This reverts commit c0bab85d63.
2026-05-11 15:57:32 +02:00
dd4471c7d2 Revert "Flip rotation-correction angles for both landscape orientations"
This reverts commit 1cd2b0a57c.
2026-05-11 15:57:32 +02:00
1cd2b0a57c Flip rotation-correction angles for both landscape orientations
Bring back the v1.1.9 approach (apply an inverse rotation to the
texcoord sampling pattern so the camera image stays world-aligned
through device rotation), but with the right signs this time. The
previous angles were 180° off for both landscape orientations and
showed the camera content upside-down on a real device.

Verified each Display.rotation against the emulator's virtual scene
(sky-yellow → road-brown → buildings-dark): the sky/yellow band now
sits at the top of the screen in all four orientations.

Bump to 1.1.11.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:46:31 +02:00
c0bab85d63 Revert "Revert orientation tracking on the camera image"
This reverts commit 4f8661f648.
2026-05-11 15:29:59 +02:00
4f8661f648 Revert orientation tracking on the camera image
Roll back the rendering-pipeline changes from v1.1.6 (and the
subsequent attempts in 1.1.7/1.1.8/1.1.9): no more
SurfaceTexture transform-matrix-driven re-orientation, no more
rebinding on rotation, no more inverse-rotation correction. The
camera passthrough goes back to fixed portrait-oriented texcoords
and the crop-to-fill math treats the camera buffer as portrait
unconditionally.

The activity stays `fullSensor`, so the Compose UI and the GL
blur passes continue to follow the device orientation — only the
camera image itself is left untouched, which matches the
"normal camera doesn't flip the picture when rotating the phone"
behaviour requested.

Bump to 1.1.10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:59:18 +02:00
6d7be66341 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>
2026-05-11 14:46:28 +02:00
d321f07973 Support landscape orientation
Replace hardcoded portrait-only texture coordinate rotation with
SurfaceTexture.getTransformMatrix(), so the camera preview and capture
re-orient correctly when the device rotates. Also drive
Preview/ImageCapture targetRotation from the live display rotation, fix
the crop-to-fill aspect math to swap effective camera dimensions
between portrait and landscape, and make the slider control panel
scroll if it doesn't fit the shorter landscape height.

Bump to 1.1.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:31:43 +02:00
f3baa723be Implement two-pass separable Gaussian blur for camera preview
Replace the single-pass 9-tap directional blur with a three-pass
pipeline: passthrough (camera→FBO), horizontal blur (FBO-A→FBO-B),
vertical blur (FBO-B→screen). This produces a true 2D Gaussian with
a 13-tap kernel per pass, eliminating the visible banding/streaking
of the old approach.

Key changes:
- TiltShiftRenderer: FBO ping-pong with two color textures, separate
  fullscreen quad for blur passes (no crop-to-fill), drawQuad helper
- TiltShiftShader: manages two programs (passthrough + blur), blur
  program uses raw screen-space angle (no camera rotation adjustment)
- tiltshift_fragment.glsl: rewritten for sampler2D in screen space,
  aspect correction on X axis (height-normalized), uBlurDirection
  uniform for H/V selection, wider falloff (3x multiplier)
- New tiltshift_passthrough_fragment.glsl for camera→FBO copy
- TiltShiftOverlay: shrink PINCH_SIZE zone (1.3x, was 2.0x) so
  pinch-to-zoom is reachable over more of the screen
- CameraManager: optimistic zoom update fixes pinch-to-zoom stalling
  (stale zoomRatio base prevented delta accumulation)

Bump version to 1.1.3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 17:38:50 +01:00
7979ebd029 Fix CameraX GL surface: render-on-demand, crop-to-fill, lifecycle
- Switch GLSurfaceView from RENDERMODE_CONTINUOUSLY to RENDERMODE_WHEN_DIRTY
  with OnFrameAvailableListener, halving GPU work
- Add crop-to-fill aspect ratio correction so camera preview is not
  stretched on displays with non-16:9 aspect ratios
- Add LifecycleEventObserver to pause/resume GLSurfaceView with Activity
  lifecycle, preventing background rendering and GL context issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:58:17 +01:00
11a79076bc Fix concurrency, lifecycle, performance, and config issues from audit
Concurrency & bitmap lifecycle:
- Defer bitmap recycling by one cycle so Compose finishes drawing before
  native memory is freed (preview bitmaps, thumbnails)
- Make galleryPreviewSource @Volatile for cross-thread visibility
- Join preview job before recycling source bitmap in cancelGalleryPreview()
  to prevent use-after-free during CPU blur loop
- Add @Volatile to TiltShiftRenderer.currentTexCoords (UI/GL thread race)
- Fix error dismiss race with cancellable Job tracking

Lifecycle & resource management:
- Release GL resources via glSurfaceView.queueEvent (must run on GL thread)
- Pause GLSurfaceView when entering gallery preview mode
- Shut down captureExecutor in CameraManager.release() (thread leak)
- Use WeakReference for lifecycleOwnerRef to avoid Activity GC delay
- Fix thumbnail bitmap leak on coroutine cancellation (add to finally)
- Guarantee imageProxy.close() in finally block

Performance:
- Compute gradient mask at 1/4 resolution with bilinear upscale (~93%
  less per-pixel trig work, ~75% less mask memory)
- Precompute cos/sin on CPU, pass as uCosAngle/uSinAngle uniforms
  (eliminates per-fragment transcendental calls in GLSL)
- Unroll 9-tap Gaussian blur kernel (avoids integer-branched weight
  lookup that de-optimizes on mobile GPUs)
- Add 80ms debounce to preview recomputation during slider drags

Silent failure fixes:
- Check bitmap.compress() return value; report error on failure
- Log all loadBitmapFromUri null paths (stream, dimensions, decode)
- Surface preview computation errors and ActivityNotFoundException to user
- Return boolean from writeExifToUri, log at ERROR level
- Wrap gallery preview downscale in try-catch (OOM protection)

Config:
- Add ACCESS_MEDIA_LOCATION permission (GPS EXIF on Android 10+)
- Accept coarse-only location grant for geotags
- Remove dead adjustResize (no effect with edge-to-edge)
- Set windowBackground to black (eliminates white flash on cold start)
- Add values-night theme for dark mode
- Remove overly broad ProGuard keeps (CameraX/GMS ship consumer rules)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:44:12 +01:00
0cd41e9814 Fix front camera rotation mirroring in shader
Add uIsFrontCamera uniform to shader and adjust coordinate
transformations for front camera's mirrored texture coordinates:
- Position transform: (1-posY, 1-posX) instead of (posY, 1-posX)
- Angle transform: -angle - 90° instead of +angle + 90°

Applied to linearFocusDistance, radialFocusDistance, and blur
direction calculations in sampleBlurred.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:07:44 +01:00
b16dd971f2 Add front camera texture coordinate handling
- Add separate texture coordinates for front and back cameras
- Front camera uses mirrored coordinates for natural selfie view
- Add setFrontCamera() method to renderer for dynamic switching
- Update texture coord buffer on GL thread when camera changes
- CameraScreen observes isFrontCamera state to trigger updates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:03:26 +01:00
3bbf4f9d14 Fix portrait orientation and reduce gesture sensitivity
- Rotate texture coordinates 90° for portrait mode display
  (camera sensors are landscape-oriented by default)
- Add sensitivity dampening constants for all gesture types:
  - Position drag: 0.3x
  - Rotation: 0.4x
  - Size pinch: 0.5x
  - Zoom pinch: 0.6x
- Track accumulated drag for smoother position changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:32:31 +01:00
07e10ac9c3 Initial implementation of Tilt-Shift Camera Android app
A dedicated camera app for tilt-shift photography with:
- Real-time OpenGL ES 2.0 shader-based blur preview
- Touch gesture controls (drag, rotate, pinch) for adjusting effect
- CameraX integration for camera preview and high-res capture
- EXIF metadata with GPS location support
- MediaStore integration for saving to gallery
- Jetpack Compose UI with haptic feedback

Tech stack: Kotlin, CameraX, OpenGL ES 2.0, Jetpack Compose
Min SDK: 26 (Android 8.0), Target SDK: 35 (Android 15)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:26:41 +01:00