Automatic background color for PagerViewer (#4996)
* Add J2K implementation of automatic background Co-authored-by: Jays2Kings <8617760+Jays2Kings@users.noreply.github.com> * Tweak the monstrosity called automatic background * Add ability to choose Automatic as a background * More tweaks Co-authored-by: Jays2Kings <8617760+Jays2Kings@users.noreply.github.com> (cherry picked from commit 122cdae5bcbb0421c98e271521d32fadc7c95beb) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt # app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt # app/src/main/res/values/arrays.xml
This commit is contained in:
parent
7e612e63b4
commit
6238f06d39
@ -34,7 +34,7 @@ android {
|
|||||||
minSdkVersion(AndroidConfig.minSdk)
|
minSdkVersion(AndroidConfig.minSdk)
|
||||||
targetSdkVersion(AndroidConfig.targetSdk)
|
targetSdkVersion(AndroidConfig.targetSdk)
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
versionCode = 17
|
versionCode = 18
|
||||||
versionName = "1.6.2"
|
versionName = "1.6.2"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.loader
|
package eu.kanade.tachiyomi.ui.reader.loader
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerPageHolder
|
|
||||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
|
||||||
import exh.source.isEhBasedSource
|
import exh.source.isEhBasedSource
|
||||||
import rx.Completable
|
import rx.Completable
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -258,20 +255,6 @@ class HttpPageLoader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
// SY -->
|
|
||||||
val readerTheme = preferences.readerTheme().get()
|
|
||||||
if (readerTheme >= 3) {
|
|
||||||
val stream = chapterCache.getImageFile(imageUrl).inputStream()
|
|
||||||
val image = BitmapFactory.decodeStream(stream)
|
|
||||||
page.bg = ImageUtil.autoSetBackground(
|
|
||||||
image,
|
|
||||||
readerTheme == 3,
|
|
||||||
preferences.context
|
|
||||||
)
|
|
||||||
page.bgType = PagerPageHolder.getBGType(readerTheme, preferences.context)
|
|
||||||
stream.close()
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
page.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
|
page.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
|
||||||
page.status = Page.READY
|
page.status = Page.READY
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ class PagerConfig(
|
|||||||
preferences: PreferencesHelper = Injekt.get()
|
preferences: PreferencesHelper = Injekt.get()
|
||||||
) : ViewerConfig(preferences, scope) {
|
) : ViewerConfig(preferences, scope) {
|
||||||
|
|
||||||
|
var automaticBackground = false
|
||||||
|
private set
|
||||||
|
|
||||||
var dualPageSplitChangedListener: ((Boolean) -> Unit)? = null
|
var dualPageSplitChangedListener: ((Boolean) -> Unit)? = null
|
||||||
|
|
||||||
var imageScaleType = 1
|
var imageScaleType = 1
|
||||||
@ -39,6 +42,9 @@ class PagerConfig(
|
|||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
preferences.readerTheme()
|
||||||
|
.register({ automaticBackground = it == 3 }, { imagePropertyChangedListener?.invoke() })
|
||||||
|
|
||||||
preferences.imageScaleType()
|
preferences.imageScaleType()
|
||||||
.register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })
|
.register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager
|
package eu.kanade.tachiyomi.ui.reader.viewer.pager
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@ -24,26 +21,19 @@ import com.davemorrissey.labs.subscaleview.ImageSource
|
|||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.github.chrisbanes.photoview.PhotoView
|
import com.github.chrisbanes.photoview.PhotoView
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
|
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
|
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
||||||
import exh.util.isInNightMode
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@ -104,12 +94,6 @@ class PagerPageHolder(
|
|||||||
*/
|
*/
|
||||||
private var readImageHeaderSubscription: Subscription? = null
|
private var readImageHeaderSubscription: Subscription? = null
|
||||||
|
|
||||||
// SY -->
|
|
||||||
private val readerTheme by lazy {
|
|
||||||
Injekt.get<PreferencesHelper>().readerTheme().get()
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addView(progressBar)
|
addView(progressBar)
|
||||||
observeStatus()
|
observeStatus()
|
||||||
@ -254,30 +238,12 @@ class PagerPageHolder(
|
|||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnNext { isAnimated ->
|
.doOnNext { isAnimated ->
|
||||||
if (!isAnimated) {
|
if (!isAnimated) {
|
||||||
// SY -->
|
initSubsamplingImageView().apply {
|
||||||
if (readerTheme >= 3) {
|
if (viewer.config.automaticBackground) {
|
||||||
val imageView = initSubsamplingImageView()
|
background = ImageUtil.chooseBackground(context, openStream!!)
|
||||||
if (page.bg != null && page.bgType == getBGType(readerTheme, context)) {
|
|
||||||
imageView.setImage(ImageSource.inputStream(openStream!!))
|
|
||||||
imageView.background = page.bg
|
|
||||||
}
|
}
|
||||||
// if the user switches to automatic when pages are already cached, the bg needs to be loaded
|
setImage(ImageSource.inputStream(openStream!!))
|
||||||
else {
|
|
||||||
val bytesArray = openStream!!.readBytes()
|
|
||||||
val bytesStream = bytesArray.inputStream()
|
|
||||||
imageView.setImage(ImageSource.inputStream(bytesStream))
|
|
||||||
bytesStream.close()
|
|
||||||
|
|
||||||
launchUI {
|
|
||||||
imageView.background = setBG(bytesArray)
|
|
||||||
page.bg = imageView.background
|
|
||||||
page.bgType = getBGType(readerTheme, context)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!))
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
} else {
|
} else {
|
||||||
initImageView().setImage(openStream!!)
|
initImageView().setImage(openStream!!)
|
||||||
}
|
}
|
||||||
@ -331,22 +297,6 @@ class PagerPageHolder(
|
|||||||
viewer.onPageSplit(page, newPage)
|
viewer.onPageSplit(page, newPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
|
||||||
private suspend fun setBG(bytesArray: ByteArray): Drawable {
|
|
||||||
return withContext(Dispatchers.Default) {
|
|
||||||
ImageUtil.autoSetBackground(
|
|
||||||
BitmapFactory.decodeByteArray(
|
|
||||||
bytesArray,
|
|
||||||
0,
|
|
||||||
bytesArray.size
|
|
||||||
),
|
|
||||||
readerTheme == 3,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page has an error.
|
* Called when the page has an error.
|
||||||
*/
|
*/
|
||||||
@ -550,14 +500,4 @@ class PagerPageHolder(
|
|||||||
.build()
|
.build()
|
||||||
context.imageLoader.enqueue(request)
|
context.imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
|
||||||
companion object {
|
|
||||||
fun getBGType(readerTheme: Int, context: Context): Int {
|
|
||||||
return if (readerTheme == 4) {
|
|
||||||
if (context.isInNightMode) 2 else 1
|
|
||||||
} else 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
|
@ -120,8 +120,8 @@ class SettingsReaderController : SettingsController() {
|
|||||||
intListPreference {
|
intListPreference {
|
||||||
key = Keys.readerTheme
|
key = Keys.readerTheme
|
||||||
titleRes = R.string.pref_reader_theme
|
titleRes = R.string.pref_reader_theme
|
||||||
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background, R.string.smart_based_on_page, R.string.smart_based_on_page_and_theme)
|
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background, R.string.automatic_background)
|
||||||
entryValues = arrayOf("1", "2", "0", "3", "4")
|
entryValues = arrayOf("1", "2", "0", "3")
|
||||||
defaultValue = "3"
|
defaultValue = "3"
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.util.system
|
package eu.kanade.tachiyomi.util.system
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
@ -9,8 +10,11 @@ import android.graphics.Rect
|
|||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import androidx.core.graphics.alpha
|
||||||
|
import androidx.core.graphics.blue
|
||||||
import androidx.core.graphics.createBitmap
|
import androidx.core.graphics.createBitmap
|
||||||
import eu.kanade.tachiyomi.R
|
import androidx.core.graphics.green
|
||||||
|
import androidx.core.graphics.red
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -161,16 +165,22 @@ object ImageUtil {
|
|||||||
RIGHT, LEFT
|
RIGHT, LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
/**
|
||||||
@Suppress("UNUSED_VARIABLE")
|
* Algorithm for determining what background to accompany a comic/manga page
|
||||||
fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable {
|
*/
|
||||||
val backgroundColor = if (alwaysUseWhite) Color.WHITE else {
|
fun chooseBackground(context: Context, imageStream: InputStream): Drawable {
|
||||||
context.getResourceColor(R.attr.colorPrimary)
|
imageStream.mark(imageStream.available() + 1)
|
||||||
}
|
|
||||||
if (image == null) return ColorDrawable(backgroundColor)
|
val image = BitmapFactory.decodeStream(imageStream)
|
||||||
|
|
||||||
|
imageStream.reset()
|
||||||
|
|
||||||
|
val whiteColor = Color.WHITE
|
||||||
|
if (image == null) return ColorDrawable(whiteColor)
|
||||||
if (image.width < 50 || image.height < 50) {
|
if (image.width < 50 || image.height < 50) {
|
||||||
return ColorDrawable(backgroundColor)
|
return ColorDrawable(whiteColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
val top = 5
|
val top = 5
|
||||||
val bot = image.height - 5
|
val bot = image.height - 5
|
||||||
val left = (image.width * 0.0275).toInt()
|
val left = (image.width * 0.0275).toInt()
|
||||||
@ -178,42 +188,52 @@ object ImageUtil {
|
|||||||
val midX = image.width / 2
|
val midX = image.width / 2
|
||||||
val midY = image.height / 2
|
val midY = image.height / 2
|
||||||
val offsetX = (image.width * 0.01).toInt()
|
val offsetX = (image.width * 0.01).toInt()
|
||||||
val offsetY = (image.height * 0.01).toInt()
|
val leftOffsetX = left - offsetX
|
||||||
val topLeftIsDark = isDark(image.getPixel(left, top))
|
val rightOffsetX = right + offsetX
|
||||||
val topRightIsDark = isDark(image.getPixel(right, top))
|
|
||||||
val midLeftIsDark = isDark(image.getPixel(left, midY))
|
val topLeftPixel = image.getPixel(left, top)
|
||||||
val midRightIsDark = isDark(image.getPixel(right, midY))
|
val topRightPixel = image.getPixel(right, top)
|
||||||
val topMidIsDark = isDark(image.getPixel(midX, top))
|
val midLeftPixel = image.getPixel(left, midY)
|
||||||
val botLeftIsDark = isDark(image.getPixel(left, bot))
|
val midRightPixel = image.getPixel(right, midY)
|
||||||
val botRightIsDark = isDark(image.getPixel(right, bot))
|
val topCenterPixel = image.getPixel(midX, top)
|
||||||
|
val botLeftPixel = image.getPixel(left, bot)
|
||||||
|
val bottomCenterPixel = image.getPixel(midX, bot)
|
||||||
|
val botRightPixel = image.getPixel(right, bot)
|
||||||
|
|
||||||
|
val topLeftIsDark = topLeftPixel.isDark()
|
||||||
|
val topRightIsDark = topRightPixel.isDark()
|
||||||
|
val midLeftIsDark = midLeftPixel.isDark()
|
||||||
|
val midRightIsDark = midRightPixel.isDark()
|
||||||
|
val topMidIsDark = topCenterPixel.isDark()
|
||||||
|
val botLeftIsDark = botLeftPixel.isDark()
|
||||||
|
val botRightIsDark = botRightPixel.isDark()
|
||||||
|
|
||||||
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
||||||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
||||||
|
|
||||||
if (!isWhite(image.getPixel(left, top)) && pixelIsClose(image.getPixel(left, top), image.getPixel(midX, top)) &&
|
val topAndBotPixels = listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
|
||||||
!isWhite(image.getPixel(midX, top)) && pixelIsClose(image.getPixel(midX, top), image.getPixel(right, top)) &&
|
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
|
||||||
!isWhite(image.getPixel(right, top)) && pixelIsClose(image.getPixel(right, top), image.getPixel(right, bot)) &&
|
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
||||||
!isWhite(image.getPixel(right, bot)) && pixelIsClose(image.getPixel(right, bot), image.getPixel(midX, bot)) &&
|
!color.isWhite() && color.isCloseTo(other)
|
||||||
!isWhite(image.getPixel(midX, bot)) && pixelIsClose(image.getPixel(midX, bot), image.getPixel(left, bot)) &&
|
}
|
||||||
!isWhite(image.getPixel(left, bot)) && pixelIsClose(image.getPixel(left, bot), image.getPixel(left, top))
|
if (isNotWhiteAndCloseTo.all { it }) {
|
||||||
) {
|
return ColorDrawable(topLeftPixel)
|
||||||
return ColorDrawable(image.getPixel(left, top))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWhite(image.getPixel(left, top)).toInt() +
|
val cornerPixels = listOf(topLeftPixel, topRightPixel, botLeftPixel, botRightPixel)
|
||||||
isWhite(image.getPixel(right, top)).toInt() +
|
val numberOfWhiteCorners = cornerPixels.map { cornerPixel -> cornerPixel.isWhite() }
|
||||||
isWhite(image.getPixel(left, bot)).toInt() +
|
.filter { it }
|
||||||
isWhite(image.getPixel(right, bot)).toInt() > 2
|
.size
|
||||||
) {
|
if (numberOfWhiteCorners > 2) {
|
||||||
darkBG = false
|
darkBG = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var blackPixel = when {
|
var blackColor = when {
|
||||||
topLeftIsDark -> image.getPixel(left, top)
|
topLeftIsDark -> topLeftPixel
|
||||||
topRightIsDark -> image.getPixel(right, top)
|
topRightIsDark -> topRightPixel
|
||||||
botLeftIsDark -> image.getPixel(left, bot)
|
botLeftIsDark -> botLeftPixel
|
||||||
botRightIsDark -> image.getPixel(right, bot)
|
botRightIsDark -> botRightPixel
|
||||||
else -> backgroundColor
|
else -> whiteColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var overallWhitePixels = 0
|
var overallWhitePixels = 0
|
||||||
@ -222,32 +242,32 @@ object ImageUtil {
|
|||||||
var topWhiteStreak = 0
|
var topWhiteStreak = 0
|
||||||
var botBlackStreak = 0
|
var botBlackStreak = 0
|
||||||
var botWhiteStreak = 0
|
var botWhiteStreak = 0
|
||||||
outer@ for (x in intArrayOf(left, right, left - offsetX, right + offsetX)) {
|
outer@ for (x in intArrayOf(left, right, leftOffsetX, rightOffsetX)) {
|
||||||
var whitePixelsStreak = 0
|
var whitePixelsStreak = 0
|
||||||
var whitePixels = 0
|
var whitePixels = 0
|
||||||
var blackPixelsStreak = 0
|
var blackPixelsStreak = 0
|
||||||
var blackPixels = 0
|
var blackPixels = 0
|
||||||
var blackStreak = false
|
var blackStreak = false
|
||||||
var whiteStrak = false
|
var whiteStreak = false
|
||||||
val notOffset = x == left || x == right
|
val notOffset = x == left || x == right
|
||||||
for ((index, y) in (0 until image.height step image.height / 25).withIndex()) {
|
inner@ for ((index, y) in (0 until image.height step image.height / 25).withIndex()) {
|
||||||
val pixel = image.getPixel(x, y)
|
val pixel = image.getPixel(x, y)
|
||||||
val pixelOff = image.getPixel(x + (if (x < image.width / 2) -offsetX else offsetX), y)
|
val pixelOff = image.getPixel(x + (if (x < image.width / 2) -offsetX else offsetX), y)
|
||||||
if (isWhite(pixel)) {
|
if (pixel.isWhite()) {
|
||||||
whitePixelsStreak++
|
whitePixelsStreak++
|
||||||
whitePixels++
|
whitePixels++
|
||||||
if (notOffset) {
|
if (notOffset) {
|
||||||
overallWhitePixels++
|
overallWhitePixels++
|
||||||
}
|
}
|
||||||
if (whitePixelsStreak > 14) {
|
if (whitePixelsStreak > 14) {
|
||||||
whiteStrak = true
|
whiteStreak = true
|
||||||
}
|
}
|
||||||
if (whitePixelsStreak > 6 && whitePixelsStreak >= index - 1) {
|
if (whitePixelsStreak > 6 && whitePixelsStreak >= index - 1) {
|
||||||
topWhiteStreak = whitePixelsStreak
|
topWhiteStreak = whitePixelsStreak
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
whitePixelsStreak = 0
|
whitePixelsStreak = 0
|
||||||
if (isDark(pixel) && isDark(pixelOff)) {
|
if (pixel.isDark() && pixelOff.isDark()) {
|
||||||
blackPixels++
|
blackPixels++
|
||||||
if (notOffset) {
|
if (notOffset) {
|
||||||
overallBlackPixels++
|
overallBlackPixels++
|
||||||
@ -256,7 +276,7 @@ object ImageUtil {
|
|||||||
if (blackPixelsStreak >= 14) {
|
if (blackPixelsStreak >= 14) {
|
||||||
blackStreak = true
|
blackStreak = true
|
||||||
}
|
}
|
||||||
continue
|
continue@inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blackPixelsStreak > 6 && blackPixelsStreak >= index - 1) {
|
if (blackPixelsStreak > 6 && blackPixelsStreak >= index - 1) {
|
||||||
@ -271,11 +291,11 @@ object ImageUtil {
|
|||||||
}
|
}
|
||||||
when {
|
when {
|
||||||
blackPixels > 22 -> {
|
blackPixels > 22 -> {
|
||||||
if (x == right || x == right + offsetX) {
|
if (x == right || x == rightOffsetX) {
|
||||||
blackPixel = when {
|
blackColor = when {
|
||||||
topRightIsDark -> image.getPixel(right, top)
|
topRightIsDark -> topRightPixel
|
||||||
botRightIsDark -> image.getPixel(right, bot)
|
botRightIsDark -> botRightPixel
|
||||||
else -> blackPixel
|
else -> blackColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
darkBG = true
|
darkBG = true
|
||||||
@ -284,11 +304,11 @@ object ImageUtil {
|
|||||||
}
|
}
|
||||||
blackStreak -> {
|
blackStreak -> {
|
||||||
darkBG = true
|
darkBG = true
|
||||||
if (x == right || x == right + offsetX) {
|
if (x == right || x == rightOffsetX) {
|
||||||
blackPixel = when {
|
blackColor = when {
|
||||||
topRightIsDark -> image.getPixel(right, top)
|
topRightIsDark -> topRightPixel
|
||||||
botRightIsDark -> image.getPixel(right, bot)
|
botRightIsDark -> botRightPixel
|
||||||
else -> blackPixel
|
else -> blackColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blackPixels > 18) {
|
if (blackPixels > 18) {
|
||||||
@ -296,7 +316,7 @@ object ImageUtil {
|
|||||||
break@outer
|
break@outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
whiteStrak || whitePixels > 22 -> darkBG = false
|
whiteStreak || whitePixels > 22 -> darkBG = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,58 +328,57 @@ object ImageUtil {
|
|||||||
if (topIsBlackStreak && bottomIsBlackStreak) {
|
if (topIsBlackStreak && bottomIsBlackStreak) {
|
||||||
darkBG = true
|
darkBG = true
|
||||||
}
|
}
|
||||||
if (darkBG) {
|
|
||||||
return if (isWhite(image.getPixel(left, bot)) && isWhite(image.getPixel(right, bot))) {
|
val isLandscape = context.resources.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
GradientDrawable(
|
if (isLandscape) {
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
return when {
|
||||||
intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
|
darkBG -> ColorDrawable(blackColor)
|
||||||
)
|
else -> ColorDrawable(whiteColor)
|
||||||
} else if (isWhite(image.getPixel(left, top)) && isWhite(image.getPixel(right, top))) {
|
|
||||||
GradientDrawable(
|
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
|
||||||
intArrayOf(backgroundColor, backgroundColor, blackPixel, blackPixel)
|
|
||||||
)
|
|
||||||
} else ColorDrawable(blackPixel)
|
|
||||||
}
|
}
|
||||||
if (topIsBlackStreak || (
|
}
|
||||||
topLeftIsDark && topRightIsDark &&
|
|
||||||
isDark(image.getPixel(left - offsetX, top)) && isDark(image.getPixel(right + offsetX, top)) &&
|
val botCornersIsWhite = botLeftPixel.isWhite() && botRightPixel.isWhite()
|
||||||
(topMidIsDark || overallBlackPixels > 9)
|
val topCornersIsWhite = topLeftPixel.isWhite() && topRightPixel.isWhite()
|
||||||
)
|
|
||||||
) {
|
val topCornersIsDark = topLeftIsDark && topRightIsDark
|
||||||
|
val botCornersIsDark = botLeftIsDark && botRightIsDark
|
||||||
|
|
||||||
|
val topOffsetCornersIsDark = image.getPixel(leftOffsetX, top).isDark() && image.getPixel(rightOffsetX, top).isDark()
|
||||||
|
val botOffsetCornersIsDark = image.getPixel(leftOffsetX, bot).isDark() && image.getPixel(rightOffsetX, bot).isDark()
|
||||||
|
|
||||||
|
val gradient = when {
|
||||||
|
darkBG && botCornersIsWhite -> {
|
||||||
|
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||||
|
}
|
||||||
|
darkBG && topCornersIsWhite -> {
|
||||||
|
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||||
|
}
|
||||||
|
darkBG -> {
|
||||||
|
return ColorDrawable(blackColor)
|
||||||
|
}
|
||||||
|
topIsBlackStreak || (topCornersIsDark && topOffsetCornersIsDark && (topMidIsDark || overallBlackPixels > 9)) -> {
|
||||||
|
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||||
|
}
|
||||||
|
bottomIsBlackStreak || (botCornersIsDark && botOffsetCornersIsDark && (bottomCenterPixel.isDark() || overallBlackPixels > 9)) -> {
|
||||||
|
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
return ColorDrawable(whiteColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GradientDrawable(
|
return GradientDrawable(
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
|
gradient
|
||||||
)
|
|
||||||
} else if (bottomIsBlackStreak || (
|
|
||||||
botLeftIsDark && botRightIsDark &&
|
|
||||||
isDark(image.getPixel(left - offsetX, bot)) && isDark(image.getPixel(right + offsetX, bot)) &&
|
|
||||||
(isDark(image.getPixel(midX, bot)) || overallBlackPixels > 9)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return GradientDrawable(
|
|
||||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
|
||||||
intArrayOf(backgroundColor, backgroundColor, blackPixel, blackPixel)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return ColorDrawable(backgroundColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isDark(color: Int): Boolean {
|
private fun Int.isDark(): Boolean =
|
||||||
return Color.red(color) < 40 && Color.blue(color) < 40 && Color.green(color) < 40 &&
|
red < 40 && blue < 40 && green < 40 && alpha > 200
|
||||||
Color.alpha(color) > 200
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isWhite(color: Int): Boolean {
|
private fun Int.isCloseTo(other: Int): Boolean =
|
||||||
return Color.red(color) + Color.blue(color) + Color.green(color) > 740
|
abs(red - other.red) < 30 && abs(green - other.green) < 30 && abs(blue - other.blue) < 30
|
||||||
}
|
|
||||||
|
|
||||||
private fun Boolean.toInt() = if (this) 1 else 0
|
private fun Int.isWhite(): Boolean =
|
||||||
|
red + blue + green > 740
|
||||||
private fun pixelIsClose(color1: Int, color2: Int): Boolean {
|
|
||||||
return abs(Color.red(color1) - Color.red(color2)) < 30 &&
|
|
||||||
abs(Color.green(color1) - Color.green(color2)) < 30 &&
|
|
||||||
abs(Color.blue(color1) - Color.blue(color2)) < 30
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
|
@ -275,6 +275,15 @@ object EXHMigrations {
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (oldVersion under 18) {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val readerTheme = prefs.getInt("pref_reader_theme_key", 3)
|
||||||
|
if (readerTheme == 4) {
|
||||||
|
prefs.edit {
|
||||||
|
putInt("pref_reader_theme_key", 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (oldVersion under 1) { } (1 is current release version)
|
// if (oldVersion under 1) { } (1 is current release version)
|
||||||
// do stuff here when releasing changed crap
|
// do stuff here when releasing changed crap
|
||||||
|
@ -180,7 +180,6 @@
|
|||||||
<string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
|
<string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
|
||||||
|
|
||||||
<!-- Browse settings -->
|
<!-- Browse settings -->
|
||||||
<string name="pref_latest_tab_language_code">Afficher le code de langue à côté du nom</string>
|
|
||||||
<string name="pref_latest_position">Dernière position de l\'onglet</string>
|
<string name="pref_latest_position">Dernière position de l\'onglet</string>
|
||||||
<string name="pref_latest_position_summery">Voulez-vous que le dernier onglet soit le premier onglet de la navigation? Cela en fera l\'onglet par défaut lors de l\'ouverture de la navigation, non recommandé si vous êtes sur des données ou un réseau mesuré</string>
|
<string name="pref_latest_position_summery">Voulez-vous que le dernier onglet soit le premier onglet de la navigation? Cela en fera l\'onglet par défaut lors de l\'ouverture de la navigation, non recommandé si vous êtes sur des données ou un réseau mesuré</string>
|
||||||
<string name="pref_source_navigation">Remplacer le dernier bouton</string>
|
<string name="pref_source_navigation">Remplacer le dernier bouton</string>
|
||||||
@ -232,8 +231,6 @@
|
|||||||
<string name="auto_webtoon_mode">Mode Webtoon automatique</string>
|
<string name="auto_webtoon_mode">Mode Webtoon automatique</string>
|
||||||
<string name="auto_webtoon_mode_summary">Utilisez le mode Webtoon automatique pour les mangas détectés comme susceptibles d\'utiliser le format de bande longue</string>
|
<string name="auto_webtoon_mode_summary">Utilisez le mode Webtoon automatique pour les mangas détectés comme susceptibles d\'utiliser le format de bande longue</string>
|
||||||
<string name="enable_zoom_out">Activer le zoom arrière</string>
|
<string name="enable_zoom_out">Activer le zoom arrière</string>
|
||||||
<string name="smart_based_on_page">Intelligent(basé sur la page)</string>
|
|
||||||
<string name="smart_based_on_page_and_theme">Intelligent(basé sur la page et le thème)</string>
|
|
||||||
<string name="tap_scroll_page">Appuyez sur faire défiler par page</string>
|
<string name="tap_scroll_page">Appuyez sur faire défiler par page</string>
|
||||||
|
|
||||||
<!-- <string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>-->
|
<!-- <string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>-->
|
||||||
|
@ -209,8 +209,6 @@
|
|||||||
<string name="auto_webtoon_mode">Modo Auto Webtoon</string>
|
<string name="auto_webtoon_mode">Modo Auto Webtoon</string>
|
||||||
<string name="auto_webtoon_mode_summary">Use o modo webtoon automático para mangás que são detectados provavelmente no formato longstrip</string>
|
<string name="auto_webtoon_mode_summary">Use o modo webtoon automático para mangás que são detectados provavelmente no formato longstrip</string>
|
||||||
<string name="enable_zoom_out">Enable zoom out</string>
|
<string name="enable_zoom_out">Enable zoom out</string>
|
||||||
<string name="smart_based_on_page">Inteligente (Baseado na página)</string>
|
|
||||||
<string name="smart_based_on_page_and_theme">Inteligente (baseado na página e no tema)</string>
|
|
||||||
<string name="tap_scroll_page">Tap scroll by page</string>
|
<string name="tap_scroll_page">Tap scroll by page</string>
|
||||||
<string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>
|
<string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>
|
||||||
|
|
||||||
|
@ -240,8 +240,6 @@
|
|||||||
<string name="auto_webtoon_mode">Автоматический веб-комикс</string>
|
<string name="auto_webtoon_mode">Автоматический веб-комикс</string>
|
||||||
<string name="auto_webtoon_mode_summary">Использовать автоматический режим для веб-комикса, в которых выявлены вероятно, использовать длинные полосы формате</string>
|
<string name="auto_webtoon_mode_summary">Использовать автоматический режим для веб-комикса, в которых выявлены вероятно, использовать длинные полосы формате</string>
|
||||||
<string name="enable_zoom_out">Включить зум</string>
|
<string name="enable_zoom_out">Включить зум</string>
|
||||||
<string name="smart_based_on_page">Умный (по странице)</string>
|
|
||||||
<string name="smart_based_on_page_and_theme">Умный (по странице и теме)</string>
|
|
||||||
<string name="tap_scroll_page">Нажмите прокрутить по странице</string>
|
<string name="tap_scroll_page">Нажмите прокрутить по странице</string>
|
||||||
<string name="tap_scroll_page_summary">Нажатие будет прокручиваться по странице, а не по размеру экрана, если эта опция включена</string>
|
<string name="tap_scroll_page_summary">Нажатие будет прокручиваться по странице, а не по размеру экрана, если эта опция включена</string>
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
<item>@string/black_background</item>
|
<item>@string/black_background</item>
|
||||||
<item>@string/gray_background</item>
|
<item>@string/gray_background</item>
|
||||||
<item>@string/white_background</item>
|
<item>@string/white_background</item>
|
||||||
<item>@string/smart_based_on_page</item>
|
<item>@string/automatic_background</item>
|
||||||
<item>@string/smart_based_on_page_and_theme</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="reader_themes_values">
|
<string-array name="reader_themes_values">
|
||||||
@ -22,7 +21,6 @@
|
|||||||
<item>2</item>
|
<item>2</item>
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
<item>4</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="image_scale_type">
|
<string-array name="image_scale_type">
|
||||||
|
@ -303,6 +303,7 @@
|
|||||||
<string name="white_background">White</string>
|
<string name="white_background">White</string>
|
||||||
<string name="gray_background">Gray</string>
|
<string name="gray_background">Gray</string>
|
||||||
<string name="black_background">Black</string>
|
<string name="black_background">Black</string>
|
||||||
|
<string name="automatic_background">Automatic</string>
|
||||||
<string name="pref_viewer_type">Default reading mode</string>
|
<string name="pref_viewer_type">Default reading mode</string>
|
||||||
<string name="default_viewer">Default</string>
|
<string name="default_viewer">Default</string>
|
||||||
<string name="default_nav">Default</string>
|
<string name="default_nav">Default</string>
|
||||||
|
@ -249,8 +249,6 @@
|
|||||||
<string name="auto_webtoon_mode">Auto Webtoon Mode</string>
|
<string name="auto_webtoon_mode">Auto Webtoon Mode</string>
|
||||||
<string name="auto_webtoon_mode_summary">Use auto webtoon mode for manga that are detected to likely use the long strip format</string>
|
<string name="auto_webtoon_mode_summary">Use auto webtoon mode for manga that are detected to likely use the long strip format</string>
|
||||||
<string name="enable_zoom_out">Enable zoom out</string>
|
<string name="enable_zoom_out">Enable zoom out</string>
|
||||||
<string name="smart_based_on_page">Smart (based on page)</string>
|
|
||||||
<string name="smart_based_on_page_and_theme">Smart (based on page and theme)</string>
|
|
||||||
<string name="tap_scroll_page">Tap scroll by page</string>
|
<string name="tap_scroll_page">Tap scroll by page</string>
|
||||||
<string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>
|
<string name="tap_scroll_page_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>
|
||||||
<string name="reader_bottom_buttons">Reader Bottom Buttons</string>
|
<string name="reader_bottom_buttons">Reader Bottom Buttons</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user