From 6238f06d39e6f3bccbe521a82923cb352105ad8b Mon Sep 17 00:00:00 2001
From: Andreas <andreas.everos@gmail.com>
Date: Wed, 5 May 2021 04:46:42 +0200
Subject: [PATCH] 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
---
 app/build.gradle.kts                          |   2 +-
 .../ui/reader/loader/HttpPageLoader.kt        |  17 --
 .../ui/reader/viewer/pager/PagerConfig.kt     |   6 +
 .../ui/reader/viewer/pager/PagerPageHolder.kt |  68 +-----
 .../ui/setting/SettingsReaderController.kt    |   4 +-
 .../kanade/tachiyomi/util/system/ImageUtil.kt | 223 ++++++++++--------
 app/src/main/java/exh/EXHMigrations.kt        |   9 +
 app/src/main/res/values-fr/strings_sy.xml     |   3 -
 app/src/main/res/values-pt-rBR/strings_sy.xml |   2 -
 app/src/main/res/values-ru/strings_sy.xml     |   2 -
 app/src/main/res/values/arrays.xml            |   4 +-
 app/src/main/res/values/strings.xml           |   1 +
 app/src/main/res/values/strings_sy.xml        |   2 -
 13 files changed, 145 insertions(+), 198 deletions(-)

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index fe8b103d2..18ed24e1f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -34,7 +34,7 @@ android {
         minSdkVersion(AndroidConfig.minSdk)
         targetSdkVersion(AndroidConfig.targetSdk)
         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-        versionCode = 17
+        versionCode = 18
         versionName = "1.6.2"
 
         buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
index 736771374..4c941bbf6 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
@@ -1,15 +1,12 @@
 package eu.kanade.tachiyomi.ui.reader.loader
 
-import android.graphics.BitmapFactory
 import eu.kanade.tachiyomi.data.cache.ChapterCache
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.source.model.Page
 import eu.kanade.tachiyomi.source.online.HttpSource
 import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
 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.system.ImageUtil
 import exh.source.isEhBasedSource
 import rx.Completable
 import rx.Observable
@@ -258,20 +255,6 @@ class HttpPageLoader(
                 }
             }
             .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.status = Page.READY
             }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
index 060cdae4d..0baad5d54 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
@@ -23,6 +23,9 @@ class PagerConfig(
     preferences: PreferencesHelper = Injekt.get()
 ) : ViewerConfig(preferences, scope) {
 
+    var automaticBackground = false
+        private set
+
     var dualPageSplitChangedListener: ((Boolean) -> Unit)? = null
 
     var imageScaleType = 1
@@ -39,6 +42,9 @@ class PagerConfig(
     // SY <--
 
     init {
+        preferences.readerTheme()
+            .register({ automaticBackground = it == 3 }, { imagePropertyChangedListener?.invoke() })
+
         preferences.imageScaleType()
             .register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })
 
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
index c06ddbf91..7b78bafaf 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
@@ -1,11 +1,8 @@
 package eu.kanade.tachiyomi.ui.reader.viewer.pager
 
 import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.BitmapFactory
 import android.graphics.PointF
 import android.graphics.drawable.Animatable
-import android.graphics.drawable.Drawable
 import android.view.GestureDetector
 import android.view.Gravity
 import android.view.MotionEvent
@@ -24,26 +21,19 @@ import com.davemorrissey.labs.subscaleview.ImageSource
 import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
 import com.github.chrisbanes.photoview.PhotoView
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.source.model.Page
 import eu.kanade.tachiyomi.ui.reader.model.InsertPage
 import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
 import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
 import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
 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.dpToPx
 import eu.kanade.tachiyomi.widget.ViewPagerAdapter
-import exh.util.isInNightMode
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
 import rx.Observable
 import rx.Subscription
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
 import java.io.InputStream
 import java.nio.ByteBuffer
 import java.util.concurrent.TimeUnit
@@ -104,12 +94,6 @@ class PagerPageHolder(
      */
     private var readImageHeaderSubscription: Subscription? = null
 
-    // SY -->
-    private val readerTheme by lazy {
-        Injekt.get<PreferencesHelper>().readerTheme().get()
-    }
-    // SY <--
-
     init {
         addView(progressBar)
         observeStatus()
@@ -254,30 +238,12 @@ class PagerPageHolder(
             .observeOn(AndroidSchedulers.mainThread())
             .doOnNext { isAnimated ->
                 if (!isAnimated) {
-                    // SY -->
-                    if (readerTheme >= 3) {
-                        val imageView = initSubsamplingImageView()
-                        if (page.bg != null && page.bgType == getBGType(readerTheme, context)) {
-                            imageView.setImage(ImageSource.inputStream(openStream!!))
-                            imageView.background = page.bg
+                    initSubsamplingImageView().apply {
+                        if (viewer.config.automaticBackground) {
+                            background = ImageUtil.chooseBackground(context, openStream!!)
                         }
-                        // if the user switches to automatic when pages are already cached, the bg needs to be loaded
-                        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!!))
+                        setImage(ImageSource.inputStream(openStream!!))
                     }
-                    // SY <--
                 } else {
                     initImageView().setImage(openStream!!)
                 }
@@ -331,22 +297,6 @@ class PagerPageHolder(
         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.
      */
@@ -550,14 +500,4 @@ class PagerPageHolder(
             .build()
         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 <--
 }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
index adf072dc9..3202914d1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
@@ -120,8 +120,8 @@ class SettingsReaderController : SettingsController() {
             intListPreference {
                 key = Keys.readerTheme
                 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)
-                entryValues = arrayOf("1", "2", "0", "3", "4")
+                entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background, R.string.automatic_background)
+                entryValues = arrayOf("1", "2", "0", "3")
                 defaultValue = "3"
                 summary = "%s"
             }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
index 0ea2754c2..6a178a090 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt
@@ -1,6 +1,7 @@
 package eu.kanade.tachiyomi.util.system
 
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
 import android.graphics.Canvas
@@ -9,8 +10,11 @@ import android.graphics.Rect
 import android.graphics.drawable.ColorDrawable
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.GradientDrawable
+import androidx.core.graphics.alpha
+import androidx.core.graphics.blue
 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.ByteArrayOutputStream
 import java.io.InputStream
@@ -161,16 +165,22 @@ object ImageUtil {
         RIGHT, LEFT
     }
 
-    // SY -->
-    @Suppress("UNUSED_VARIABLE")
-    fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable {
-        val backgroundColor = if (alwaysUseWhite) Color.WHITE else {
-            context.getResourceColor(R.attr.colorPrimary)
-        }
-        if (image == null) return ColorDrawable(backgroundColor)
+    /**
+     * Algorithm for determining what background to accompany a comic/manga page
+     */
+    fun chooseBackground(context: Context, imageStream: InputStream): Drawable {
+        imageStream.mark(imageStream.available() + 1)
+
+        val image = BitmapFactory.decodeStream(imageStream)
+
+        imageStream.reset()
+
+        val whiteColor = Color.WHITE
+        if (image == null) return ColorDrawable(whiteColor)
         if (image.width < 50 || image.height < 50) {
-            return ColorDrawable(backgroundColor)
+            return ColorDrawable(whiteColor)
         }
+
         val top = 5
         val bot = image.height - 5
         val left = (image.width * 0.0275).toInt()
@@ -178,42 +188,52 @@ object ImageUtil {
         val midX = image.width / 2
         val midY = image.height / 2
         val offsetX = (image.width * 0.01).toInt()
-        val offsetY = (image.height * 0.01).toInt()
-        val topLeftIsDark = isDark(image.getPixel(left, top))
-        val topRightIsDark = isDark(image.getPixel(right, top))
-        val midLeftIsDark = isDark(image.getPixel(left, midY))
-        val midRightIsDark = isDark(image.getPixel(right, midY))
-        val topMidIsDark = isDark(image.getPixel(midX, top))
-        val botLeftIsDark = isDark(image.getPixel(left, bot))
-        val botRightIsDark = isDark(image.getPixel(right, bot))
+        val leftOffsetX = left - offsetX
+        val rightOffsetX = right + offsetX
+
+        val topLeftPixel = image.getPixel(left, top)
+        val topRightPixel = image.getPixel(right, top)
+        val midLeftPixel = image.getPixel(left, midY)
+        val midRightPixel = image.getPixel(right, midY)
+        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)) ||
             (topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
 
-        if (!isWhite(image.getPixel(left, top)) && pixelIsClose(image.getPixel(left, top), image.getPixel(midX, top)) &&
-            !isWhite(image.getPixel(midX, top)) && pixelIsClose(image.getPixel(midX, top), image.getPixel(right, top)) &&
-            !isWhite(image.getPixel(right, top)) && pixelIsClose(image.getPixel(right, top), image.getPixel(right, bot)) &&
-            !isWhite(image.getPixel(right, bot)) && pixelIsClose(image.getPixel(right, bot), image.getPixel(midX, bot)) &&
-            !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))
-        ) {
-            return ColorDrawable(image.getPixel(left, top))
+        val topAndBotPixels = listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
+        val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
+            val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
+            !color.isWhite() && color.isCloseTo(other)
+        }
+        if (isNotWhiteAndCloseTo.all { it }) {
+            return ColorDrawable(topLeftPixel)
         }
 
-        if (isWhite(image.getPixel(left, top)).toInt() +
-            isWhite(image.getPixel(right, top)).toInt() +
-            isWhite(image.getPixel(left, bot)).toInt() +
-            isWhite(image.getPixel(right, bot)).toInt() > 2
-        ) {
+        val cornerPixels = listOf(topLeftPixel, topRightPixel, botLeftPixel, botRightPixel)
+        val numberOfWhiteCorners = cornerPixels.map { cornerPixel -> cornerPixel.isWhite() }
+            .filter { it }
+            .size
+        if (numberOfWhiteCorners > 2) {
             darkBG = false
         }
 
-        var blackPixel = when {
-            topLeftIsDark -> image.getPixel(left, top)
-            topRightIsDark -> image.getPixel(right, top)
-            botLeftIsDark -> image.getPixel(left, bot)
-            botRightIsDark -> image.getPixel(right, bot)
-            else -> backgroundColor
+        var blackColor = when {
+            topLeftIsDark -> topLeftPixel
+            topRightIsDark -> topRightPixel
+            botLeftIsDark -> botLeftPixel
+            botRightIsDark -> botRightPixel
+            else -> whiteColor
         }
 
         var overallWhitePixels = 0
@@ -222,32 +242,32 @@ object ImageUtil {
         var topWhiteStreak = 0
         var botBlackStreak = 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 whitePixels = 0
             var blackPixelsStreak = 0
             var blackPixels = 0
             var blackStreak = false
-            var whiteStrak = false
+            var whiteStreak = false
             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 pixelOff = image.getPixel(x + (if (x < image.width / 2) -offsetX else offsetX), y)
-                if (isWhite(pixel)) {
+                if (pixel.isWhite()) {
                     whitePixelsStreak++
                     whitePixels++
                     if (notOffset) {
                         overallWhitePixels++
                     }
                     if (whitePixelsStreak > 14) {
-                        whiteStrak = true
+                        whiteStreak = true
                     }
                     if (whitePixelsStreak > 6 && whitePixelsStreak >= index - 1) {
                         topWhiteStreak = whitePixelsStreak
                     }
                 } else {
                     whitePixelsStreak = 0
-                    if (isDark(pixel) && isDark(pixelOff)) {
+                    if (pixel.isDark() && pixelOff.isDark()) {
                         blackPixels++
                         if (notOffset) {
                             overallBlackPixels++
@@ -256,7 +276,7 @@ object ImageUtil {
                         if (blackPixelsStreak >= 14) {
                             blackStreak = true
                         }
-                        continue
+                        continue@inner
                     }
                 }
                 if (blackPixelsStreak > 6 && blackPixelsStreak >= index - 1) {
@@ -271,11 +291,11 @@ object ImageUtil {
             }
             when {
                 blackPixels > 22 -> {
-                    if (x == right || x == right + offsetX) {
-                        blackPixel = when {
-                            topRightIsDark -> image.getPixel(right, top)
-                            botRightIsDark -> image.getPixel(right, bot)
-                            else -> blackPixel
+                    if (x == right || x == rightOffsetX) {
+                        blackColor = when {
+                            topRightIsDark -> topRightPixel
+                            botRightIsDark -> botRightPixel
+                            else -> blackColor
                         }
                     }
                     darkBG = true
@@ -284,11 +304,11 @@ object ImageUtil {
                 }
                 blackStreak -> {
                     darkBG = true
-                    if (x == right || x == right + offsetX) {
-                        blackPixel = when {
-                            topRightIsDark -> image.getPixel(right, top)
-                            botRightIsDark -> image.getPixel(right, bot)
-                            else -> blackPixel
+                    if (x == right || x == rightOffsetX) {
+                        blackColor = when {
+                            topRightIsDark -> topRightPixel
+                            botRightIsDark -> botRightPixel
+                            else -> blackColor
                         }
                     }
                     if (blackPixels > 18) {
@@ -296,7 +316,7 @@ object ImageUtil {
                         break@outer
                     }
                 }
-                whiteStrak || whitePixels > 22 -> darkBG = false
+                whiteStreak || whitePixels > 22 -> darkBG = false
             }
         }
 
@@ -308,58 +328,57 @@ object ImageUtil {
         if (topIsBlackStreak && bottomIsBlackStreak) {
             darkBG = true
         }
-        if (darkBG) {
-            return if (isWhite(image.getPixel(left, bot)) && isWhite(image.getPixel(right, bot))) {
-                GradientDrawable(
-                    GradientDrawable.Orientation.TOP_BOTTOM,
-                    intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
-                )
-            } 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)
+
+        val isLandscape = context.resources.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE
+        if (isLandscape) {
+            return when {
+                darkBG -> ColorDrawable(blackColor)
+                else -> ColorDrawable(whiteColor)
+            }
         }
-        if (topIsBlackStreak || (
-            topLeftIsDark && topRightIsDark &&
-                isDark(image.getPixel(left - offsetX, top)) && isDark(image.getPixel(right + offsetX, top)) &&
-                (topMidIsDark || overallBlackPixels > 9)
-            )
-        ) {
-            return GradientDrawable(
-                GradientDrawable.Orientation.TOP_BOTTOM,
-                intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
-            )
-        } 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)
-            )
+
+        val botCornersIsWhite = botLeftPixel.isWhite() && botRightPixel.isWhite()
+        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 ColorDrawable(backgroundColor)
+
+        return GradientDrawable(
+            GradientDrawable.Orientation.TOP_BOTTOM,
+            gradient
+        )
     }
 
-    private fun isDark(color: Int): Boolean {
-        return Color.red(color) < 40 && Color.blue(color) < 40 && Color.green(color) < 40 &&
-            Color.alpha(color) > 200
-    }
+    private fun Int.isDark(): Boolean =
+        red < 40 && blue < 40 && green < 40 && alpha > 200
 
-    private fun isWhite(color: Int): Boolean {
-        return Color.red(color) + Color.blue(color) + Color.green(color) > 740
-    }
+    private fun Int.isCloseTo(other: Int): Boolean =
+        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 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 <--
+    private fun Int.isWhite(): Boolean =
+        red + blue + green > 740
 }
diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt
index 463187f80..2ec9abc0f 100644
--- a/app/src/main/java/exh/EXHMigrations.kt
+++ b/app/src/main/java/exh/EXHMigrations.kt
@@ -275,6 +275,15 @@ object EXHMigrations {
                             .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)
                 // do stuff here when releasing changed crap
diff --git a/app/src/main/res/values-fr/strings_sy.xml b/app/src/main/res/values-fr/strings_sy.xml
index 96c5f1407..c16498e85 100644
--- a/app/src/main/res/values-fr/strings_sy.xml
+++ b/app/src/main/res/values-fr/strings_sy.xml
@@ -180,7 +180,6 @@
     <string name="library_group_updates_all">Lancer des mises à jour de catégorie tout le temps</string>
 
     <!-- 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_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>
@@ -232,8 +231,6 @@
     <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="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_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>-->
diff --git a/app/src/main/res/values-pt-rBR/strings_sy.xml b/app/src/main/res/values-pt-rBR/strings_sy.xml
index 3040b0d63..043a17f9f 100644
--- a/app/src/main/res/values-pt-rBR/strings_sy.xml
+++ b/app/src/main/res/values-pt-rBR/strings_sy.xml
@@ -209,8 +209,6 @@
     <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="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_summary">Tapping will scroll by page instead of screen size when this option is enabled</string>
 
diff --git a/app/src/main/res/values-ru/strings_sy.xml b/app/src/main/res/values-ru/strings_sy.xml
index 0905a0106..b6579526c 100644
--- a/app/src/main/res/values-ru/strings_sy.xml
+++ b/app/src/main/res/values-ru/strings_sy.xml
@@ -240,8 +240,6 @@
     <string name="auto_webtoon_mode">Автоматический веб-комикс</string>
     <string name="auto_webtoon_mode_summary">Использовать автоматический режим для веб-комикса, в которых выявлены вероятно, использовать длинные полосы формате</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_summary">Нажатие будет прокручиваться по странице, а не по размеру экрана, если эта опция включена</string>
 
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 22c68d3e2..a516a943a 100755
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -13,8 +13,7 @@
         <item>@string/black_background</item>
         <item>@string/gray_background</item>
         <item>@string/white_background</item>
-        <item>@string/smart_based_on_page</item>
-        <item>@string/smart_based_on_page_and_theme</item>
+        <item>@string/automatic_background</item>
     </string-array>
 
     <string-array name="reader_themes_values">
@@ -22,7 +21,6 @@
         <item>2</item>
         <item>0</item>
         <item>3</item>
-        <item>4</item>
     </string-array>
 
     <string-array name="image_scale_type">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1f6a5004b..1da9c95b2 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -303,6 +303,7 @@
     <string name="white_background">White</string>
     <string name="gray_background">Gray</string>
     <string name="black_background">Black</string>
+    <string name="automatic_background">Automatic</string>
     <string name="pref_viewer_type">Default reading mode</string>
     <string name="default_viewer">Default</string>
     <string name="default_nav">Default</string>
diff --git a/app/src/main/res/values/strings_sy.xml b/app/src/main/res/values/strings_sy.xml
index 8f9ee1799..a96cce69f 100644
--- a/app/src/main/res/values/strings_sy.xml
+++ b/app/src/main/res/values/strings_sy.xml
@@ -249,8 +249,6 @@
     <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="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_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>