Add bitmap safety in applyTiltShiftEffect()
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>
This commit is contained in:
parent
f53d6f0b1b
commit
593f2c5b1f
1 changed files with 63 additions and 45 deletions
|
|
@ -152,32 +152,38 @@ class ImageCaptureHandler(
|
||||||
/**
|
/**
|
||||||
* Applies tilt-shift blur effect to a bitmap.
|
* Applies tilt-shift blur effect to a bitmap.
|
||||||
* Supports both linear and radial modes.
|
* Supports both linear and radial modes.
|
||||||
|
*
|
||||||
|
* All intermediate bitmaps are tracked and recycled in a finally block
|
||||||
|
* so that an OOM or other exception does not leak native memory.
|
||||||
*/
|
*/
|
||||||
private fun applyTiltShiftEffect(source: Bitmap, params: BlurParameters): Bitmap {
|
private fun applyTiltShiftEffect(source: Bitmap, params: BlurParameters): Bitmap {
|
||||||
val width = source.width
|
val width = source.width
|
||||||
val height = source.height
|
val height = source.height
|
||||||
|
|
||||||
// Create output bitmap
|
var result: Bitmap? = null
|
||||||
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
var scaled: Bitmap? = null
|
||||||
|
var blurred: Bitmap? = null
|
||||||
|
var blurredFullSize: Bitmap? = null
|
||||||
|
var mask: Bitmap? = null
|
||||||
|
|
||||||
// For performance, we use a scaled-down version for blur and composite
|
try {
|
||||||
val scaleFactor = 4 // Blur a 1/4 size image for speed
|
result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
|
val scaleFactor = 4
|
||||||
val blurredWidth = width / scaleFactor
|
val blurredWidth = width / scaleFactor
|
||||||
val blurredHeight = height / scaleFactor
|
val blurredHeight = height / scaleFactor
|
||||||
|
|
||||||
// Create scaled bitmap for blur
|
scaled = Bitmap.createScaledBitmap(source, blurredWidth, blurredHeight, true)
|
||||||
val scaled = Bitmap.createScaledBitmap(source, blurredWidth, blurredHeight, true)
|
|
||||||
|
|
||||||
// Apply stack blur (fast approximation)
|
blurred = stackBlur(scaled, (params.blurAmount * 25).toInt().coerceIn(1, 25))
|
||||||
val blurred = stackBlur(scaled, (params.blurAmount * 25).toInt().coerceIn(1, 25))
|
|
||||||
scaled.recycle()
|
scaled.recycle()
|
||||||
|
scaled = null
|
||||||
|
|
||||||
// Scale blurred back up
|
blurredFullSize = Bitmap.createScaledBitmap(blurred, width, height, true)
|
||||||
val blurredFullSize = Bitmap.createScaledBitmap(blurred, width, height, true)
|
|
||||||
blurred.recycle()
|
blurred.recycle()
|
||||||
|
blurred = null
|
||||||
|
|
||||||
// Create gradient mask based on tilt-shift parameters
|
mask = createGradientMask(width, height, params)
|
||||||
val mask = createGradientMask(width, height, params)
|
|
||||||
|
|
||||||
// Composite: blend original with blurred based on mask
|
// Composite: blend original with blurred based on mask
|
||||||
val pixels = IntArray(width * height)
|
val pixels = IntArray(width * height)
|
||||||
|
|
@ -189,7 +195,9 @@ class ImageCaptureHandler(
|
||||||
mask.getPixels(maskPixels, 0, width, 0, 0, width, height)
|
mask.getPixels(maskPixels, 0, width, 0, 0, width, height)
|
||||||
|
|
||||||
blurredFullSize.recycle()
|
blurredFullSize.recycle()
|
||||||
|
blurredFullSize = null
|
||||||
mask.recycle()
|
mask.recycle()
|
||||||
|
mask = null
|
||||||
|
|
||||||
for (i in pixels.indices) {
|
for (i in pixels.indices) {
|
||||||
val maskAlpha = (maskPixels[i] and 0xFF) / 255f
|
val maskAlpha = (maskPixels[i] and 0xFF) / 255f
|
||||||
|
|
@ -208,7 +216,17 @@ class ImageCaptureHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
result.setPixels(pixels, 0, width, 0, 0, width, height)
|
result.setPixels(pixels, 0, width, 0, 0, width, height)
|
||||||
return result
|
|
||||||
|
val output = result
|
||||||
|
result = null // prevent finally from recycling the returned bitmap
|
||||||
|
return output
|
||||||
|
} finally {
|
||||||
|
result?.recycle()
|
||||||
|
scaled?.recycle()
|
||||||
|
blurred?.recycle()
|
||||||
|
blurredFullSize?.recycle()
|
||||||
|
mask?.recycle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue