初始化本地仓库
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package uts.sdk.modules.masCodeScan
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* 相对于实时预览层的有效扫码区域配置。
|
||||
* 由 Activity 统一校验后同时用于扫描框绘制和条码结果过滤。
|
||||
*/
|
||||
data class ScanRegionConfig(
|
||||
val enabled: Boolean,
|
||||
val widthRatio: Float = DEFAULT_SIZE_RATIO,
|
||||
val heightRatio: Float = DEFAULT_SIZE_RATIO,
|
||||
val centerXRatio: Float = DEFAULT_CENTER_RATIO,
|
||||
val centerYRatio: Float = DEFAULT_CENTER_RATIO
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_SIZE_RATIO = 0.74f
|
||||
const val DEFAULT_CENTER_RATIO = 0.5f
|
||||
private const val MIN_SIZE_RATIO = 0.1f
|
||||
private const val MAX_SIZE_RATIO = 1f
|
||||
|
||||
fun safe(
|
||||
enabled: Boolean,
|
||||
widthRatio: Float,
|
||||
heightRatio: Float,
|
||||
centerXRatio: Float,
|
||||
centerYRatio: Float
|
||||
): ScanRegionConfig {
|
||||
val safeWidth = widthRatio.sanitize(DEFAULT_SIZE_RATIO).coerceIn(MIN_SIZE_RATIO, MAX_SIZE_RATIO)
|
||||
val safeHeight = heightRatio.sanitize(DEFAULT_SIZE_RATIO).coerceIn(MIN_SIZE_RATIO, MAX_SIZE_RATIO)
|
||||
return ScanRegionConfig(
|
||||
enabled = enabled,
|
||||
widthRatio = safeWidth,
|
||||
heightRatio = safeHeight,
|
||||
centerXRatio = centerXRatio.sanitize(DEFAULT_CENTER_RATIO).coerceIn(0f, 1f),
|
||||
centerYRatio = centerYRatio.sanitize(DEFAULT_CENTER_RATIO).coerceIn(0f, 1f)
|
||||
)
|
||||
}
|
||||
|
||||
private fun Float.sanitize(defaultValue: Float): Float {
|
||||
return if (isFinite()) this else defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
/** 返回扫描框在预览 View 坐标系中的实际位置,越界部分会收敛到 View 内。 */
|
||||
fun resolveViewRect(viewWidth: Int, viewHeight: Int): RectF {
|
||||
if (viewWidth <= 0 || viewHeight <= 0) return RectF()
|
||||
// width / height 都以预览宽度为基准,并使用同一缩放因子收敛,确保始终保持配置比例。
|
||||
val desiredWidth = viewWidth * widthRatio
|
||||
val desiredHeight = viewWidth * heightRatio
|
||||
val sizeScale = min(1f, min(viewWidth / desiredWidth, viewHeight / desiredHeight))
|
||||
val frameWidth = desiredWidth * sizeScale
|
||||
val frameHeight = desiredHeight * sizeScale
|
||||
val wantedLeft = viewWidth * centerXRatio - frameWidth / 2f
|
||||
val wantedTop = viewHeight * centerYRatio - frameHeight / 2f
|
||||
val left = wantedLeft.coerceIn(0f, max(0f, viewWidth - frameWidth))
|
||||
val top = wantedTop.coerceIn(0f, max(0f, viewHeight - frameHeight))
|
||||
return RectF(left, top, left + frameWidth, top + frameHeight)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 PreviewView 的扫描框映射到 ML Kit 返回 boundingBox 所在的旋转后图像坐标。
|
||||
* PreviewView 使用 FILL_CENTER,因此必须补偿等比填充产生的裁剪偏移。
|
||||
*/
|
||||
object ScanRegionMapper {
|
||||
fun mapViewRectToImage(
|
||||
viewRect: RectF,
|
||||
viewWidth: Int,
|
||||
viewHeight: Int,
|
||||
imageWidth: Int,
|
||||
imageHeight: Int,
|
||||
cropRect: Rect,
|
||||
rotationDegrees: Int
|
||||
): Rect? {
|
||||
if (viewRect.isEmpty || viewWidth <= 0 || viewHeight <= 0 || imageWidth <= 0 || imageHeight <= 0) {
|
||||
return null
|
||||
}
|
||||
val visualCrop = YuvFrames.rectToVisualSpace(cropRect, imageWidth, imageHeight, rotationDegrees)
|
||||
val cropWidth = visualCrop.width()
|
||||
val cropHeight = visualCrop.height()
|
||||
if (cropWidth <= 0 || cropHeight <= 0) return null
|
||||
|
||||
val scale = max(viewWidth.toFloat() / cropWidth, viewHeight.toFloat() / cropHeight)
|
||||
if (!scale.isFinite() || scale <= 0f) return null
|
||||
val offsetX = (viewWidth - cropWidth * scale) / 2f
|
||||
val offsetY = (viewHeight - cropHeight * scale) / 2f
|
||||
val mapped = Rect(
|
||||
((viewRect.left - offsetX) / scale).toInt() + visualCrop.left,
|
||||
((viewRect.top - offsetY) / scale).toInt() + visualCrop.top,
|
||||
((viewRect.right - offsetX) / scale).toInt() + visualCrop.left,
|
||||
((viewRect.bottom - offsetY) / scale).toInt() + visualCrop.top
|
||||
)
|
||||
return mapped.apply {
|
||||
left = left.coerceIn(visualCrop.left, visualCrop.right)
|
||||
top = top.coerceIn(visualCrop.top, visualCrop.bottom)
|
||||
right = right.coerceIn(left, visualCrop.right)
|
||||
bottom = bottom.coerceIn(top, visualCrop.bottom)
|
||||
}.takeIf { it.width() > 0 && it.height() > 0 }
|
||||
}
|
||||
|
||||
fun containsBarcodeCenter(region: Rect, barcodeBox: Rect): Boolean {
|
||||
return region.contains(barcodeBox.centerX(), barcodeBox.centerY())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user