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:
Andreas 2021-05-05 04:46:42 +02:00 committed by Jobobby04
parent 7e612e63b4
commit 6238f06d39
13 changed files with 145 additions and 198 deletions

View File

@ -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()}\"")

View File

@ -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
} }

View File

@ -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() })

View File

@ -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 <--
} }

View File

@ -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"
} }

View File

@ -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 <--
} }

View File

@ -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

View File

@ -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>-->

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>