mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 19:11:50 +01:00
Downloader: Optimize split tall image (#7435)
This commit is contained in:
parent
deaded5af2
commit
ff32ab09fb
@ -493,7 +493,12 @@ class Downloader(
|
|||||||
// check if the original page was previously splitted before then skip.
|
// check if the original page was previously splitted before then skip.
|
||||||
if (imageFile.name!!.contains("__")) return true
|
if (imageFile.name!!.contains("__")) return true
|
||||||
|
|
||||||
return ImageUtil.splitTallImage(imageFile, imageFilePath)
|
return try {
|
||||||
|
ImageUtil.splitTallImage(imageFile, imageFilePath)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e)
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -185,7 +185,7 @@ object ImageUtil {
|
|||||||
* @return true if the height:width ratio is greater than 3.
|
* @return true if the height:width ratio is greater than 3.
|
||||||
*/
|
*/
|
||||||
private fun isTallImage(imageStream: InputStream): Boolean {
|
private fun isTallImage(imageStream: InputStream): Boolean {
|
||||||
val options = extractImageOptions(imageStream, false)
|
val options = extractImageOptions(imageStream, resetAfterExtraction = false)
|
||||||
return (options.outHeight / options.outWidth) > 3
|
return (options.outHeight / options.outWidth) > 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,16 +197,34 @@ object ImageUtil {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val options = extractImageOptions(imageFile.openInputStream(), false).apply { inJustDecodeBounds = false }
|
val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { inJustDecodeBounds = false }
|
||||||
// Values are stored as they get modified during split loop
|
// Values are stored as they get modified during split loop
|
||||||
val imageHeight = options.outHeight
|
val imageHeight = options.outHeight
|
||||||
val imageWidth = options.outWidth
|
val imageWidth = options.outWidth
|
||||||
|
|
||||||
val splitHeight = getDisplayMaxHeightInPx
|
val splitHeight = (getDisplayMaxHeightInPx * 1.5).toInt()
|
||||||
// -1 so it doesn't try to split when imageHeight = getDisplayHeightInPx
|
// -1 so it doesn't try to split when imageHeight = getDisplayHeightInPx
|
||||||
val partCount = (imageHeight - 1) / getDisplayMaxHeightInPx + 1
|
val partCount = (imageHeight - 1) / splitHeight + 1
|
||||||
|
|
||||||
logcat { "Splitting ${imageHeight}px height image into $partCount part with estimated ${splitHeight}px per height" }
|
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) {
|
val bitmapRegionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
BitmapRegionDecoder.newInstance(imageFile.openInputStream())
|
BitmapRegionDecoder.newInstance(imageFile.openInputStream())
|
||||||
@ -220,36 +238,52 @@ object ImageUtil {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
logcat {
|
||||||
(0 until partCount).forEach { splitIndex ->
|
"Splitting image with height of $imageHeight into $partCount part " +
|
||||||
val splitPath = imageFilePath.substringBeforeLast(".") + "__${"%03d".format(splitIndex + 1)}.jpg"
|
"with estimated ${optimalSplitHeight}px height per split"
|
||||||
|
}
|
||||||
|
|
||||||
val topOffset = splitIndex * splitHeight
|
return try {
|
||||||
val outputImageHeight = min(splitHeight, imageHeight - topOffset)
|
splitDataList.forEach { splitData ->
|
||||||
val bottomOffset = topOffset + outputImageHeight
|
val splitPath = splitImagePath(imageFilePath, splitData.index)
|
||||||
logcat { "Split #$splitIndex with topOffset=$topOffset height=$outputImageHeight bottomOffset=$bottomOffset" }
|
|
||||||
|
|
||||||
val region = Rect(0, topOffset, imageWidth, bottomOffset)
|
val region = Rect(0, splitData.topOffset, imageWidth, splitData.bottomOffset)
|
||||||
|
|
||||||
FileOutputStream(splitPath).use { outputStream ->
|
FileOutputStream(splitPath).use { outputStream ->
|
||||||
val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options)
|
val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options)
|
||||||
splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
||||||
|
splitBitmap.recycle()
|
||||||
|
}
|
||||||
|
logcat {
|
||||||
|
"Success: Split #${splitData.index + 1} with topOffset=${splitData.topOffset} " +
|
||||||
|
"height=${splitData.outputImageHeight} bottomOffset=${splitData.bottomOffset}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
imageFile.delete()
|
imageFile.delete()
|
||||||
return true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Image splits were not successfully saved so delete them and keep the original image
|
// Image splits were not successfully saved so delete them and keep the original image
|
||||||
(0 until partCount)
|
splitDataList
|
||||||
.map { imageFilePath.substringBeforeLast(".") + "__${"%03d".format(it + 1)}.jpg" }
|
.map { splitImagePath(imageFilePath, it.index) }
|
||||||
.forEach { File(it).delete() }
|
.forEach { File(it).delete() }
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
return false
|
false
|
||||||
} finally {
|
} finally {
|
||||||
bitmapRegionDecoder.recycle()
|
bitmapRegionDecoder.recycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun splitImagePath(imageFilePath: String, index: Int) =
|
||||||
|
imageFilePath.substringBeforeLast(".") + "__${"%03d".format(index + 1)}.jpg"
|
||||||
|
|
||||||
|
data class SplitData(
|
||||||
|
val index: Int,
|
||||||
|
val topOffset: Int,
|
||||||
|
val outputImageHeight: Int,
|
||||||
|
) {
|
||||||
|
val bottomOffset = topOffset + outputImageHeight
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Algorithm for determining what background to accompany a comic/manga page
|
* Algorithm for determining what background to accompany a comic/manga page
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user