Track all intermediate bitmaps with nullable variables and recycle
them in a finally block. This prevents native memory leaks when an
OOM or other exception occurs mid-processing. Variables are set to
null after recycle or handoff to the caller.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
runBlocking on the camera callback thread could deadlock when
saveBitmap() needed the main thread. Split capturePhoto() into two
phases: synchronous CPU work (decode/rotate/effect) inside the
suspendCancellableCoroutine callback, and suspend-safe saveBitmap()
after the continuation resumes in coroutine context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Load signing credentials from local.properties (not committed)
- Keystore stored in .signing/ directory (not committed)
- Release builds are now signed and installable
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- 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>
Apply screen aspect ratio correction to offset.y (screen X direction)
instead of offset.x (screen Y direction) in both linear and radial
mode distance calculations. This fixes angle distortion at non-90°
rotations in portrait mode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Transform screen coordinates to texture coordinates (90° CW rotation):
- Position: (x,y) -> (y, 1-x)
- Angle: θ -> θ + 90°
Applied in linearFocusDistance, radialFocusDistance, and sampleBlurred
to fix preview not matching UI overlay position and rotation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use rememberUpdatedState in ControlPanel to prevent stale closure
capture during continuous slider drags. This ensures the latest
blur parameters are used when updating, avoiding conflicts with
concurrent gesture updates from TiltShiftOverlay.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add radial/elliptical blur mode with aspect ratio control
- Add UI sliders for blur intensity, falloff, and shape
- Add front camera support with flip button
- Update minimum SDK to API 35 (Android 15)
- Enable landscape orientation (fullSensor)
- Rename app to "Naiv Tilt Shift Camera"
- Set APK output name to naiv-tilt-shift
- Add project specification document
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
State persistence:
- Use rememberUpdatedState to always get latest params inside pointerInput
- Capture gestureStartParams at beginning of each gesture
- All adjustments now use initial values + accumulated change
Drag tracking:
- Track initialDragCentroid at drag start
- Calculate total drag offset from initial point (not frame-by-frame)
- Drag now properly moves focus center 1:1
Shader rotation sync:
- Adjust angle by -90° in shader to compensate for portrait texture rotation
- Preview blur effect now rotates in sync with overlay UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rotation:
- Use direct angle calculation between touch points (atan2)
- Track initial touch angle and effect angle separately
- Effect rotation now matches finger rotation exactly
Position drag:
- Remove all sensitivity dampening
- 1:1 mapping: finger moves 100px, effect center moves 100px
Gesture zones rebalanced:
- Rotation: only very center of focus zone (< 30% of focus height)
- Size adjustment: large area around effect (30% - 200%)
- Camera zoom: only far outside the effect (> 200%)
This prevents rotation from dominating size adjustments.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2D positioning:
- Add positionX parameter to BlurParameters (was only Y before)
- Update shader with uPositionX and uPositionY uniforms
- Single-finger drag now moves focus center anywhere on screen
- Update gradient mask generation for capture
Rotation tracking:
- Remove dampening from rotation gesture (1:1 tracking)
- Rotate gesture now directly tracks finger movement
- Preview effect rotates in sync with overlay
Overlay and shader sync:
- Both now use same positionX, positionY, angle parameters
- Preview blur effect matches overlay visualization
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move #extension directive to first line (required by GLSL)
- Replace array initializer syntax with getWeight() function
(float[]() constructor not supported in GLSL ES 1.00)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>