Skip to content

Commit a865756

Browse files
committed
chore(kikcode): Define ScanQuality from header; allow providing it to scanKikCode
simplify/reduce PlanarYUVLuminanceSource required args Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 401a33d commit a865756

3 files changed

Lines changed: 69 additions & 32 deletions

File tree

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
11
package com.kik.kikx.kikcodes
22

33
import com.kik.kikx.models.ScannableKikCode
4+
import com.kik.scan.KikCode
5+
import com.kik.scan.Scanner.ScanResult
6+
7+
sealed class ScanQuality(val headerValue: Int) {
8+
data object Low : ScanQuality(0)
9+
data object Medium : ScanQuality(3)
10+
data object High : ScanQuality(8)
11+
data object Best : ScanQuality(10)
12+
13+
companion object {
14+
private val values = listOf(Best, High, Medium, Low)
15+
16+
fun iterator(): Iterator<ScanQuality> {
17+
return values.iterator()
18+
}
19+
}
20+
}
21+
22+
open class ScannerError(override val message: String) : Exception(message)
423

524
interface KikCodeScanner {
6-
class NoKikCodeFoundException : Exception("No Kik Code found in image buffer")
25+
class NoKikCodeFoundException : ScannerError("No Kik Code found in image buffer")
26+
class FailedToParseCodeException(val scanResult: ScanResult) :
27+
ScannerError("Code found in image buffer, but failed to parse")
28+
29+
class UnsupportedKikCodeFoundException(val kikCode: KikCode) : ScannerError("Code found in unsupported")
730

8-
suspend fun scanKikCode(imageData: ByteArray, width: Int, height: Int): Result<ScannableKikCode>
31+
suspend fun scanKikCode(
32+
imageData: ByteArray, width: Int, height: Int, quality: ScanQuality = ScanQuality.Medium
33+
): Result<ScannableKikCode>
934
}

app/src/main/java/com/kik/kikx/kikcodes/implementation/KikCodeScannerImpl.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.kik.kikx.kikcodes.implementation
22

33
import android.util.Base64
44
import com.kik.kikx.kikcodes.KikCodeScanner
5+
import com.kik.kikx.kikcodes.ScanQuality
56
import com.kik.kikx.models.GroupInviteCode
67
import com.kik.kikx.models.ScannableKikCode
78
import com.kik.scan.GroupKikCode
@@ -12,10 +13,6 @@ import com.kik.scan.UsernameKikCode
1213

1314
class KikCodeScannerImpl : KikCodeScanner {
1415

15-
companion object {
16-
private const val SCAN_QUALITY = 3
17-
}
18-
1916
private fun KikCode.toModelKikCode(): ScannableKikCode {
2017
return when (this) {
2118
is GroupKikCode -> {
@@ -27,16 +24,23 @@ class KikCodeScannerImpl : KikCodeScanner {
2724
}
2825
is UsernameKikCode -> ScannableKikCode.UsernameKikCode(username, nonce, colour)
2926
is RemoteKikCode -> ScannableKikCode.RemoteKikCode(payloadId, colour)
30-
else -> throw Exception("Unsupported Kik code type")
27+
else -> throw KikCodeScanner.UnsupportedKikCodeFoundException(this)
3128
}
3229
}
3330

34-
override suspend fun scanKikCode(imageData: ByteArray, width: Int, height: Int): Result<ScannableKikCode> {
35-
val source = PlanarYUVLuminanceSource(imageData, width, height, 0, 0, width, height, false)
31+
override suspend fun scanKikCode(imageData: ByteArray, width: Int, height: Int, quality: ScanQuality): Result<ScannableKikCode> {
32+
val source = PlanarYUVLuminanceSource(imageData, width, height)
33+
34+
try {
35+
val scanResult = Scanner.scan(source.matrix, width, height, quality.headerValue)
36+
?: return Result.failure(KikCodeScanner.NoKikCodeFoundException())
37+
38+
val kikCode = KikCode.parse(scanResult.data)
39+
?: return Result.failure(KikCodeScanner.FailedToParseCodeException(scanResult))
40+
41+
val scannable = kikCode.toModelKikCode() // will throw UnsupportedKikCodeFoundException
3642

37-
return try {
38-
val scanResult = Scanner.scan(source.matrix, width, height, SCAN_QUALITY) ?: throw KikCodeScanner.NoKikCodeFoundException()
39-
runCatching { KikCode.parse(scanResult.data)?.toModelKikCode() ?: throw KikCodeScanner.NoKikCodeFoundException() }
43+
return Result.success(scannable)
4044
} catch (e: Exception) {
4145
return Result.failure(e)
4246
}

app/src/main/java/com/kik/kikx/kikcodes/implementation/PlanarYUVLuminanceSource.kt

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,16 @@ import kotlin.experimental.and
3333
* jmeyer: NOTE
3434
* This class used to extend LuminanceSource. It has been trimmed down to not require a ZXing import
3535
*/
36-
class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dataWidth: Int, private val _dataHeight: Int, private val _left: Int, private val _top: Int, private val width: Int, private val height: Int, reverseHorizontal: Boolean) {
37-
36+
class PlanarYUVLuminanceSource(
37+
private val yuvData: ByteArray,
38+
private val width: Int,
39+
private val height: Int,
40+
private val left: Int = 0,
41+
private val top: Int = 0,
42+
private val dataWidth: Int = width,
43+
private val dataHeight: Int = height,
44+
reverseHorizontal: Boolean = false
45+
) {
3846
// If the caller asks for the entire underlying image, save the copy and give them the
3947
// original data. The docs specifically warn that result.length must be ignored.
4048
// If the width matches the full width of the underlying data, perform a single copy.
@@ -43,22 +51,22 @@ class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dat
4351
get() {
4452
val width = width
4553
val height = height
46-
if (width == _dataWidth && height == _dataHeight) {
47-
return _yuvData
54+
if (width == dataWidth && height == dataHeight) {
55+
return yuvData
4856
}
4957

5058
val area = width * height
5159
val matrix = ByteArray(area)
52-
var inputOffset = _top * _dataWidth + _left
53-
if (width == _dataWidth) {
54-
System.arraycopy(_yuvData, inputOffset, matrix, 0, area)
60+
var inputOffset = top * dataWidth + left
61+
if (width == dataWidth) {
62+
System.arraycopy(yuvData, inputOffset, matrix, 0, area)
5563
return matrix
5664
}
57-
val yuv = _yuvData
65+
val yuv = yuvData
5866
for (y in 0 until height) {
5967
val outputOffset = y * width
6068
System.arraycopy(yuv, inputOffset, matrix, outputOffset, width)
61-
inputOffset += _dataWidth
69+
inputOffset += dataWidth
6270
}
6371
return matrix
6472
}
@@ -68,7 +76,7 @@ class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dat
6876

6977
init {
7078

71-
if (_left + width > _dataWidth || _top + height > _dataHeight) {
79+
if (left + width > dataWidth || top + height > dataHeight) {
7280
// LogUtils.throwOrLog(IllegalArgumentException("Crop rectangle does not fit within image data."))
7381
}
7482
if (reverseHorizontal) {
@@ -85,29 +93,29 @@ class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dat
8593
if (row == null || row.size < width) {
8694
row = ByteArray(width)
8795
}
88-
val offset = (y + _top) * _dataWidth + _left
89-
System.arraycopy(_yuvData, offset, row, 0, width)
96+
val offset = (y + top) * dataWidth + left
97+
System.arraycopy(yuvData, offset, row, 0, width)
9098
return row
9199
}
92100

93-
fun crop(left: Int, top: Int, width: Int, height: Int): PlanarYUVLuminanceSource {
94-
return PlanarYUVLuminanceSource(_yuvData, _dataWidth, _dataHeight, this._left + left, this._top + top, width, height, false)
101+
fun crop(left: Int = 0, top: Int = 0, width: Int, height: Int): PlanarYUVLuminanceSource {
102+
return PlanarYUVLuminanceSource(yuvData, dataWidth, dataHeight, this.left + left, this.top + top, width, height, false)
95103
}
96104

97105
fun renderCroppedGreyscaleBitmap(): Bitmap {
98106
val width = width
99107
val height = height
100108
val pixels = IntArray(width * height)
101-
val yuv = _yuvData
102-
var inputOffset = _top * _dataWidth + _left
109+
val yuv = yuvData
110+
var inputOffset = top * dataWidth + left
103111

104112
for (y in 0 until height) {
105113
val outputOffset = y * width
106114
for (x in 0 until width) {
107115
val grey = yuv[inputOffset + x] and 0xff.toByte()
108116
pixels[outputOffset + x] = -0x1000000 or grey * 0x00010101
109117
}
110-
inputOffset += _dataWidth
118+
inputOffset += dataWidth
111119
}
112120

113121
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
@@ -116,9 +124,9 @@ class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dat
116124
}
117125

118126
private fun reverseHorizontal(width: Int, height: Int) {
119-
val yuvData = this._yuvData
127+
val yuvData = this.yuvData
120128
var y = 0
121-
var rowStart = _top * _dataWidth + _left
129+
var rowStart = top * dataWidth + left
122130
while (y < height) {
123131
val middle = rowStart + width / 2
124132
var x1 = rowStart
@@ -131,7 +139,7 @@ class PlanarYUVLuminanceSource(private val _yuvData: ByteArray, private val _dat
131139
x2--
132140
}
133141
y++
134-
rowStart += _dataWidth
142+
rowStart += dataWidth
135143
}
136144
}
137145
}

0 commit comments

Comments
 (0)