New debug mode for extended live analysis overlay
This commit is contained in:
@@ -46,6 +46,7 @@ import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
@@ -149,17 +150,15 @@ fun bindCameraUseCases(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnalysisOverlay(liveAnalysisState: LiveAnalysisState) {
|
||||
fun AnalysisOverlay(liveAnalysisState: LiveAnalysisState, debugMode: Boolean) {
|
||||
val binaryMask = liveAnalysisState.binaryMask
|
||||
if (binaryMask == null) {
|
||||
return
|
||||
}
|
||||
val maskOverlay = replaceColor(binaryMask, Color.Black, Color.Transparent)
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
drawImage(
|
||||
maskOverlay.scale(size.width.toInt(), size.height.toInt()).asImageBitmap(),
|
||||
colorFilter = ColorFilter.tint(Color(0x8000FF00), BlendMode.SrcIn)
|
||||
)
|
||||
if (debugMode) {
|
||||
drawMask(this, binaryMask)
|
||||
}
|
||||
if (liveAnalysisState.documentQuad != null) {
|
||||
val scaledQuad = liveAnalysisState.documentQuad.scaledTo(
|
||||
fromWidth = binaryMask.width,
|
||||
@@ -174,6 +173,15 @@ fun AnalysisOverlay(liveAnalysisState: LiveAnalysisState) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawMask(drawScope: DrawScope, binaryMask: Bitmap) {
|
||||
val maskOverlay = replaceColor(binaryMask, Color.Black, Color.Transparent)
|
||||
val size = drawScope.size
|
||||
drawScope.drawImage(
|
||||
maskOverlay.scale(size.width.toInt(), size.height.toInt()).asImageBitmap(),
|
||||
colorFilter = ColorFilter.tint(Color(0x8000FF00), BlendMode.SrcIn)
|
||||
)
|
||||
}
|
||||
|
||||
fun replaceColor(bitmap: Bitmap, toReplace: Color, replacement: Color): Bitmap {
|
||||
val width = bitmap.width
|
||||
val height = bitmap.height
|
||||
|
||||
@@ -86,6 +86,7 @@ data class CameraUiState(
|
||||
val liveAnalysisState: LiveAnalysisState,
|
||||
val captureState: CaptureState,
|
||||
val showDetectionError: Boolean,
|
||||
val isDebugMode: Boolean
|
||||
)
|
||||
|
||||
const val CAPTURED_IMAGE_DISPLAY_DURATION = 1500L
|
||||
@@ -101,6 +102,7 @@ fun CameraScreen(
|
||||
var previewView by remember { mutableStateOf<PreviewView?>(null) }
|
||||
val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
|
||||
val thumbnailCoords = remember { mutableStateOf(Offset.Zero) }
|
||||
var isDebugMode by remember { mutableStateOf(false) }
|
||||
|
||||
val captureController = remember { CameraCaptureController() }
|
||||
DisposableEffect(Unit) {
|
||||
@@ -149,7 +151,12 @@ fun CameraScreen(
|
||||
{ offset -> thumbnailCoords.value = offset }
|
||||
)
|
||||
},
|
||||
cameraUiState = CameraUiState(pageIds.size, liveAnalysisState, captureState, showDetectionError),
|
||||
cameraUiState = CameraUiState(
|
||||
pageIds.size,
|
||||
liveAnalysisState,
|
||||
captureState,
|
||||
showDetectionError,
|
||||
isDebugMode),
|
||||
onCapture = {
|
||||
previewView?.bitmap?.let {
|
||||
Log.i("MyScan", "Pressed <Capture>")
|
||||
@@ -160,6 +167,7 @@ fun CameraScreen(
|
||||
}
|
||||
},
|
||||
onFinalizePressed = onFinalizePressed,
|
||||
onDebugModeSwitched = { isDebugMode = !isDebugMode },
|
||||
thumbnailCoords = thumbnailCoords,
|
||||
)
|
||||
}
|
||||
@@ -171,6 +179,7 @@ private fun CameraScreenScaffold(
|
||||
cameraUiState: CameraUiState,
|
||||
onCapture: () -> Unit,
|
||||
onFinalizePressed: () -> Unit,
|
||||
onDebugModeSwitched: () -> Unit,
|
||||
thumbnailCoords: MutableState<Offset>,
|
||||
) {
|
||||
Box {
|
||||
@@ -180,6 +189,7 @@ private fun CameraScreenScaffold(
|
||||
pageList = pageList,
|
||||
pageCount = cameraUiState.pageCount,
|
||||
onFinalizePressed = onFinalizePressed,
|
||||
onDebugModeSwitched = onDebugModeSwitched,
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
@@ -189,7 +199,9 @@ private fun CameraScreenScaffold(
|
||||
.fillMaxSize()
|
||||
) {
|
||||
CameraPreviewWithOverlay(cameraPreview, cameraUiState)
|
||||
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
|
||||
if (cameraUiState.isDebugMode) {
|
||||
MessageBox(cameraUiState.liveAnalysisState.inferenceTime)
|
||||
}
|
||||
CaptureButton(
|
||||
onClick = onCapture,
|
||||
modifier = Modifier
|
||||
@@ -228,16 +240,16 @@ private fun CapturedImage(image: ImageBitmap, thumbnailCoords: MutableState<Offs
|
||||
val density = LocalDensity.current
|
||||
Box (contentAlignment = Alignment.BottomStart,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight(0.8f)
|
||||
.onGloballyPositioned { coordinates ->
|
||||
val bounds = coordinates.boundsInWindow()
|
||||
val centerX = bounds.left + bounds.width / 2
|
||||
val centerY = bounds.top + bounds.height / 2
|
||||
with(density) {
|
||||
targetOffsetX = thumbnailCoords.value.x - centerX
|
||||
targetOffsetY = thumbnailCoords.value.y - centerY
|
||||
.fillMaxHeight(0.8f)
|
||||
.onGloballyPositioned { coordinates ->
|
||||
val bounds = coordinates.boundsInWindow()
|
||||
val centerX = bounds.left + bounds.width / 2
|
||||
val centerY = bounds.top + bounds.height / 2
|
||||
with(density) {
|
||||
targetOffsetX = thumbnailCoords.value.x - centerX
|
||||
targetOffsetY = thumbnailCoords.value.y - centerY
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
bitmap = image,
|
||||
@@ -309,7 +321,7 @@ private fun CameraPreviewWithOverlay(
|
||||
.height(height.dp)
|
||||
) {
|
||||
cameraPreview()
|
||||
AnalysisOverlay(cameraUiState.liveAnalysisState)
|
||||
AnalysisOverlay(cameraUiState.liveAnalysisState, cameraUiState.isDebugMode)
|
||||
captureState.frozenImage?.let {
|
||||
Image(
|
||||
bitmap = it.asImageBitmap(),
|
||||
@@ -358,7 +370,25 @@ fun CameraScreenFooter(
|
||||
pageList: @Composable () -> Unit,
|
||||
pageCount: Int,
|
||||
onFinalizePressed: () -> Unit,
|
||||
onDebugModeSwitched: () -> Unit,
|
||||
) {
|
||||
var tapCount by remember { mutableStateOf(0) }
|
||||
var lastTapTime by remember { mutableStateOf(0L) }
|
||||
val tapThreshold = 500L
|
||||
val onPageCountClick = {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastTapTime < tapThreshold) {
|
||||
tapCount++
|
||||
if (tapCount >= 3) {
|
||||
onDebugModeSwitched()
|
||||
tapCount = 0
|
||||
}
|
||||
} else {
|
||||
tapCount = 1
|
||||
}
|
||||
lastTapTime = currentTime
|
||||
}
|
||||
|
||||
Column (modifier = Modifier.background(MaterialTheme.colorScheme.primaryContainer)) {
|
||||
pageList()
|
||||
BottomAppBar(
|
||||
@@ -373,7 +403,8 @@ fun CameraScreenFooter(
|
||||
) {
|
||||
Text(
|
||||
text = "$pageCount pages",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.clickable(onClick = onPageCountClick)
|
||||
)
|
||||
|
||||
Button (
|
||||
@@ -432,9 +463,10 @@ private fun ScreenPreview(captureState: CaptureState) {
|
||||
listState = LazyListState(),
|
||||
)
|
||||
},
|
||||
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState, false),
|
||||
cameraUiState = CameraUiState(pageCount = 4, LiveAnalysisState(), captureState, false, false),
|
||||
onCapture = {},
|
||||
onFinalizePressed = {},
|
||||
onDebugModeSwitched = {},
|
||||
thumbnailCoords = thumbnailCoords,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user