Skip to content

Commit a7ea1b1

Browse files
hotfix: cherry pick updated yuvtorgb converter, fix resource not released
1 parent 5f3413e commit a7ea1b1

File tree

2 files changed

+63
-41
lines changed

2 files changed

+63
-41
lines changed

android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class MobileScanner(
153153

154154
bmResult.recycle()
155155
imageProxy.close()
156+
imageFormat.release()
156157

157158
mobileScannerCallback(
158159
barcodeMap,

android/src/main/kotlin/dev/steenbakker/mobile_scanner/utils/YuvToRgbConverter.kt

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@ package dev.steenbakker.mobile_scanner.utils
22

33
import android.content.Context
44
import android.graphics.Bitmap
5-
import android.graphics.ImageFormat
65
import android.media.Image
7-
import android.os.Build
86
import android.renderscript.Allocation
97
import android.renderscript.Element
108
import android.renderscript.RenderScript
119
import android.renderscript.ScriptIntrinsicYuvToRGB
1210
import android.renderscript.Type
13-
import androidx.annotation.RequiresApi
1411
import java.nio.ByteBuffer
1512

16-
1713
/**
1814
* Helper class used to efficiently convert a [Media.Image] object from
1915
* YUV_420_888 format to an RGB [Bitmap] object.
@@ -23,59 +19,84 @@ import java.nio.ByteBuffer
2319
* The [yuvToRgb] method is able to achieve the same FPS as the CameraX image
2420
* analysis use case at the default analyzer resolution, which is 30 FPS with
2521
* 640x480 on a Pixel 3 XL device.
26-
*/class YuvToRgbConverter(context: Context) {
22+
*/
23+
/// TODO: Upgrade to implementation without deprecated android.renderscript, but with same or better performance. See https://github.com/juliansteenbakker/mobile_scanner/issues/1142
24+
class YuvToRgbConverter(context: Context) {
25+
@Suppress("DEPRECATION")
2726
private val rs = RenderScript.create(context)
27+
@Suppress("DEPRECATION")
2828
private val scriptYuvToRgb =
2929
ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
3030

31-
// Do not add getters/setters functions to these private variables
32-
// because yuvToRgb() assume they won't be modified elsewhere
3331
private var yuvBits: ByteBuffer? = null
3432
private var bytes: ByteArray = ByteArray(0)
33+
@Suppress("DEPRECATION")
3534
private var inputAllocation: Allocation? = null
35+
@Suppress("DEPRECATION")
3636
private var outputAllocation: Allocation? = null
3737

3838
@Synchronized
3939
fun yuvToRgb(image: Image, output: Bitmap) {
40-
val yuvBuffer = YuvByteBuffer(image, yuvBits)
41-
yuvBits = yuvBuffer.buffer
40+
try {
41+
val yuvBuffer = YuvByteBuffer(image, yuvBits)
42+
yuvBits = yuvBuffer.buffer
4243

43-
if (needCreateAllocations(image, yuvBuffer)) {
44-
val yuvType = Type.Builder(rs, Element.U8(rs))
45-
.setX(image.width)
46-
.setY(image.height)
47-
.setYuvFormat(yuvBuffer.type)
48-
inputAllocation = Allocation.createTyped(
49-
rs,
50-
yuvType.create(),
51-
Allocation.USAGE_SCRIPT
52-
)
53-
bytes = ByteArray(yuvBuffer.buffer.capacity())
54-
val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
55-
.setX(image.width)
56-
.setY(image.height)
57-
outputAllocation = Allocation.createTyped(
58-
rs,
59-
rgbaType.create(),
60-
Allocation.USAGE_SCRIPT
61-
)
62-
}
44+
if (needCreateAllocations(image, yuvBuffer)) {
45+
createAllocations(image, yuvBuffer)
46+
}
6347

64-
yuvBuffer.buffer.get(bytes)
65-
inputAllocation!!.copyFrom(bytes)
48+
yuvBuffer.buffer.get(bytes)
49+
@Suppress("DEPRECATION")
50+
inputAllocation!!.copyFrom(bytes)
6651

67-
// Convert NV21 or YUV_420_888 format to RGB
68-
inputAllocation!!.copyFrom(bytes)
69-
scriptYuvToRgb.setInput(inputAllocation)
70-
scriptYuvToRgb.forEach(outputAllocation)
71-
outputAllocation!!.copyTo(output)
52+
@Suppress("DEPRECATION")
53+
scriptYuvToRgb.setInput(inputAllocation)
54+
@Suppress("DEPRECATION")
55+
scriptYuvToRgb.forEach(outputAllocation)
56+
@Suppress("DEPRECATION")
57+
outputAllocation!!.copyTo(output)
58+
} catch (e: Exception) {
59+
throw IllegalStateException("Failed to convert YUV to RGB", e)
60+
}
7261
}
7362

7463
private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
75-
return (inputAllocation == null || // the very 1st call
76-
inputAllocation!!.type.x != image.width || // image size changed
77-
inputAllocation!!.type.y != image.height ||
78-
inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
79-
bytes.size == yuvBuffer.buffer.capacity())
64+
@Suppress("DEPRECATION")
65+
return inputAllocation?.type?.x != image.width ||
66+
inputAllocation?.type?.y != image.height ||
67+
inputAllocation?.type?.yuv != yuvBuffer.type
68+
}
69+
70+
private fun createAllocations(image: Image, yuvBuffer: YuvByteBuffer) {
71+
@Suppress("DEPRECATION")
72+
val yuvType = Type.Builder(rs, Element.U8(rs))
73+
.setX(image.width)
74+
.setY(image.height)
75+
.setYuvFormat(yuvBuffer.type)
76+
@Suppress("DEPRECATION")
77+
inputAllocation = Allocation.createTyped(
78+
rs,
79+
yuvType.create(),
80+
Allocation.USAGE_SCRIPT
81+
)
82+
bytes = ByteArray(yuvBuffer.buffer.capacity())
83+
@Suppress("DEPRECATION")
84+
val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
85+
.setX(image.width)
86+
.setY(image.height)
87+
@Suppress("DEPRECATION")
88+
outputAllocation = Allocation.createTyped(
89+
rs,
90+
rgbaType.create(),
91+
Allocation.USAGE_SCRIPT
92+
)
93+
}
94+
95+
@Suppress("DEPRECATION")
96+
fun release() {
97+
inputAllocation?.destroy()
98+
outputAllocation?.destroy()
99+
scriptYuvToRgb.destroy()
100+
rs.destroy()
80101
}
81102
}

0 commit comments

Comments
 (0)