From f0249fcd647f377e14c704b6714e8100903001f3 Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Fri, 27 Feb 2026 15:20:57 +0100 Subject: [PATCH] Add bitmap safety in onCaptureSuccess callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track the current bitmap through the decode→rotate→effect pipeline with a nullable variable. On exception, the in-flight bitmap is recycled in the catch block to prevent native memory leaks. Errors are now logged with Log.e and a proper companion TAG. Co-Authored-By: Claude Opus 4.6 --- .../tiltshift/camera/ImageCaptureHandler.kt | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/no/naiv/tiltshift/camera/ImageCaptureHandler.kt b/app/src/main/java/no/naiv/tiltshift/camera/ImageCaptureHandler.kt index af15e10..9f5c5f4 100644 --- a/app/src/main/java/no/naiv/tiltshift/camera/ImageCaptureHandler.kt +++ b/app/src/main/java/no/naiv/tiltshift/camera/ImageCaptureHandler.kt @@ -5,6 +5,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Matrix import android.location.Location +import android.util.Log import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCaptureException import androidx.camera.core.ImageProxy @@ -28,6 +29,10 @@ class ImageCaptureHandler( private val photoSaver: PhotoSaver ) { + companion object { + private const val TAG = "ImageCaptureHandler" + } + /** * Holds the processed bitmap ready for saving, produced inside the * camera callback (synchronous CPU work) and consumed afterwards @@ -58,26 +63,30 @@ class ImageCaptureHandler( executor, object : ImageCapture.OnImageCapturedCallback() { override fun onCaptureSuccess(imageProxy: ImageProxy) { + var currentBitmap: Bitmap? = null try { val imageRotation = imageProxy.imageInfo.rotationDegrees - var bitmap = imageProxyToBitmap(imageProxy) + currentBitmap = imageProxyToBitmap(imageProxy) imageProxy.close() - if (bitmap == null) { + if (currentBitmap == null) { continuation.resume( SaveResult.Error("Failed to convert image") as Any ) return } - bitmap = rotateBitmap(bitmap, imageRotation, isFrontCamera) + currentBitmap = rotateBitmap(currentBitmap, imageRotation, isFrontCamera) - val processedBitmap = applyTiltShiftEffect(bitmap, blurParams) - bitmap.recycle() + val processedBitmap = applyTiltShiftEffect(currentBitmap, blurParams) + currentBitmap.recycle() + currentBitmap = null continuation.resume(ProcessedCapture(processedBitmap)) } catch (e: Exception) { + Log.e(TAG, "Image processing failed", e) + currentBitmap?.recycle() continuation.resume( SaveResult.Error("Capture failed: ${e.message}", e) as Any )