mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
General Fixes to reader + shifting
*Fixed the lag when initally shifting form single to double page (stopped using with ui context) * Added a confidence to the padding check, since its easier to know if one page belongs to one side of a pair than another sometimes (still not perfect, but it never will be
This commit is contained in:
parent
443c581ea6
commit
4ef3f2437b
5 changed files with 109 additions and 56 deletions
|
@ -454,7 +454,9 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
outState.putBoolean(::menuVisible.name, menuVisible)
|
||||
(viewer as? PagerViewer)?.let { pViewer ->
|
||||
val config = pViewer.config
|
||||
outState.putBoolean(SHIFT_DOUBLE_PAGES, config.shiftDoublePage)
|
||||
if (config.doublePages) {
|
||||
outState.putBoolean(SHIFT_DOUBLE_PAGES, config.shiftDoublePage)
|
||||
}
|
||||
if (config.shiftDoublePage && config.doublePages) {
|
||||
pViewer.getShiftedPage()?.let {
|
||||
outState.putInt(SHIFTED_PAGE_INDEX, it.index)
|
||||
|
@ -605,8 +607,11 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
|
||||
fun shiftDoublePages(forceShift: Boolean? = null, page: ReaderPage? = null) {
|
||||
(viewer as? PagerViewer)?.let { pViewer ->
|
||||
if (forceShift == pViewer.config.shiftDoublePage) return
|
||||
if (page != null && pViewer.getShiftedPage() == page) return
|
||||
if (forceShift == pViewer.config.shiftDoublePage &&
|
||||
page != null && page == pViewer.getShiftedPage()
|
||||
) {
|
||||
return
|
||||
}
|
||||
pViewer.config.shiftDoublePage = !pViewer.config.shiftDoublePage
|
||||
viewModel.state.value.viewerChapters?.let {
|
||||
pViewer.updateShifting(page)
|
||||
|
@ -617,7 +622,7 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
}
|
||||
|
||||
fun isFirstPageFull(): Boolean = viewModel.getCurrentChapter()?.pages?.get(0)?.fullPage == true
|
||||
fun isFirstPageEnd(): Boolean = viewModel.getCurrentChapter()?.pages?.get(0)?.isEndPage == true
|
||||
fun getFirstPage(): ReaderPage? = viewModel.getCurrentChapter()?.pages?.get(0)
|
||||
|
||||
private fun popToMain() {
|
||||
if (fromUrl) {
|
||||
|
@ -1278,10 +1283,8 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
|
||||
private fun shouldShiftDoublePages(currentIndex: Int): Boolean {
|
||||
val currentChapter = viewModel.getCurrentChapter()
|
||||
val currentPage = currentChapter?.pages?.get(currentIndex)
|
||||
return (
|
||||
currentIndex +
|
||||
(currentPage?.isEndPage == true && currentPage.fullPage != true).toInt() +
|
||||
(currentChapter?.pages?.take(currentIndex)?.count { it.alonePage } ?: 0)
|
||||
) % 2 != 0
|
||||
}
|
||||
|
@ -1299,9 +1302,6 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
}
|
||||
indexChapterToShift = null
|
||||
indexPageToShift = null
|
||||
} else if (lastShiftDoubleState != null) {
|
||||
val currentIndex = viewerChapters.currChapter.requestedPage
|
||||
(viewer as? PagerViewer)?.config?.shiftDoublePage = shouldShiftDoublePages(currentIndex)
|
||||
}
|
||||
val currentChapterPageCount = viewerChapters.currChapter.pages?.size ?: 1
|
||||
binding.readerNav.root.visibility = when {
|
||||
|
|
|
@ -18,6 +18,7 @@ open class ReaderPage(
|
|||
var firstHalf: Boolean? = null,
|
||||
var longPage: Boolean? = null,
|
||||
var isEndPage: Boolean? = null,
|
||||
var paddedPageConfidence: Int = 0,
|
||||
var isStartPage: Boolean? = null,
|
||||
) : Page(index, url, imageUrl, null) {
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
|
|||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.PointF
|
||||
|
@ -25,6 +26,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
|||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil.isPagePadded
|
||||
import eu.kanade.tachiyomi.util.system.ThemeUtil
|
||||
import eu.kanade.tachiyomi.util.system.bottomCutoutInset
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
|
@ -474,12 +476,12 @@ class PagerPageHolder(
|
|||
errorLayout?.isVisible = false
|
||||
|
||||
cancelReadImageHeader()
|
||||
val streamFn = page.stream ?: return
|
||||
val streamFn2 = extraPage?.stream
|
||||
|
||||
var openStream: InputStream? = null
|
||||
|
||||
readImageHeaderJob = scope.launchIO {
|
||||
val streamFn = page.stream ?: return@launchIO
|
||||
val streamFn2 = extraPage?.stream
|
||||
|
||||
var openStream: InputStream? = null
|
||||
try {
|
||||
val stream = streamFn().buffered(16)
|
||||
|
||||
|
@ -492,12 +494,12 @@ class PagerPageHolder(
|
|||
if (viewer.config.readerTheme >= 2) {
|
||||
val bgType = getBGType(viewer.config.readerTheme, context)
|
||||
if (page.bg != null && page.bgType == bgType) {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
setImage(openStream, false, imageConfig)
|
||||
pageView?.background = page.bg
|
||||
}
|
||||
// if the user switches to automatic when pages are already cached, the bg needs to be loaded
|
||||
else {
|
||||
val bytesArray = openStream!!.readBytes()
|
||||
val bytesArray = openStream.readBytes()
|
||||
val bytesStream = bytesArray.inputStream()
|
||||
setImage(bytesStream, false, imageConfig)
|
||||
closeStreams(bytesStream)
|
||||
|
@ -513,10 +515,10 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
setImage(openStream!!, false, imageConfig)
|
||||
setImage(openStream, false, imageConfig)
|
||||
}
|
||||
} else {
|
||||
setImage(openStream!!, true, imageConfig)
|
||||
setImage(openStream, true, imageConfig)
|
||||
if (viewer.config.readerTheme >= 2 && page.bg != null) {
|
||||
pageView?.background = page.bg
|
||||
}
|
||||
|
@ -634,7 +636,7 @@ class PagerPageHolder(
|
|||
splitDoublePages()
|
||||
}
|
||||
}
|
||||
withUIContext { progressBar.completeAndFadeOut() }
|
||||
scope.launchUI { progressBar.completeAndFadeOut() }
|
||||
return imageStream
|
||||
}
|
||||
if (page.longPage == true && viewer.config.splitPages) {
|
||||
|
@ -707,7 +709,13 @@ class PagerPageHolder(
|
|||
val height = imageBitmap.height
|
||||
val width = imageBitmap.width
|
||||
|
||||
val imageBytes2 by lazy { imageStream2.readBytes() }
|
||||
val isLTR = (viewer !is R2LPagerViewer).xor(viewer.config.invertDoublePages)
|
||||
|
||||
if (height < width) {
|
||||
if (extraPage?.index == 1) {
|
||||
setExtraPageBitmap(imageBytes2, isLTR)
|
||||
}
|
||||
closeStreams(imageStream, imageStream2)
|
||||
val oldValue = page.fullPage
|
||||
page.fullPage = true
|
||||
|
@ -720,34 +728,53 @@ class PagerPageHolder(
|
|||
} else {
|
||||
viewer.splitDoublePages(page)
|
||||
}
|
||||
extraPage = null
|
||||
}
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
val isLTR = (viewer !is R2LPagerViewer).xor(viewer.config.invertDoublePages)
|
||||
var earlyImageBitmap2: Bitmap? = null
|
||||
if (page.index <= 2 && page.isEndPage == null && page.fullPage == null) {
|
||||
page.isEndPage = ImageUtil.isPagePadded(imageBitmap, rightSide = !isLTR)
|
||||
page.paddedPageConfidence = imageBitmap.isPagePadded(rightSide = !isLTR)
|
||||
page.isEndPage = page.paddedPageConfidence > 0
|
||||
if (extraPage?.index == 1 && extraPage?.isEndPage == null) {
|
||||
earlyImageBitmap2 = setExtraPageBitmap(imageBytes2, isLTR)
|
||||
}
|
||||
val isFirstPageEndPage by lazy {
|
||||
viewer.activity.getFirstPage()?.let {
|
||||
it.isEndPage != true || page.paddedPageConfidence > it.paddedPageConfidence
|
||||
} != false
|
||||
}
|
||||
if (page.index == 1 && page.isEndPage == true && viewer.config.shiftDoublePage &&
|
||||
!viewer.activity.isFirstPageEnd()
|
||||
isFirstPageEndPage
|
||||
) {
|
||||
shiftDoublePages(false)
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
} else if ((page.isEndPage == true) &&
|
||||
(if (page.index == 2) !viewer.activity.isFirstPageFull() else true) &&
|
||||
extraPage?.isEndPage != true
|
||||
} else if (page.isEndPage == true &&
|
||||
when (page.index) {
|
||||
// 3rd page shouldn't shift if page 1 is a spread
|
||||
2 -> !viewer.activity.isFirstPageFull()
|
||||
// 2nd page shouldn't shift if page 1 is more likely an end page
|
||||
1 -> isFirstPageEndPage
|
||||
// first page shouldn't shift if page 2 is definitely an end page
|
||||
0 -> extraPage?.run { isEndPage == true && paddedPageConfidence == 3 } != true ||
|
||||
page.paddedPageConfidence == 3
|
||||
else -> false
|
||||
}
|
||||
) {
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
} else if (!viewer.activity.manuallyShiftedPages && page.index == 0 && page.isEndPage == true) {
|
||||
} else if (!viewer.activity.manuallyShiftedPages && (page.index == 0 || page.index == 2) &&
|
||||
page.isEndPage == true && page.paddedPageConfidence == 3
|
||||
) {
|
||||
// if for some reason the first page should be by itself but its not, fix that
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
|
||||
val imageBytes2 = imageStream2.readBytes()
|
||||
val imageBitmap2 = try {
|
||||
val imageBitmap2 = earlyImageBitmap2 ?: try {
|
||||
BitmapFactory.decodeByteArray(imageBytes2, 0, imageBytes2.size)
|
||||
} catch (e: Exception) {
|
||||
closeStreams(imageStream, imageStream2)
|
||||
|
@ -757,7 +784,7 @@ class PagerPageHolder(
|
|||
Timber.e("Cannot combine pages ${e.message}")
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
withUIContext { progressBar.setProgress(97) }
|
||||
scope.launchUI { progressBar.setProgress(97) }
|
||||
val height2 = imageBitmap2.height
|
||||
val width2 = imageBitmap2.width
|
||||
|
||||
|
@ -775,14 +802,23 @@ class PagerPageHolder(
|
|||
}
|
||||
|
||||
closeStreams(imageStream, imageStream2)
|
||||
if (extraPage?.index == 1 && extraPage?.isStartPage == null && extraPage?.fullPage == null) {
|
||||
extraPage?.isStartPage = ImageUtil.isPagePadded(imageBitmap, rightSide = isLTR)
|
||||
if (extraPage?.isStartPage == true) {
|
||||
shiftDoublePages(true)
|
||||
extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
extraPage?.let { extraPage ->
|
||||
if (extraPage.index <= 2 && extraPage.isEndPage == null &&
|
||||
extraPage.isStartPage == null && extraPage.fullPage == null
|
||||
) {
|
||||
extraPage.paddedPageConfidence = imageBitmap2.isPagePadded(rightSide = isLTR)
|
||||
extraPage.isStartPage = extraPage.paddedPageConfidence > 0
|
||||
if (extraPage.isStartPage == true) {
|
||||
shiftDoublePages(page.index == 0 || viewer.activity.isFirstPageFull())
|
||||
this.extraPage = null
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
}
|
||||
}
|
||||
// If page has been removed in another thread, don't show it
|
||||
if (extraPage == null) {
|
||||
return supportHingeIfThere(imageBytes.inputStream())
|
||||
}
|
||||
return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg, viewer.config.hingeGapSize, context) {
|
||||
scope.launchUI {
|
||||
if (it == 100) {
|
||||
|
@ -794,6 +830,20 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setExtraPageBitmap(imageBytes2: ByteArray, isLTR: Boolean): Bitmap? {
|
||||
val earlyImageBitmap2 = try {
|
||||
BitmapFactory.decodeByteArray(imageBytes2, 0, imageBytes2.size)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
val paddedPageConfidence = earlyImageBitmap2?.isPagePadded(rightSide = !isLTR) ?: 0
|
||||
if (paddedPageConfidence == 3) {
|
||||
extraPage?.paddedPageConfidence = paddedPageConfidence
|
||||
extraPage?.isEndPage = true
|
||||
}
|
||||
return earlyImageBitmap2
|
||||
}
|
||||
|
||||
private suspend fun supportHingeIfThere(imageStream: InputStream): InputStream {
|
||||
if (viewer.config.hingeGapSize > 0 && !ImageUtil.isAnimatedAndSupported(imageStream)) {
|
||||
val imageBytes = imageStream.readBytes()
|
||||
|
@ -833,16 +883,16 @@ class PagerPageHolder(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun shiftDoublePages(shift: Boolean) {
|
||||
delayPageUpdate { viewer.activity.shiftDoublePages(shift) }
|
||||
private fun shiftDoublePages(shift: Boolean) {
|
||||
delayPageUpdate { viewer.activity.shiftDoublePages(shift, page) }
|
||||
}
|
||||
|
||||
private suspend fun splitDoublePages() {
|
||||
private fun splitDoublePages() {
|
||||
delayPageUpdate { viewer.splitDoublePages(page) }
|
||||
}
|
||||
|
||||
private suspend fun delayPageUpdate(callback: () -> Unit) {
|
||||
withUIContext {
|
||||
private fun delayPageUpdate(callback: () -> Unit) {
|
||||
scope.launchUI {
|
||||
callback()
|
||||
if (extraPage?.fullPage == true || page.fullPage == true) {
|
||||
extraPage = null
|
||||
|
|
|
@ -190,6 +190,7 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
|||
firstHalf = null
|
||||
isEndPage = null
|
||||
isStartPage = null
|
||||
paddedPageConfidence = 0
|
||||
}
|
||||
}
|
||||
if (viewer.config.splitPages) {
|
||||
|
|
|
@ -622,27 +622,27 @@ object ImageUtil {
|
|||
abs(color1.blue - color2.blue) < 30
|
||||
}
|
||||
|
||||
/** Returns if this bitmap matches what would be (if rightSide param is true)
|
||||
* the single left side page, or the second page to read in a RTL book, first in an LTR book
|
||||
/**
|
||||
* Returns if this bitmap matches what would be (if rightSide param is true)
|
||||
* the single left side page, or the second page to read in a RTL book, first in an LTR book.
|
||||
*
|
||||
* @param image: bitmap image to check
|
||||
* @param rightSide: when true, check if its a single left side page, else right side
|
||||
* @return An int based on confidence, 0 meaning not padded, 1 meaning barely padded,
|
||||
* 2 meaning likely padded, 3 meaining definitely padded
|
||||
* @param rightSide: When true, check if its a single left side page, else right side
|
||||
* */
|
||||
fun isPagePadded(image: Bitmap, rightSide: Boolean): Boolean {
|
||||
if (image.isSidePadded(!rightSide, checkWhite = true) ||
|
||||
image.isSidePadded(!rightSide, checkWhite = false)
|
||||
) {
|
||||
return false
|
||||
fun Bitmap.isPagePadded(rightSide: Boolean): Int {
|
||||
val booleans = listOf(true, false)
|
||||
return when {
|
||||
booleans.any { isSidePadded(!rightSide, checkWhite = it) } -> 0
|
||||
booleans.any { isSidePadded(rightSide, checkWhite = it) } -> 3
|
||||
booleans.any { isOneSideMorePadded(rightSide, checkWhite = it) } -> 2
|
||||
booleans.any { isSidePadded(rightSide, checkWhite = it, halfCheck = true) } -> 1
|
||||
else -> 0
|
||||
}
|
||||
return image.isSidePadded(rightSide, checkWhite = true) ||
|
||||
image.isSidePadded(rightSide, checkWhite = false) ||
|
||||
// if neither of the above 2 worked,
|
||||
// try starting from the vert. middle and see which side has more padding
|
||||
image.isOneSideMorePadded(rightSide, checkWhite = true) ||
|
||||
image.isOneSideMorePadded(rightSide, checkWhite = false)
|
||||
}
|
||||
|
||||
private fun Bitmap.isSidePadded(rightSide: Boolean, checkWhite: Boolean): Boolean {
|
||||
/** Returns if one side has a vertical padding and the other side does not */
|
||||
private fun Bitmap.isSidePadded(rightSide: Boolean, checkWhite: Boolean, halfCheck: Boolean = false): Boolean {
|
||||
val left = (width * 0.0275).toInt()
|
||||
val right = width - left
|
||||
val paddedSide = if (rightSide) right else left
|
||||
|
@ -650,12 +650,13 @@ object ImageUtil {
|
|||
return (1 until 30).count {
|
||||
// if all of a side is padded (the left page usually has a white padding on the right when scanned)
|
||||
getPixel(paddedSide, (height * (it / 30f)).roundToInt()).isWhiteOrDark(checkWhite)
|
||||
} >= 27 && !(1 until 50).all {
|
||||
} >= (if (halfCheck) 15 else 27) && !(1 until 50).all {
|
||||
// and if all of the other side isn't padded
|
||||
getPixel(unPaddedSide, (height * (it / 50f)).roundToInt()).isWhiteOrDark(checkWhite)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns if one side is more padded than the other */
|
||||
private fun Bitmap.isOneSideMorePadded(rightSide: Boolean, checkWhite: Boolean): Boolean {
|
||||
val middle = (height * 0.475).roundToInt()
|
||||
val middle2 = (height * 0.525).roundToInt()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue