Add bitmap safety in onCaptureSuccess callback
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 <noreply@anthropic.com>
This commit is contained in:
parent
593f2c5b1f
commit
f0249fcd64
1 changed files with 14 additions and 5 deletions
|
|
@ -5,6 +5,7 @@ import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import android.util.Log
|
||||||
import androidx.camera.core.ImageCapture
|
import androidx.camera.core.ImageCapture
|
||||||
import androidx.camera.core.ImageCaptureException
|
import androidx.camera.core.ImageCaptureException
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
|
|
@ -28,6 +29,10 @@ class ImageCaptureHandler(
|
||||||
private val photoSaver: PhotoSaver
|
private val photoSaver: PhotoSaver
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "ImageCaptureHandler"
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the processed bitmap ready for saving, produced inside the
|
* Holds the processed bitmap ready for saving, produced inside the
|
||||||
* camera callback (synchronous CPU work) and consumed afterwards
|
* camera callback (synchronous CPU work) and consumed afterwards
|
||||||
|
|
@ -58,26 +63,30 @@ class ImageCaptureHandler(
|
||||||
executor,
|
executor,
|
||||||
object : ImageCapture.OnImageCapturedCallback() {
|
object : ImageCapture.OnImageCapturedCallback() {
|
||||||
override fun onCaptureSuccess(imageProxy: ImageProxy) {
|
override fun onCaptureSuccess(imageProxy: ImageProxy) {
|
||||||
|
var currentBitmap: Bitmap? = null
|
||||||
try {
|
try {
|
||||||
val imageRotation = imageProxy.imageInfo.rotationDegrees
|
val imageRotation = imageProxy.imageInfo.rotationDegrees
|
||||||
|
|
||||||
var bitmap = imageProxyToBitmap(imageProxy)
|
currentBitmap = imageProxyToBitmap(imageProxy)
|
||||||
imageProxy.close()
|
imageProxy.close()
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (currentBitmap == null) {
|
||||||
continuation.resume(
|
continuation.resume(
|
||||||
SaveResult.Error("Failed to convert image") as Any
|
SaveResult.Error("Failed to convert image") as Any
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = rotateBitmap(bitmap, imageRotation, isFrontCamera)
|
currentBitmap = rotateBitmap(currentBitmap, imageRotation, isFrontCamera)
|
||||||
|
|
||||||
val processedBitmap = applyTiltShiftEffect(bitmap, blurParams)
|
val processedBitmap = applyTiltShiftEffect(currentBitmap, blurParams)
|
||||||
bitmap.recycle()
|
currentBitmap.recycle()
|
||||||
|
currentBitmap = null
|
||||||
|
|
||||||
continuation.resume(ProcessedCapture(processedBitmap))
|
continuation.resume(ProcessedCapture(processedBitmap))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Image processing failed", e)
|
||||||
|
currentBitmap?.recycle()
|
||||||
continuation.resume(
|
continuation.resume(
|
||||||
SaveResult.Error("Capture failed: ${e.message}", e) as Any
|
SaveResult.Error("Capture failed: ${e.message}", e) as Any
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue