Compare commits

..

No commits in common. "88aff2c77fbaed52ab101ce75c2cbe72f1747579" and "36d5ee0763be2b0bcc65f9d061961d86359fe6f6" have entirely different histories.

19 changed files with 66 additions and 121 deletions

View File

@ -18,6 +18,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
- Fixed "currentTab was used multiple times" - Fixed "currentTab was used multiple times"
- Fixed a rare crash when invoking "Mark previous as read" action - Fixed a rare crash when invoking "Mark previous as read" action
- Fixed long strip images not loading in some old devices - Fixed long strip images not loading in some old devices
- Added option to always use SSIV for image decoding
### Improved ### Improved
- Bangumi search now shows the score and summary of a search result ([@MajorTanya](https://github.com/MajorTanya)) ([#1396](https://github.com/mihonapp/mihon/pull/1396)) - Bangumi search now shows the score and summary of a search result ([@MajorTanya](https://github.com/MajorTanya)) ([#1396](https://github.com/mihonapp/mihon/pull/1396))

View File

@ -2,7 +2,6 @@ package eu.kanade.domain.base
import android.content.Context import android.content.Context
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.util.system.GLUtil
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@ -32,5 +31,5 @@ class BasePreferences(
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "") fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT) fun alwaysUseSSIVToDecode() = preferenceStore.getBoolean("pref_always_use_ssiv_to_decode", false)
} }

View File

@ -162,12 +162,12 @@ sealed class Preference {
data class CustomPreference( data class CustomPreference(
override val title: String, override val title: String,
val content: @Composable () -> Unit, val content: @Composable (PreferenceItem<String>) -> Unit,
) : PreferenceItem<Unit>() { ) : PreferenceItem<String>() {
override val enabled: Boolean = true override val enabled: Boolean = true
override val subtitle: String? = null override val subtitle: String? = null
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (newValue: Unit) -> Boolean = { true } override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
} }
} }

View File

@ -167,7 +167,7 @@ internal fun PreferenceItem(
InfoWidget(text = item.title) InfoWidget(text = item.title)
} }
is Preference.PreferenceItem.CustomPreference -> { is Preference.PreferenceItem.CustomPreference -> {
item.content() item.content(item)
} }
} }
} }

View File

@ -47,7 +47,6 @@ import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN import eu.kanade.tachiyomi.network.PREF_DOH_SHECAN
import eu.kanade.tachiyomi.ui.more.OnboardingScreen import eu.kanade.tachiyomi.ui.more.OnboardingScreen
import eu.kanade.tachiyomi.util.CrashLogUtil import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.system.GLUtil
import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isShizukuInstalled import eu.kanade.tachiyomi.util.system.isShizukuInstalled
@ -332,23 +331,9 @@ object SettingsAdvancedScreen : SearchableSettings {
basePreferences.displayProfile().set(uri.toString()) basePreferences.displayProfile().set(uri.toString())
} }
} }
val hardwareBitmapThresholdPref = basePreferences.hardwareBitmapThreshold()
val hardwareBitmapThreshold by hardwareBitmapThresholdPref.collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_reader), title = stringResource(MR.strings.pref_category_reader),
preferenceItems = persistentListOf( 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( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_display_profile), title = stringResource(MR.strings.pref_display_profile),
subtitle = basePreferences.displayProfile().get(), subtitle = basePreferences.displayProfile().get(),
@ -356,6 +341,10 @@ object SettingsAdvancedScreen : SearchableSettings {
chooseColorProfile.launch(arrayOf("*/*")) chooseColorProfile.launch(arrayOf("*/*"))
}, },
), ),
Preference.PreferenceItem.SwitchPreference(
pref = basePreferences.alwaysUseSSIVToDecode(),
title = stringResource(MR.strings.pref_always_use_ssiv_to_decode),
),
), ),
) )
} }

View File

@ -41,7 +41,6 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.DeviceUtil 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.WebViewUtil
import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.system.animatorDurationScale
import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.cancelNotification
@ -59,7 +58,6 @@ import org.conscrypt.Conscrypt
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.widget.WidgetManager import tachiyomi.presentation.widget.WidgetManager
@ -144,14 +142,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
.onEach(FirebaseConfig::setCrashlyticsEnabled) .onEach(FirebaseConfig::setCrashlyticsEnabled)
.launchIn(scope) .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()) setAppCompatDelegateThemeMode(Injekt.get<UiPreferences>().themeMode().get())
// Updates widget update // Updates widget update

View File

@ -10,6 +10,7 @@ import coil3.decode.ImageSource
import coil3.fetch.SourceFetchResult import coil3.fetch.SourceFetchResult
import coil3.request.Options import coil3.request.Options
import coil3.request.bitmapConfig import coil3.request.bitmapConfig
import eu.kanade.tachiyomi.util.system.GLUtil
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.decoder.ImageDecoder import tachiyomi.decoder.ImageDecoder
@ -45,7 +46,10 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
check(bitmap != null) { "Failed to decode image" } check(bitmap != null) { "Failed to decode image" }
if (options.bitmapConfig == Bitmap.Config.HARDWARE && ImageUtil.canUseHardwareBitmap(bitmap)) { if (
options.bitmapConfig == Bitmap.Config.HARDWARE &&
maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
) {
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false) val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
if (hwBitmap != null) { if (hwBitmap != null) {
bitmap.recycle() bitmap.recycle()

View File

@ -71,7 +71,6 @@ object Notifications {
const val CHANNEL_APP_UPDATE = "app_apk_update_channel" const val CHANNEL_APP_UPDATE = "app_apk_update_channel"
const val ID_APP_UPDATER = 1 const val ID_APP_UPDATER = 1
const val ID_APP_UPDATE_PROMPT = 2 const val ID_APP_UPDATE_PROMPT = 2
const val ID_APP_UPDATE_ERROR = 3
const val CHANNEL_EXTENSIONS_UPDATE = "ext_apk_update_channel" const val CHANNEL_EXTENSIONS_UPDATE = "ext_apk_update_channel"
const val ID_UPDATES_TO_EXTS = -401 const val ID_UPDATES_TO_EXTS = -401
const val ID_EXTENSION_INSTALLER = -402 const val ID_EXTENSION_INSTALLER = -402

View File

@ -181,9 +181,9 @@ internal class AppUpdateNotifier(private val context: Context) {
addAction( addAction(
R.drawable.ic_close_24dp, R.drawable.ic_close_24dp,
context.stringResource(MR.strings.action_cancel), context.stringResource(MR.strings.action_cancel),
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATE_ERROR), NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_APP_UPDATER),
) )
} }
notificationBuilder.show(Notifications.ID_APP_UPDATE_ERROR) notificationBuilder.show(Notifications.ID_APP_UPDATER)
} }
} }

View File

@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
@ -28,7 +29,6 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.core.common.Constants import tachiyomi.core.common.Constants
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@ -86,7 +86,7 @@ data class SourceSearchScreen(
} }
BrowseSourceContent( BrowseSourceContent(
source = screenModel.source, source = screenModel.source,
mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(), mangaList = screenModel.mangaPagerFlow.collectAsLazyPagingItems(),
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
displayMode = screenModel.displayMode, displayMode = screenModel.displayMode,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,

View File

@ -31,6 +31,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
@ -55,7 +56,6 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.core.common.Constants import tachiyomi.core.common.Constants
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.source.model.StubSource import tachiyomi.domain.source.model.StubSource
@ -208,7 +208,7 @@ data class BrowseSourceScreen(
) { paddingValues -> ) { paddingValues ->
BrowseSourceContent( BrowseSourceContent(
source = screenModel.source, source = screenModel.source,
mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(), mangaList = screenModel.mangaPagerFlow.collectAsLazyPagingItems(),
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation), columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
displayMode = screenModel.displayMode, displayMode = screenModel.displayMode,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,

View File

@ -26,11 +26,10 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@ -106,9 +105,9 @@ class BrowseSourceScreenModel(
* Flow of Pager flow tied to [State.listing] * Flow of Pager flow tied to [State.listing]
*/ */
private val hideInLibraryItems = sourcePreferences.hideInLibraryItems().get() private val hideInLibraryItems = sourcePreferences.hideInLibraryItems().get()
val mangaPagerFlowFlow = state.map { it.listing } val mangaPagerFlow = state.map { it.listing }
.distinctUntilChanged() .distinctUntilChanged()
.map { listing -> .flatMapLatest { listing ->
Pager(PagingConfig(pageSize = 25)) { Pager(PagingConfig(pageSize = 25)) {
getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters) getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters)
}.flow.map { pagingData -> }.flow.map { pagingData ->
@ -120,9 +119,8 @@ class BrowseSourceScreenModel(
} }
.filter { !hideInLibraryItems || !it.value.favorite } .filter { !hideInLibraryItems || !it.value.favorite }
} }
.cachedIn(ioCoroutineScope)
} }
.stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow()) .cachedIn(ioCoroutineScope)
fun getColumnsPreference(orientation: Int): GridCells { fun getColumnsPreference(orientation: Int): GridCells {
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE

View File

@ -33,13 +33,17 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
import com.github.chrisbanes.photoview.PhotoView import com.github.chrisbanes.photoview.PhotoView
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.coil.cropBorders import eu.kanade.tachiyomi.data.coil.cropBorders
import eu.kanade.tachiyomi.data.coil.customDecoder import eu.kanade.tachiyomi.data.coil.customDecoder
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView 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.system.animatorDurationScale
import eu.kanade.tachiyomi.util.view.isVisibleOnScreen import eu.kanade.tachiyomi.util.view.isVisibleOnScreen
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* A wrapper view for showing page image. * A wrapper view for showing page image.
@ -57,6 +61,8 @@ open class ReaderPageImageView @JvmOverloads constructor(
private val isWebtoon: Boolean = false, private val isWebtoon: Boolean = false,
) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) { ) : FrameLayout(context, attrs, defStyleAttrs, defStyleRes) {
private val alwaysUseSSIVToDecode by lazy { Injekt.get<BasePreferences>().alwaysUseSSIVToDecode().get() }
private var pageView: View? = null private var pageView: View? = null
private var config: Config? = null private var config: Config? = null
@ -233,7 +239,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
} else { } else {
SubsamplingScaleImageView(context) SubsamplingScaleImageView(context)
}.apply { }.apply {
setMaxTileSize(ImageUtil.hardwareBitmapThreshold) setMaxTileSize(GLUtil.maxTextureSize)
setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER) setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE) setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE)
setMinimumTileDpi(180) setMinimumTileDpi(180)
@ -294,34 +300,32 @@ open class ReaderPageImageView @JvmOverloads constructor(
isVisible = true isVisible = true
} }
is BufferedSource -> { is BufferedSource -> {
if (!isWebtoon) { if (alwaysUseSSIVToDecode || !isWebtoon || !ImageUtil.canUseCoilToDecode(data)) {
setHardwareConfig(ImageUtil.canUseHardwareBitmap(data))
setImage(ImageSource.inputStream(data.inputStream())) setImage(ImageSource.inputStream(data.inputStream()))
isVisible = true isVisible = true
return@apply } else {
val request = ImageRequest.Builder(context)
.data(data)
.memoryCachePolicy(CachePolicy.DISABLED)
.diskCachePolicy(CachePolicy.DISABLED)
.target(
onSuccess = { result ->
val image = result as BitmapImage
setImage(ImageSource.bitmap(image.bitmap))
isVisible = true
},
onError = {
this@ReaderPageImageView.onImageLoadError()
},
)
.size(ViewSizeResolver(this@ReaderPageImageView))
.precision(Precision.INEXACT)
.cropBorders(config.cropBorders)
.customDecoder(true)
.crossfade(false)
.build()
context.imageLoader.enqueue(request)
} }
ImageRequest.Builder(context)
.data(data)
.memoryCachePolicy(CachePolicy.DISABLED)
.diskCachePolicy(CachePolicy.DISABLED)
.target(
onSuccess = { result ->
val image = result as BitmapImage
setImage(ImageSource.bitmap(image.bitmap))
isVisible = true
},
onError = {
onImageLoadError()
},
)
.size(ViewSizeResolver(this@ReaderPageImageView))
.precision(Precision.INEXACT)
.cropBorders(config.cropBorders)
.customDecoder(true)
.crossfade(false)
.build()
.let(context.imageLoader::enqueue)
} }
else -> { else -> {
throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}")

View File

@ -3,9 +3,10 @@ package eu.kanade.tachiyomi.util.system
import javax.microedition.khronos.egl.EGL10 import javax.microedition.khronos.egl.EGL10
import javax.microedition.khronos.egl.EGLConfig import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.egl.EGLContext import javax.microedition.khronos.egl.EGLContext
import kotlin.math.max
object GLUtil { object GLUtil {
val DEVICE_TEXTURE_LIMIT: Int by lazy { val maxTextureSize: Int by lazy {
// Get EGL Display // Get EGL Display
val egl = EGLContext.getEGL() as EGL10 val egl = EGLContext.getEGL() as EGL10
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY) val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
@ -37,20 +38,10 @@ object GLUtil {
// Release // Release
egl.eglTerminate(display) egl.eglTerminate(display)
// Return largest texture size found (after making it a multiplier of [Multiplier]), or default // Return largest texture size found, or default
if (maximumTextureSize > SAFE_TEXTURE_LIMIT) { max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION)
(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()
} }
} }
private const val MULTIPLIER: Int = 1024 // Safe minimum default size
private const val IMAGE_MAX_BITMAP_DIMENSION = 2048

View File

@ -310,20 +310,9 @@ object ImageUtil {
val bottomOffset = topOffset + splitHeight val bottomOffset = topOffset + splitHeight
} }
fun canUseHardwareBitmap(bitmap: Bitmap): Boolean { fun canUseCoilToDecode(imageSource: BufferedSource): Boolean {
return canUseHardwareBitmap(bitmap.width, bitmap.height) val options = extractImageOptions(imageSource)
} return maxOf(options.outWidth, options.outHeight) <= GLUtil.maxTextureSize
fun canUseHardwareBitmap(imageSource: BufferedSource): Boolean {
return with(extractImageOptions(imageSource)) {
canUseHardwareBitmap(outWidth, outHeight)
}
}
var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT
private fun canUseHardwareBitmap(width: Int, height: Int): Boolean {
return maxOf(width, height) <= hardwareBitmapThreshold
} }
/** /**

View File

@ -48,7 +48,7 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:66e0db195d" subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b"
image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540" image-decoder = "com.github.tachiyomiorg:image-decoder:41c059e540"
natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"

View File

@ -392,8 +392,7 @@
<string name="pref_show_reading_mode">Show reading mode</string> <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_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_display_profile">Custom display profile</string>
<string name="pref_hardware_bitmap_threshold">Custom hardware bitmap threshold</string> <string name="pref_always_use_ssiv_to_decode">Always use SSIV to decode long strip images</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_crop_borders">Crop borders</string>
<string name="pref_custom_brightness">Custom brightness</string> <string name="pref_custom_brightness">Custom brightness</string>
<string name="pref_grayscale">Grayscale</string> <string name="pref_grayscale">Grayscale</string>

View File

@ -42,7 +42,5 @@ dependencies {
implementation(compose.ui.tooling.preview) implementation(compose.ui.tooling.preview)
implementation(compose.ui.util) implementation(compose.ui.util)
implementation(androidx.paging.runtime)
implementation(androidx.paging.compose)
implementation(kotlinx.immutables) implementation(kotlinx.immutables)
} }

View File

@ -1,16 +0,0 @@
package mihon.presentation.core.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@Composable
fun <T: Any> StateFlow<Flow<PagingData<T>>>.collectAsLazyPagingItems(): LazyPagingItems<T> {
val flow by collectAsState()
return flow.collectAsLazyPagingItems()
}