From b98dfd65b5a897bf82673cc11bb4dbe5eac8f0ca Mon Sep 17 00:00:00 2001
From: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Date: Wed, 20 Nov 2024 17:13:15 +0600
Subject: [PATCH] Add option to lower the threshold for hardware bitmaps

Closes #1436
Closes #1486

(cherry picked from commit dcddac5daaff3ec89c8507c35dc13d345ffdb6d7)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
---
 .../eu/kanade/domain/base/BasePreferences.kt  |  3 +++
 .../settings/screen/SettingsAdvancedScreen.kt | 15 +++++++++++++
 app/src/main/java/eu/kanade/tachiyomi/App.kt  | 10 +++++++++
 .../ui/reader/viewer/ReaderPageImageView.kt   |  3 +--
 .../eu/kanade/tachiyomi/util/system/GLUtil.kt | 21 +++++++++++++------
 .../core/common/util/system/ImageUtil.kt      |  4 +++-
 .../moko-resources/base/strings.xml           |  2 ++
 7 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
index 1700f0a02..e2651a5f3 100644
--- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
@@ -2,6 +2,7 @@ package eu.kanade.domain.base
 
 import android.content.Context
 import dev.icerock.moko.resources.StringResource
+import eu.kanade.tachiyomi.util.system.GLUtil
 import tachiyomi.core.common.preference.Preference
 import tachiyomi.core.common.preference.PreferenceStore
 import tachiyomi.i18n.MR
@@ -30,4 +31,6 @@ class BasePreferences(
     }
 
     fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
+
+    fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
 }
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
index 24255fe74..1a6cab3a1 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
@@ -59,6 +59,7 @@ import eu.kanade.tachiyomi.source.AndroidSourceManager
 import eu.kanade.tachiyomi.ui.more.OnboardingScreen
 import eu.kanade.tachiyomi.util.CrashLogUtil
 import eu.kanade.tachiyomi.util.storage.DiskUtil
+import eu.kanade.tachiyomi.util.system.GLUtil
 import eu.kanade.tachiyomi.util.system.isDevFlavor
 import eu.kanade.tachiyomi.util.system.isPreviewBuildType
 import eu.kanade.tachiyomi.util.system.isShizukuInstalled
@@ -366,9 +367,23 @@ object SettingsAdvancedScreen : SearchableSettings {
                 basePreferences.displayProfile().set(uri.toString())
             }
         }
+        val hardwareBitmapThresholdPref = basePreferences.hardwareBitmapThreshold()
+        val hardwareBitmapThreshold by hardwareBitmapThresholdPref.collectAsState()
         return Preference.PreferenceGroup(
             title = stringResource(MR.strings.pref_category_reader),
             preferenceItems = persistentListOf(
+                Preference.PreferenceItem.ListPreference(
+                    pref = hardwareBitmapThresholdPref,
+                    title = stringResource(MR.strings.pref_hardware_bitmap_threshold),
+                    subtitle = stringResource(
+                        MR.strings.pref_hardware_bitmap_threshold_summary,
+                        hardwareBitmapThreshold,
+                    ),
+                    enabled = GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT,
+                    entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
+                        .associateWith { it.toString() }
+                        .toImmutableMap(),
+                ),
                 Preference.PreferenceItem.TextPreference(
                     title = stringResource(MR.strings.pref_display_profile),
                     subtitle = basePreferences.displayProfile().get(),
diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt
index a965906e5..fe0a1ab4b 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/App.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt
@@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
 import eu.kanade.tachiyomi.network.NetworkPreferences
 import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
 import eu.kanade.tachiyomi.util.system.DeviceUtil
+import eu.kanade.tachiyomi.util.system.GLUtil
 import eu.kanade.tachiyomi.util.system.WebViewUtil
 import eu.kanade.tachiyomi.util.system.animatorDurationScale
 import eu.kanade.tachiyomi.util.system.cancelNotification
@@ -80,6 +81,7 @@ import org.conscrypt.Conscrypt
 import tachiyomi.core.common.i18n.stringResource
 import tachiyomi.core.common.preference.Preference
 import tachiyomi.core.common.preference.PreferenceStore
+import tachiyomi.core.common.util.system.ImageUtil
 import tachiyomi.core.common.util.system.logcat
 import tachiyomi.domain.storage.service.StorageManager
 import tachiyomi.i18n.MR
@@ -175,6 +177,14 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
             .onEach(FirebaseConfig::setCrashlyticsEnabled)
             .launchIn(scope)
 
+        basePreferences.hardwareBitmapThreshold().let { preference ->
+            if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT)
+        }
+
+        basePreferences.hardwareBitmapThreshold().changes()
+            .onEach { ImageUtil.hardwareBitmapThreshold = it }
+            .launchIn(scope)
+
         setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
 
         // Updates widget update
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt
index 75db2fb8a..ef195b117 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt
@@ -37,7 +37,6 @@ import eu.kanade.domain.base.BasePreferences
 import eu.kanade.tachiyomi.data.coil.cropBorders
 import eu.kanade.tachiyomi.data.coil.customDecoder
 import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
-import eu.kanade.tachiyomi.util.system.GLUtil
 import eu.kanade.tachiyomi.util.system.animatorDurationScale
 import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
 import okio.BufferedSource
@@ -235,7 +234,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
         } else {
             SubsamplingScaleImageView(context)
         }.apply {
-            setMaxTileSize(GLUtil.maxTextureSize)
+            setMaxTileSize(ImageUtil.hardwareBitmapThreshold)
             setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
             setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE)
             setMinimumTileDpi(180)
diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/GLUtil.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/GLUtil.kt
index 98bccf126..16f3203fb 100644
--- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/GLUtil.kt
+++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/GLUtil.kt
@@ -3,10 +3,9 @@ package eu.kanade.tachiyomi.util.system
 import javax.microedition.khronos.egl.EGL10
 import javax.microedition.khronos.egl.EGLConfig
 import javax.microedition.khronos.egl.EGLContext
-import kotlin.math.max
 
 object GLUtil {
-    val maxTextureSize: Int by lazy {
+    val DEVICE_TEXTURE_LIMIT: Int by lazy {
         // Get EGL Display
         val egl = EGLContext.getEGL() as EGL10
         val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
@@ -38,10 +37,20 @@ object GLUtil {
         // Release
         egl.eglTerminate(display)
 
-        // Return largest texture size found, or default
-        max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION)
+        // Return largest texture size found (after making it a multiplier of [Multiplier]), or default
+        if (maximumTextureSize > SAFE_TEXTURE_LIMIT) {
+            (maximumTextureSize / MULTIPLIER) * MULTIPLIER
+        } else {
+            SAFE_TEXTURE_LIMIT
+        }
+    }
+
+    const val SAFE_TEXTURE_LIMIT: Int = 2048
+
+    val CUSTOM_TEXTURE_LIMIT_OPTIONS: List<Int> by lazy {
+        val steps = ((DEVICE_TEXTURE_LIMIT / MULTIPLIER) - 1)
+        List(steps) { (it + 2) * MULTIPLIER }.asReversed()
     }
 }
 
-// Safe minimum default size
-private const val IMAGE_MAX_BITMAP_DIMENSION = 2048
+private const val MULTIPLIER: Int = 1024
diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
index e16ddd870..6dafd01af 100644
--- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
+++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
@@ -370,8 +370,10 @@ object ImageUtil {
         }
     }
 
+    var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT
+
     private fun canUseHardwareBitmap(width: Int, height: Int): Boolean {
-        return maxOf(width, height) <= GLUtil.maxTextureSize
+        return maxOf(width, height) <= hardwareBitmapThreshold
     }
 
     /**
diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml
index bbc72496d..c8d625de3 100755
--- a/i18n/src/commonMain/moko-resources/base/strings.xml
+++ b/i18n/src/commonMain/moko-resources/base/strings.xml
@@ -392,6 +392,8 @@
     <string name="pref_show_reading_mode">Show reading mode</string>
     <string name="pref_show_reading_mode_summary">Briefly show current mode when reader is opened</string>
     <string name="pref_display_profile">Custom display profile</string>
+    <string name="pref_hardware_bitmap_threshold">Custom hardware bitmap threshold</string>
+    <string name="pref_hardware_bitmap_threshold_summary">If reader loads a blank image incrementally reduce the threshold.\nSelected: %s</string>
     <string name="pref_crop_borders">Crop borders</string>
     <string name="pref_custom_brightness">Custom brightness</string>
     <string name="pref_grayscale">Grayscale</string>