Commit graph

7 commits

Author SHA1 Message Date
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