Pages crop proof-of-concept
parent
77bb5c2fcd
commit
77e393ae48
@ -0,0 +1,79 @@
|
|||||||
|
package org.koitharu.kotatsu.reader.domain
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.graphics.Rect
|
||||||
|
import androidx.core.graphics.get
|
||||||
|
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||||
|
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder
|
||||||
|
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.runInterruptible
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class WhitespaceDetector(
|
||||||
|
private val context: Context
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
suspend fun getBounds(imageSource: ImageSource): Rect? = mutex.withLock {
|
||||||
|
runInterruptible(Dispatchers.IO) {
|
||||||
|
val decoder = SkiaImageRegionDecoder(Bitmap.Config.RGB_565)
|
||||||
|
try {
|
||||||
|
val size = decoder.init(context, imageSource)
|
||||||
|
detectWhitespaces(decoder, size)
|
||||||
|
} finally {
|
||||||
|
decoder.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
private fun detectWhitespaces(decoder: ImageRegionDecoder, size: Point): Rect? {
|
||||||
|
val result = Rect(0, 0, size.x, size.y)
|
||||||
|
val window = Rect()
|
||||||
|
val windowSize = 200
|
||||||
|
|
||||||
|
var baseColor = -1
|
||||||
|
window.set(0, 0, windowSize, windowSize)
|
||||||
|
decoder.decodeRegion(window, 1).use { bitmap ->
|
||||||
|
baseColor = bitmap[0, 0]
|
||||||
|
outerTop@ for (x in 1 until bitmap.width / 2) {
|
||||||
|
for (y in 1 until bitmap.height / 2) {
|
||||||
|
if (isSameColor(baseColor, bitmap[x, y])) {
|
||||||
|
result.left = x
|
||||||
|
result.top = y
|
||||||
|
} else {
|
||||||
|
break@outerTop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.set(size.x - windowSize - 1, size.y - windowSize - 1, size.x - 1, size.y - 1)
|
||||||
|
decoder.decodeRegion(window, 1).use { bitmap ->
|
||||||
|
outerBottom@ for (x in (bitmap.width / 2 until bitmap.width).reversed()) {
|
||||||
|
for (y in (bitmap.height / 2 until bitmap.height).reversed()) {
|
||||||
|
if (isSameColor(baseColor, bitmap[x, y])) {
|
||||||
|
result.right = size.x - x
|
||||||
|
result.bottom = size.y - y
|
||||||
|
} else {
|
||||||
|
break@outerBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.takeUnless { it.isEmpty || (it.width() == size.x && it.height() == size.y) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSameColor(a: Int, b: Int) = abs(a - b) <= 4 // TODO
|
||||||
|
|
||||||
|
private inline fun <R> Bitmap.use(block: (Bitmap) -> R) = try {
|
||||||
|
block(this)
|
||||||
|
} finally {
|
||||||
|
recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue