Long Strip Split for Webtoon (#5759)

* Long Strip Split for Webtoon

* Review Changes

* Review Changes 2 + Rebase
This commit is contained in:
AntsyLich
2022-08-27 21:41:18 +06:00
committed by GitHub
parent d6c0a5ef8b
commit 88b56121a3
11 changed files with 218 additions and 44 deletions

View File

@@ -206,35 +206,6 @@ object ImageUtil {
return true
}
val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { inJustDecodeBounds = false }
// Values are stored as they get modified during split loop
val imageHeight = options.outHeight
val imageWidth = options.outWidth
val splitHeight = (getDisplayMaxHeightInPx * 1.5).toInt()
// -1 so it doesn't try to split when imageHeight = getDisplayHeightInPx
val partCount = (imageHeight - 1) / splitHeight + 1
val optimalSplitHeight = imageHeight / partCount
val splitDataList = (0 until partCount).fold(mutableListOf<SplitData>()) { list, index ->
list.apply {
// Only continue if the list is empty or there is image remaining
if (isEmpty() || imageHeight > last().bottomOffset) {
val topOffset = index * optimalSplitHeight
var outputImageHeight = min(optimalSplitHeight, imageHeight - topOffset)
val remainingHeight = imageHeight - (topOffset + outputImageHeight)
// If remaining height is smaller or equal to 1/3th of
// optimal split height then include it in current page
if (remainingHeight <= (optimalSplitHeight / 3)) {
outputImageHeight += remainingHeight
}
add(SplitData(index, topOffset, outputImageHeight))
}
}
}
val bitmapRegionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
BitmapRegionDecoder.newInstance(imageFile.openInputStream())
} else {
@@ -247,10 +218,12 @@ object ImageUtil {
return false
}
logcat {
"Splitting image with height of $imageHeight into $partCount part " +
"with estimated ${optimalSplitHeight}px height per split"
}
val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { inJustDecodeBounds = false }
// Values are stored as they get modified during split loop
val imageWidth = options.outWidth
val splitDataList = getSplitDataForOptions(options)
return try {
splitDataList.forEach { splitData ->
@@ -285,6 +258,93 @@ object ImageUtil {
private fun splitImagePath(imageFilePath: String, index: Int) =
imageFilePath.substringBeforeLast(".") + "__${"%03d".format(index + 1)}.jpg"
/**
* Check whether the image is a long Strip that needs splitting
* @return true if the image is not animated and it's height is greater than image width and screen height
*/
fun isStripSplitNeeded(imageStream: BufferedInputStream): Boolean {
if (isAnimatedAndSupported(imageStream)) return false
val options = extractImageOptions(imageStream)
val imageHeightIsBiggerThanWidth = options.outHeight > options.outWidth
val imageHeightBiggerThanScreenHeight = options.outHeight > getDisplayMaxHeightInPx
return imageHeightIsBiggerThanWidth && imageHeightBiggerThanScreenHeight
}
/**
* Split the imageStream according to the provided splitData
*/
fun splitStrip(imageStream: InputStream, splitData: SplitData): InputStream {
val bitmapRegionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
BitmapRegionDecoder.newInstance(imageStream)
} else {
@Suppress("DEPRECATION")
BitmapRegionDecoder.newInstance(imageStream, false)
}
if (bitmapRegionDecoder == null) {
throw Exception("Failed to create new instance of BitmapRegionDecoder")
}
logcat {
"WebtoonSplit #${splitData.index} with topOffset=${splitData.topOffset} " +
"outputImageHeight=${splitData.outputImageHeight} bottomOffset=${splitData.bottomOffset}"
}
val options = extractImageOptions(imageStream).apply { inJustDecodeBounds = false }
val region = Rect(0, splitData.topOffset, splitData.outputImageHeight, splitData.bottomOffset)
try {
val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options)
val outputStream = ByteArrayOutputStream()
splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
return ByteArrayInputStream(outputStream.toByteArray())
} catch (e: Throwable) {
throw e
} finally {
bitmapRegionDecoder.recycle()
}
}
fun getSplitDataForStream(imageStream: InputStream): List<SplitData> {
val options = extractImageOptions(imageStream)
return getSplitDataForOptions(options)
}
private fun getSplitDataForOptions(options: BitmapFactory.Options): List<SplitData> {
val imageHeight = options.outHeight
val splitHeight = (getDisplayMaxHeightInPx * 1.5).toInt()
// -1 so it doesn't try to split when imageHeight = splitHeight
val partCount = (imageHeight - 1) / splitHeight + 1
val optimalSplitHeight = imageHeight / partCount
logcat {
"Generating SplitData for image with height of $imageHeight. " +
"Estimated $partCount part and ${optimalSplitHeight}px height per part"
}
return mutableListOf<SplitData>().apply {
for (index in (0 until partCount)) {
// Only continue if the list is empty or there is image remaining
if (isNotEmpty() && imageHeight <= last().bottomOffset) break
val topOffset = index * optimalSplitHeight
var outputImageHeight = min(optimalSplitHeight, imageHeight - topOffset)
val remainingHeight = imageHeight - (topOffset + outputImageHeight)
// If remaining height is smaller or equal to 1/10th of
// optimal split height then include it in current page
if (remainingHeight <= (optimalSplitHeight / 10)) {
outputImageHeight += remainingHeight
}
add(SplitData(index, topOffset, outputImageHeight))
}
}
}
data class SplitData(
val index: Int,
val topOffset: Int,