feat: Add option to lower the threshold for hardware bitmaps

This commit is contained in:
AntsyLich 2024-11-20 23:01:54 +07:00 committed by Ahmad Ansori Palembani
parent 27002a20ef
commit fd73958923
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
7 changed files with 85 additions and 48 deletions

View file

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

View file

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

View file

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

View file

@ -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<EGLConfig>(totalConfigurations[0])
egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations)
// Query actual list configurations
val configurationsList = arrayOfNulls<EGLConfig>(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<Int> by lazy {
val steps = ((DEVICE_TEXTURE_LIMIT / MULTIPLIER) - 1)
List(steps) { (it + 2) * MULTIPLIER }.asReversed()
}
}
private const val MULTIPLIER: Int = 1024

View file

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

View file

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

View file

@ -513,6 +513,8 @@
<string name="pref_low">Low</string>
<string name="pref_lowest">Lowest</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_double_tap_zoom">Double tap to zoom</string>
<!-- Manga details -->