From fd73958923bda0b6a0a522cb9ebf14f235eeca93 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:01:54 +0700 Subject: [PATCH] feat: Add option to lower the threshold for hardware bitmaps --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 23 ++++-- .../ui/reader/viewer/ReaderPageImageView.kt | 6 +- .../controllers/SettingsAdvancedController.kt | 21 +++++- .../eu/kanade/tachiyomi/util/system/GLUtil.kt | 74 ++++++++++--------- .../kanade/tachiyomi/util/system/ImageUtil.kt | 4 +- .../java/yokai/domain/base/BasePreferences.kt | 3 + .../moko-resources/base/strings.xml | 2 + 7 files changed, 85 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 1c3c1c1c34..5947409922 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -51,6 +51,8 @@ import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.source.SourcePresenter import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata import eu.kanade.tachiyomi.util.system.AuthenticatorUtil +import eu.kanade.tachiyomi.util.system.GLUtil +import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.localeContext import eu.kanade.tachiyomi.util.system.notification @@ -102,6 +104,10 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F modules(preferenceModule(this@App), appModule(this@App), domainModule()) } + ProcessLifecycleOwner.get().lifecycle.addObserver(this) + + val scope = ProcessLifecycleOwner.get().lifecycleScope + basePreferences.crashReport().changes() .onEach { try { @@ -110,18 +116,23 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F // Probably already enabled/disabled } } - .launchIn(ProcessLifecycleOwner.get().lifecycleScope) + .launchIn(scope) setupNotificationChannels() - ProcessLifecycleOwner.get().lifecycle.addObserver(this) - MangaCoverMetadata.load() preferences.nightMode().changes() .onEach { AppCompatDelegate.setDefaultNightMode(it) } - .launchIn(ProcessLifecycleOwner.get().lifecycleScope) + .launchIn(scope) - ProcessLifecycleOwner.get().lifecycleScope.launchIO { + basePreferences.hardwareBitmapThreshold().let { preference -> + if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT) + } + basePreferences.hardwareBitmapThreshold().changes() + .onEach { ImageUtil.hardwareBitmapThreshold = it } + .launchIn(scope) + + scope.launchIO { with(TachiyomiWidgetManager()) { this@App.init() } } @@ -160,7 +171,7 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F notificationManager.cancel(Notifications.ID_INCOGNITO_MODE) } } - .launchIn(ProcessLifecycleOwner.get().lifecycleScope) + .launchIn(scope) initializeMigrator() } 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 b9ac961dad..74514ff0ac 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 @@ -36,7 +36,6 @@ import eu.kanade.tachiyomi.data.coil.customDecoder import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView import eu.kanade.tachiyomi.util.system.DeviceUtil -import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale import okio.BufferedSource @@ -127,7 +126,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) @@ -232,8 +231,7 @@ open class ReaderPageImageView @JvmOverloads constructor( isVisible = true } is BufferedSource -> { - // FIXME: Remove `ImageUtil.isMaxTextureSizeExceeded` after porting https://github.com/mihonapp/mihon/commit/dcddac5daaff3ec89c8507c35dc13d345ffdb6d7#diff-cbb19957efc1d319c0cdc62d5cf4b32bad5b51da21d3c67c3d4256200eb9c5d1 - if (!isWebtoon || ImageUtil.isMaxTextureSizeExceeded(data)) { + if (!isWebtoon) { setHardwareConfig(!ImageUtil.isMaxTextureSizeExceeded(data)) setImage(ImageSource.inputStream(data.inputStream())) isVisible = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/controllers/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/controllers/SettingsAdvancedController.kt index 721ba889e2..33110d2913 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/controllers/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/controllers/SettingsAdvancedController.kt @@ -57,6 +57,7 @@ import eu.kanade.tachiyomi.ui.setting.preference import eu.kanade.tachiyomi.ui.setting.preferenceCategory import eu.kanade.tachiyomi.ui.setting.switchPreference import eu.kanade.tachiyomi.util.CrashLogUtil +import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.disableItems import eu.kanade.tachiyomi.util.system.e import eu.kanade.tachiyomi.util.system.isPackageInstalled @@ -73,6 +74,7 @@ import eu.kanade.tachiyomi.util.view.setPositiveButton import eu.kanade.tachiyomi.util.view.setTitle import eu.kanade.tachiyomi.util.view.withFadeTransaction import java.io.File +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers @@ -393,8 +395,25 @@ class SettingsAdvancedController : SettingsLegacyController() { preferenceCategory { titleRes = MR.strings.reader + listPreference(activity) { + bindTo(basePreferences.hardwareBitmapThreshold()) + titleRes = MR.strings.pref_hardware_bitmap_threshold + + val entryMap = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS + .associateWith { it.toString() } + .toImmutableMap() + entries = entryMap.values.toList() + entryValues = entryMap.keys.map { it.toString() }.toList() + + isVisible = GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT + + basePreferences.hardwareBitmapThreshold().changesIn(viewScope) { threshold -> + summary = context.getString(MR.strings.pref_hardware_bitmap_threshold_summary, threshold) + } + } + preference { - key = "pref_display_profile" + bindTo(basePreferences.displayProfile()) titleRes = MR.strings.pref_display_profile onClick { (activity as? MainActivity)?.showColourProfilePicker() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt index 739ce709df..a3a3ac6216 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt @@ -3,52 +3,54 @@ 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 -class GLUtil private constructor() { - companion object { - // Safe minimum default size - private const val IMAGE_MAX_BITMAP_DIMENSION = 2048 +object GLUtil { + val DEVICE_TEXTURE_LIMIT: Int by lazy { + // Get EGL Display + val egl = EGLContext.getEGL() as EGL10 + val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY) - val maxTextureSize: Int - get() { - // Get EGL Display - val egl = EGLContext.getEGL() as EGL10 - val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY) + // Initialise + val version = IntArray(2) + egl.eglInitialize(display, version) - // Initialise - val version = IntArray(2) - egl.eglInitialize(display, version) + // Query total number of configurations + val totalConfigurations = IntArray(1) + egl.eglGetConfigs(display, null, 0, totalConfigurations) - // Query total number of configurations - val totalConfigurations = IntArray(1) - egl.eglGetConfigs(display, null, 0, totalConfigurations) + // Query actual list configurations + val configurationsList = arrayOfNulls(totalConfigurations[0]) + egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations) - // Query actual list configurations - val configurationsList = arrayOfNulls(totalConfigurations[0]) - egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations) + val textureSize = IntArray(1) + var maximumTextureSize = 0 - val textureSize = IntArray(1) - var maximumTextureSize = 0 + // Iterate through all the configurations to located the maximum texture size + for (i in 0 until totalConfigurations[0]) { + // Only need to check for width since opengl textures are always squared + egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize) - // Iterate through all the configurations to located the maximum texture size - for (i in 0 until totalConfigurations[0]) { - // Only need to check for width since opengl textures are always squared - egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize) + // Keep track of the maximum texture size + if (maximumTextureSize < textureSize[0]) maximumTextureSize = textureSize[0] + } - // Keep track of the maximum texture size - if (maximumTextureSize < textureSize[0]) maximumTextureSize = textureSize[0] - } + // Release + egl.eglTerminate(display) - // Release - egl.eglTerminate(display) - - // Return largest texture size found, or default - return 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 + } } - init { - throw InstantiationException("This class is not for instantiation") + const val SAFE_TEXTURE_LIMIT: Int = 2048 + + val CUSTOM_TEXTURE_LIMIT_OPTIONS: List by lazy { + val steps = ((DEVICE_TEXTURE_LIMIT / MULTIPLIER) - 1) + List(steps) { (it + 2) * MULTIPLIER }.asReversed() } } + +private const val MULTIPLIER: Int = 1024 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 46cf1254c0..823af81bef 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 @@ -785,9 +785,11 @@ object ImageUtil { fun isMaxTextureSizeExceeded(bitmap: Bitmap): Boolean = isMaxTextureSizeExceeded(bitmap.width, bitmap.height) + var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT + private fun isMaxTextureSizeExceeded(width: Int, height: Int): Boolean { if (minOf(width, height) <= 0) return false - return maxOf(width, height) > GLUtil.maxTextureSize + return maxOf(width, height) > hardwareBitmapThreshold } } diff --git a/app/src/main/java/yokai/domain/base/BasePreferences.kt b/app/src/main/java/yokai/domain/base/BasePreferences.kt index 4f07b1e220..5e450ee513 100644 --- a/app/src/main/java/yokai/domain/base/BasePreferences.kt +++ b/app/src/main/java/yokai/domain/base/BasePreferences.kt @@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.core.preference.Preference import eu.kanade.tachiyomi.core.preference.PreferenceStore import eu.kanade.tachiyomi.core.preference.getEnum import eu.kanade.tachiyomi.extension.util.ExtensionInstaller +import eu.kanade.tachiyomi.util.system.GLUtil import yokai.i18n.MR class BasePreferences(private val preferenceStore: PreferenceStore) { @@ -46,4 +47,6 @@ class BasePreferences(private val preferenceStore: PreferenceStore) { DEFAULT(MR.strings.recents_long_tap_default), LAST_READ(MR.strings.recents_long_tap_last_read) } + + fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT) } diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 187adaa935..7c7077a2ec 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -513,6 +513,8 @@ Low Lowest Custom display profile + Custom hardware bitmap threshold + If reader loads a blank image incrementally reduce the threshold.\nSelected: %s Double tap to zoom