226 lines
7.2 KiB
Kotlin
226 lines
7.2 KiB
Kotlin
package com.assimilate.alltrans.common
|
|
|
|
import android.graphics.Canvas
|
|
import android.graphics.Color
|
|
import android.graphics.Paint
|
|
import android.graphics.RectF
|
|
import android.graphics.Typeface
|
|
import android.text.Layout
|
|
import android.text.StaticLayout
|
|
import android.text.TextPaint
|
|
import android.util.Log
|
|
import com.assimilate.alltrans.MyApp
|
|
import com.assimilate.alltrans.curview.GraphicOverlay
|
|
import com.google.mlkit.vision.text.Text
|
|
import kotlin.math.max
|
|
import kotlin.math.min
|
|
|
|
class TextGraphic(
|
|
overlay: GraphicOverlay?,
|
|
private val text: Text,
|
|
private val textShow: Boolean,
|
|
private val needTrans: Boolean,
|
|
private val fbFrom: String
|
|
) : GraphicOverlay.Graphic(overlay) {
|
|
|
|
private val shouldGroupRecognizedTextInBlocks: Boolean =
|
|
PreferenceUtils.shouldGroupRecognizedTextInBlocks(MyApp.applicationContext())
|
|
private val showLanguageTag: Boolean =
|
|
PreferenceUtils.showLanguageTag(MyApp.applicationContext())
|
|
private val showConfidence: Boolean =
|
|
PreferenceUtils.shouldShowTextConfidence(MyApp.applicationContext())
|
|
|
|
|
|
private val textPaint: TextPaint = TextPaint().apply {
|
|
color = TEXT_COLOR
|
|
textSize = TEXT_SIZE
|
|
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
|
isFakeBoldText = true
|
|
}
|
|
private val labelPaint: Paint = Paint().apply {
|
|
color = MARKER_COLOR
|
|
style = Paint.Style.FILL
|
|
}
|
|
private var hiddenTextBlocks: MutableList<Boolean> =
|
|
MutableList(text.textBlocks.size) { !textShow }
|
|
private var isVisible: Boolean
|
|
|
|
private var isLoading: Boolean = false
|
|
|
|
// 翻译后的文本
|
|
private var translatedTextBlocks: List<String> = listOf()
|
|
|
|
|
|
init {
|
|
isVisible = textShow
|
|
|
|
if (needTrans) {
|
|
isLoading = true
|
|
TranslationManager(text, fbFrom) { translatedTextPairs ->
|
|
translatedTextBlocks = translatedTextPairs.map { it.first }
|
|
isLoading = false
|
|
// 可以同时打印原Text和翻译后的结果
|
|
translatedTextPairs.forEach { (translated, original) ->
|
|
Log.d("Translation", "Original: $original -> Translated: $translated")
|
|
}
|
|
|
|
postInvalidate()
|
|
}
|
|
}
|
|
// Redraw the overlay, as this graphic has been added.
|
|
postInvalidate()
|
|
}
|
|
|
|
|
|
override fun draw(canvas: Canvas) {
|
|
if (!isVisible) return
|
|
|
|
for ((index, textBlock) in text.textBlocks.withIndex()) {
|
|
if (hiddenTextBlocks[index]) continue
|
|
drawTextBlock(textBlock, canvas, index)
|
|
}
|
|
}
|
|
|
|
private fun drawLoadingIndicator(textBlock: Text.TextBlock, canvas: Canvas) {
|
|
val rect = RectF(textBlock.boundingBox)
|
|
val centerX = (rect.left + rect.right) / 2
|
|
val centerY = (rect.top + rect.bottom) / 2
|
|
val radius = min(rect.width(), rect.height()) / 4
|
|
|
|
val paint = Paint().apply {
|
|
color = Color.BLUE
|
|
style = Paint.Style.STROKE
|
|
strokeWidth = 8f
|
|
}
|
|
|
|
canvas.drawCircle(centerX, centerY, radius, paint)
|
|
}
|
|
|
|
|
|
private fun drawTextBlock(textBlock: Text.TextBlock, canvas: Canvas, index: Int) {
|
|
val translatedBlockText = translatedTextBlocks.getOrNull(index) ?: textBlock.text
|
|
|
|
if (shouldGroupRecognizedTextInBlocks) {
|
|
val rect = RectF(textBlock.boundingBox)
|
|
drawText(
|
|
getFormattedText(translatedBlockText, textBlock.recognizedLanguage, null),
|
|
rect,
|
|
canvas,
|
|
textPaint
|
|
)
|
|
}
|
|
// if (isLoading) {
|
|
// drawLoadingIndicator(textBlock, canvas)
|
|
// }
|
|
}
|
|
|
|
override fun contains(x: Float, y: Float): Boolean {
|
|
for (textBlock in text.textBlocks) {
|
|
val rect = RectF(textBlock.boundingBox)
|
|
val x0 = translateX(rect.left)
|
|
val x1 = translateX(rect.right)
|
|
rect.left = min(x0, x1)
|
|
rect.right = max(x0, x1)
|
|
rect.top = translateY(rect.top)
|
|
rect.bottom = translateY(rect.bottom)
|
|
if (rect.contains(x, y)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fun showTextBlockAt(x: Float, y: Float) {
|
|
isVisible = true
|
|
for ((index, textBlock) in text.textBlocks.withIndex()) {
|
|
val rect = RectF(textBlock.boundingBox)
|
|
val x0 = translateX(rect.left)
|
|
val x1 = translateX(rect.right)
|
|
rect.left = min(x0, x1)
|
|
rect.right = max(x0, x1)
|
|
rect.top = translateY(rect.top)
|
|
rect.bottom = translateY(rect.bottom)
|
|
if (rect.contains(x, y)) {
|
|
hiddenTextBlocks[index] = false
|
|
postInvalidate()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
fun hideTextBlock() {
|
|
isVisible = false
|
|
for (i in hiddenTextBlocks.indices) {
|
|
hiddenTextBlocks[i] = true
|
|
}
|
|
postInvalidate()
|
|
}
|
|
|
|
private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
|
|
val res = if (showLanguageTag) String.format(
|
|
TEXT_WITH_LANGUAGE_TAG_FORMAT,
|
|
languageTag,
|
|
text
|
|
) else text
|
|
return if (showConfidence && confidence != null) String.format(
|
|
"%s (%.2f)",
|
|
res,
|
|
confidence
|
|
) else res
|
|
}
|
|
|
|
private fun drawText(text: String, rect: RectF, canvas: Canvas, paint: TextPaint) {
|
|
val x0 = translateX(rect.left)
|
|
val x1 = translateX(rect.right)
|
|
rect.left = min(x0, x1)
|
|
rect.right = max(x0, x1)
|
|
rect.top = translateY(rect.top)
|
|
rect.bottom = translateY(rect.bottom)
|
|
|
|
// 绘制背景矩形
|
|
canvas.drawRect(rect, labelPaint)
|
|
|
|
// 准备文本绘制
|
|
val textPaintCopy = TextPaint(paint)
|
|
val availableWidth = rect.width().toInt()
|
|
val availableHeight = rect.height().toInt()
|
|
var textSize = textPaintCopy.textSize
|
|
|
|
var textLayout: StaticLayout
|
|
while (textSize > 0) {
|
|
textPaintCopy.textSize = textSize
|
|
textLayout =
|
|
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
|
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
|
.setLineSpacing(0.0f, 1.0f)
|
|
.setIncludePad(false)
|
|
.build()
|
|
|
|
if (textLayout.height <= availableHeight) {
|
|
break
|
|
}
|
|
textSize -= 1
|
|
}
|
|
|
|
textLayout =
|
|
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
|
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
|
.setLineSpacing(0.0f, 1.0f)
|
|
.setIncludePad(false)
|
|
.build()
|
|
|
|
canvas.save()
|
|
canvas.translate(rect.left, rect.top)
|
|
textLayout.draw(canvas)
|
|
canvas.restore()
|
|
}
|
|
|
|
companion object {
|
|
private const val TAG = "TextGraphic"
|
|
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
|
|
private val TEXT_COLOR = Color.parseColor("#FF474747")
|
|
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
|
|
private const val TEXT_SIZE = 44.0f
|
|
}
|
|
}
|