Skip to content

Commit ca71bdc

Browse files
committed
fix crash when rounding NaN
1 parent 35ce2fa commit ca71bdc

File tree

2 files changed

+80
-11
lines changed

2 files changed

+80
-11
lines changed

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,35 @@ class MobileScanner(
170170
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
171171
}
172172

173-
// scales the scanWindow to the provided inputImage and checks if that scaled
174-
// scanWindow contains the barcode
175-
private fun isBarcodeInScanWindow(
173+
// Scales the scanWindow to the provided inputImage and checks if that scaled
174+
// scanWindow contains the barcode.
175+
@VisibleForTesting
176+
fun isBarcodeInScanWindow(
176177
scanWindow: List<Float>,
177178
barcode: Barcode,
178179
inputImage: ImageProxy
179180
): Boolean {
181+
// TODO: use `cornerPoints` instead, since the bounding box is not bound to the coordinate system of the input image
182+
// On iOS we do this correctly, so the calculation should match that.
180183
val barcodeBoundingBox = barcode.boundingBox ?: return false
181184

182-
val imageWidth = inputImage.height
183-
val imageHeight = inputImage.width
185+
try {
186+
val imageWidth = inputImage.height
187+
val imageHeight = inputImage.width
184188

185-
val left = (scanWindow[0] * imageWidth).roundToInt()
186-
val top = (scanWindow[1] * imageHeight).roundToInt()
187-
val right = (scanWindow[2] * imageWidth).roundToInt()
188-
val bottom = (scanWindow[3] * imageHeight).roundToInt()
189+
val left = (scanWindow[0] * imageWidth).roundToInt()
190+
val top = (scanWindow[1] * imageHeight).roundToInt()
191+
val right = (scanWindow[2] * imageWidth).roundToInt()
192+
val bottom = (scanWindow[3] * imageHeight).roundToInt()
189193

190-
val scaledScanWindow = Rect(left, top, right, bottom)
191-
return scaledScanWindow.contains(barcodeBoundingBox)
194+
val scaledScanWindow = Rect(left, top, right, bottom)
195+
196+
return scaledScanWindow.contains(barcodeBoundingBox)
197+
} catch (exception: IllegalArgumentException) {
198+
// Rounding of the scan window dimensions can fail, due to encountering NaN.
199+
// If we get NaN, rather than give a false positive, just return false.
200+
return false
201+
}
192202
}
193203

194204
// Return the best resolution for the actual device orientation.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dev.steenbakker.mobile_scanner
2+
3+
import android.app.Activity
4+
import android.graphics.Rect
5+
import androidx.camera.core.ImageProxy
6+
import com.google.mlkit.vision.barcode.BarcodeScanner
7+
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
8+
import com.google.mlkit.vision.barcode.common.Barcode
9+
import kotlin.test.Test
10+
import org.mockito.Mockito
11+
import io.flutter.view.TextureRegistry
12+
import kotlin.test.expect
13+
14+
/*
15+
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
16+
*
17+
* Once you have built the plugin's example app, you can run these tests from the command
18+
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
19+
* you can run them directly from IDEs that support JUnit such as Android Studio.
20+
*/
21+
22+
internal class MobileScannerTest {
23+
@Test
24+
fun isBarcodeInScanWindow_canHandleNaNValues() {
25+
val barcodeScannerMock = Mockito.mock(BarcodeScanner::class.java)
26+
27+
val mobileScanner = MobileScanner(
28+
Mockito.mock(Activity::class.java),
29+
Mockito.mock(TextureRegistry::class.java),
30+
{ _: List<Map<String, Any?>>, _: ByteArray?, _: Int?, _: Int? -> },
31+
{ _: String -> },
32+
{ _: BarcodeScannerOptions? -> barcodeScannerMock }
33+
)
34+
35+
// Intentional suppression for the mock value in the test,
36+
// since there is no NaN constant.
37+
@Suppress("DIVISION_BY_ZERO")
38+
val notANumber = 0.0f / 0.0f
39+
40+
val barcodeMock: Barcode = Mockito.mock(Barcode::class.java)
41+
val imageMock: ImageProxy = Mockito.mock(ImageProxy::class.java)
42+
43+
// TODO: use corner points instead of bounding box
44+
45+
// Bounding box that is 100 pixels offset from the left and top,
46+
// and is 100 pixels in width and height.
47+
Mockito.`when`(barcodeMock.boundingBox).thenReturn(
48+
Rect(100, 100, 200, 300))
49+
Mockito.`when`(imageMock.height).thenReturn(400)
50+
Mockito.`when`(imageMock.width).thenReturn(400)
51+
52+
// Use a scan window that has an invalid value, but otherwise uses the entire image.
53+
val scanWindow: List<Float> = listOf(0f, notANumber, 100f, 100f)
54+
55+
expect(false) {
56+
mobileScanner.isBarcodeInScanWindow(scanWindow, barcodeMock, imageMock)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)