Add high quality WebGPU renderer (#3388)
This commit is contained in:
@@ -33,6 +33,8 @@ class BasePreferences(
|
||||
PRIVATE(MR.strings.ext_installer_private, false),
|
||||
}
|
||||
|
||||
val highQualityRenderer: Preference<Boolean> = preferenceStore.getBoolean("pref_high_quality_renderer_key", false)
|
||||
|
||||
val displayProfile: Preference<String> = preferenceStore.getString("pref_display_profile_key", "")
|
||||
|
||||
val hardwareBitmapThreshold: Preference<Int> = preferenceStore.getInt(
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
@@ -38,21 +39,31 @@ import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.view.updatePadding
|
||||
import ca.mpreg.webgpuviewer.renderer.WebGpuRenderer
|
||||
import ca.mpreg.webgpuviewer.viewer.ImagePage
|
||||
import ca.mpreg.webgpuviewer.viewer.ImageViewer
|
||||
import ca.mpreg.webgpuviewer.viewer.ImageViewerState
|
||||
import coil3.asDrawable
|
||||
import coil3.imageLoader
|
||||
import coil3.request.CachePolicy
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.size.Size
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.DropdownMenu
|
||||
import eu.kanade.presentation.manga.EditCoverAction
|
||||
import eu.kanade.tachiyomi.data.coil.ImageDecoder2
|
||||
import eu.kanade.tachiyomi.data.coil.newDecoder
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.clickableNoIndication
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@Composable
|
||||
fun MangaCoverDialog(
|
||||
@@ -64,6 +75,9 @@ fun MangaCoverDialog(
|
||||
onEditClick: ((EditCoverAction) -> Unit)?,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val useNewRenderer = Injekt.get<BasePreferences>().highQualityRenderer.get()
|
||||
val view = LocalView.current
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
properties = DialogProperties(
|
||||
@@ -148,6 +162,41 @@ fun MangaCoverDialog(
|
||||
}
|
||||
},
|
||||
) { contentPadding ->
|
||||
if (useNewRenderer) {
|
||||
val state = ImageViewerState()
|
||||
|
||||
state.dpi = view.resources.displayMetrics.densityDpi / 100f
|
||||
|
||||
ImageRequest.Builder(view.context)
|
||||
.data(manga)
|
||||
.size(Size.ORIGINAL)
|
||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||
.newDecoder(true)
|
||||
.target { result ->
|
||||
val res = (result as ImageDecoder2.DecodeResultImage).res
|
||||
val page = runBlocking(WebGpuRenderer.dispatcher) {
|
||||
ImagePage(res.image, res.width, res.height)
|
||||
}.apply {
|
||||
image?.backgroundColor = 0
|
||||
}
|
||||
state.apply {
|
||||
fetchPage = { index ->
|
||||
if (index == 0) {
|
||||
page
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
.let(view.context.imageLoader::enqueue)
|
||||
|
||||
ImageViewer(state = state)
|
||||
return@Scaffold
|
||||
}
|
||||
|
||||
val statusBarPaddingPx = with(LocalDensity.current) { contentPadding.calculateTopPadding().roundToPx() }
|
||||
val bottomPaddingPx = with(LocalDensity.current) { contentPadding.calculateBottomPadding().roundToPx() }
|
||||
|
||||
|
||||
+4
@@ -346,6 +346,10 @@ object SettingsAdvancedScreen : SearchableSettings {
|
||||
return Preference.PreferenceGroup(
|
||||
title = stringResource(MR.strings.pref_category_reader),
|
||||
preferenceItems = listOf(
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = basePreferences.highQualityRenderer,
|
||||
title = stringResource(MR.strings.pref_high_quality_renderer),
|
||||
),
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
preference = basePreferences.hardwareBitmapThreshold,
|
||||
entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
|
||||
|
||||
@@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsViewModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.CheckboxItem
|
||||
@@ -22,6 +23,8 @@ import tachiyomi.presentation.core.components.SettingsChipRow
|
||||
import tachiyomi.presentation.core.components.SliderItem
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.NumberFormat
|
||||
|
||||
@Composable
|
||||
@@ -40,6 +43,26 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
||||
}
|
||||
}
|
||||
|
||||
val default = Injekt.get<ReaderPreferences>().defaultReadingMode.get()
|
||||
val resolved = ReadingMode.fromPreference(
|
||||
when {
|
||||
readingMode == ReadingMode.DEFAULT -> default
|
||||
else -> manga?.readingMode?.toInt() ?: default
|
||||
},
|
||||
)
|
||||
if (resolved == ReadingMode.LEFT_TO_RIGHT || resolved == ReadingMode.RIGHT_TO_LEFT) {
|
||||
val dualPageView by viewModel.preferences.dualPageView.collectAsState()
|
||||
SettingsChipRow(MR.strings.pref_dual_page_view) {
|
||||
ReaderPreferences.DualPageView.entries.map {
|
||||
FilterChip(
|
||||
selected = it == dualPageView,
|
||||
onClick = { viewModel.preferences.dualPageView.set(it) },
|
||||
label = { Text(stringResource(it.titleRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val orientation = remember(manga) { ReaderOrientation.fromPreference(manga?.readerOrientation?.toInt()) }
|
||||
SettingsChipRow(MR.strings.rotation_type) {
|
||||
ReaderOrientation.entries.map {
|
||||
@@ -56,6 +79,9 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
||||
WebtoonViewerSettings(viewModel)
|
||||
} else {
|
||||
PagerViewerSettings(viewModel)
|
||||
if (viewer is WebGpuViewer) {
|
||||
WebGpuViewerSettings(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,3 +259,30 @@ private fun ColumnScope.TapZonesItems(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.WebGpuViewerSettings(viewModel: ReaderSettingsViewModel) {
|
||||
HeadingItem(MR.strings.webgpu_viewer)
|
||||
|
||||
val transitionAnimation by viewModel.preferences.transitionAnimation.collectAsState()
|
||||
SettingsChipRow(MR.strings.pref_transition_animation) {
|
||||
ReaderPreferences.TransitionAnimation.entries.map {
|
||||
FilterChip(
|
||||
selected = it == transitionAnimation,
|
||||
onClick = { viewModel.preferences.transitionAnimation.set(it) },
|
||||
label = { Text(stringResource(it.titleRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val cutoutMode by viewModel.preferences.cutoutMode.collectAsState()
|
||||
SettingsChipRow(MR.strings.pref_cutout_mode) {
|
||||
ReaderPreferences.CutoutMode.entries.map {
|
||||
FilterChip(
|
||||
selected = it == cutoutMode,
|
||||
onClick = { viewModel.preferences.cutoutMode.set(it) },
|
||||
label = { Text(stringResource(it.titleRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.data.coil
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import coil3.Canvas
|
||||
import coil3.Image
|
||||
import coil3.ImageLoader
|
||||
import coil3.asImage
|
||||
import coil3.decode.DecodeResult
|
||||
@@ -62,7 +64,9 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
||||
class Factory : Decoder.Factory {
|
||||
|
||||
override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
|
||||
return if (options.customDecoder || isApplicable(result.source.source())) {
|
||||
return if (options.newDecoder) {
|
||||
ImageDecoder2(result.source, options)
|
||||
} else if (options.customDecoder || isApplicable(result.source.source())) {
|
||||
TachiyomiImageDecoder(result.source, options)
|
||||
} else {
|
||||
null
|
||||
@@ -88,3 +92,27 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
||||
var displayProfile: ByteArray? = null
|
||||
}
|
||||
}
|
||||
|
||||
class ImageDecoder2(private val resources: ImageSource, private val options: Options) : Decoder {
|
||||
|
||||
class DecodeResultImage(val res: ca.mpreg.imagedecoder.ImageDecoder.DecodeResult) : Image {
|
||||
override val size: Long get() = res.image.capacity().toLong()
|
||||
override val width: Int get() = res.width
|
||||
override val height: Int get() = res.height
|
||||
override val shareable: Boolean get() = true
|
||||
override fun draw(canvas: Canvas) {}
|
||||
}
|
||||
|
||||
override suspend fun decode(): DecodeResult {
|
||||
val source = resources.source()
|
||||
|
||||
val decoder = ca.mpreg.imagedecoder.ImageDecoder.new(source.inputStream())
|
||||
|
||||
val res = decoder.decode()
|
||||
|
||||
return DecodeResult(
|
||||
image = DecodeResultImage(res),
|
||||
isSampled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,3 +42,11 @@ val Options.customDecoder: Boolean
|
||||
get() = getExtra(customDecoderKey)
|
||||
|
||||
private val customDecoderKey = Extras.Key(default = false)
|
||||
|
||||
fun ImageRequest.Builder.newDecoder(enable: Boolean) = apply {
|
||||
extras[newDecoderKey] = enable
|
||||
}
|
||||
val Options.newDecoder: Boolean
|
||||
get() = getExtra(newDecoderKey)
|
||||
|
||||
private val newDecoderKey = Extras.Key(default = false)
|
||||
|
||||
@@ -78,6 +78,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsViewModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.system.isNightMode
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
@@ -479,7 +480,7 @@ class ReaderActivity : BaseActivity() {
|
||||
onShare = ::shareChapter.takeIf { isHttpSource },
|
||||
|
||||
chapterNavigatorType = if (!verticalNavigator) {
|
||||
if (state.viewer is R2LPagerViewer) {
|
||||
if (state.viewer is R2LPagerViewer || (state.viewer as? WebGpuViewer)?.isReversed ?: false) {
|
||||
ChapterNavigatorType.HORIZONTAL_RTL
|
||||
} else {
|
||||
ChapterNavigatorType.HORIZONTAL_LTR
|
||||
|
||||
@@ -132,6 +132,11 @@ class ReaderPreferences(
|
||||
false,
|
||||
)
|
||||
|
||||
val dualPageView: Preference<DualPageView> = preferenceStore.getEnum(
|
||||
"pref_dual_page_view",
|
||||
DualPageView.NEVER,
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
// region Color filter
|
||||
@@ -189,6 +194,15 @@ class ReaderPreferences(
|
||||
|
||||
// endregion
|
||||
|
||||
// region WebGpu
|
||||
|
||||
val transitionAnimation: Preference<TransitionAnimation> =
|
||||
preferenceStore.getEnum("webgpu_transition_animation", TransitionAnimation.DEFAULT)
|
||||
|
||||
val cutoutMode: Preference<CutoutMode> = preferenceStore.getEnum("webgpu_cutout_mode", CutoutMode.AVOID)
|
||||
|
||||
// endregion
|
||||
|
||||
enum class FlashColor {
|
||||
BLACK,
|
||||
WHITE,
|
||||
@@ -201,8 +215,14 @@ class ReaderPreferences(
|
||||
val shouldInvertVertical: Boolean = false,
|
||||
) {
|
||||
NONE(MR.strings.tapping_inverted_none),
|
||||
HORIZONTAL(MR.strings.tapping_inverted_horizontal, shouldInvertHorizontal = true),
|
||||
VERTICAL(MR.strings.tapping_inverted_vertical, shouldInvertVertical = true),
|
||||
HORIZONTAL(
|
||||
MR.strings.tapping_inverted_horizontal,
|
||||
shouldInvertHorizontal = true,
|
||||
),
|
||||
VERTICAL(
|
||||
MR.strings.tapping_inverted_vertical,
|
||||
shouldInvertVertical = true,
|
||||
),
|
||||
BOTH(MR.strings.tapping_inverted_both, shouldInvertHorizontal = true, shouldInvertVertical = true),
|
||||
}
|
||||
|
||||
@@ -213,6 +233,41 @@ class ReaderPreferences(
|
||||
LOWEST(47),
|
||||
}
|
||||
|
||||
enum class TransitionAnimation(val titleRes: StringResource) {
|
||||
DEFAULT(MR.strings.transition_animation_default),
|
||||
FLIP_LEFT(MR.strings.transition_animation_flip_left),
|
||||
FLIP_RIGHT(
|
||||
MR.strings.transition_animation_flip_right,
|
||||
),
|
||||
STACK_LEFT(MR.strings.transition_animation_stack_left),
|
||||
STACK_RIGHT(MR.strings.transition_animation_stack_right),
|
||||
STACK_UP(
|
||||
MR.strings.transition_animation_stack_up,
|
||||
),
|
||||
STACK_DOWN(MR.strings.transition_animation_stack_down),
|
||||
SPHERE(MR.strings.transition_animation_sphere),
|
||||
CUBE_INSIDE(
|
||||
MR.strings.transition_animation_cube_inside,
|
||||
),
|
||||
CUBE_OUTSIDE(MR.strings.transition_animation_cube_outside),
|
||||
FADE(MR.strings.transition_animation_fade),
|
||||
FADE_WHITE(
|
||||
MR.strings.transition_animation_fade_white,
|
||||
),
|
||||
}
|
||||
|
||||
enum class CutoutMode(val titleRes: StringResource) {
|
||||
IGNORE(MR.strings.cutout_mode_ignore),
|
||||
AVOID(MR.strings.cutout_mode_avoid),
|
||||
SHIFT(MR.strings.cutout_mode_shift),
|
||||
}
|
||||
|
||||
enum class DualPageView(val titleRes: StringResource) {
|
||||
NEVER(MR.strings.dual_page_view_never),
|
||||
ALWAYS(MR.strings.dual_page_view_always),
|
||||
WIDE(MR.strings.dual_page_view_wide),
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WEBTOON_PADDING_MIN = 0
|
||||
const val WEBTOON_PADDING_MAX = 25
|
||||
|
||||
@@ -2,14 +2,19 @@ package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.L2RPagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webgpu.WebGpuViewerContinuous
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
enum class ReadingMode(
|
||||
val stringRes: StringResource,
|
||||
@@ -67,6 +72,16 @@ enum class ReadingMode(
|
||||
}
|
||||
|
||||
fun toViewer(preference: Int?, activity: ReaderActivity): Viewer {
|
||||
if (Injekt.get<BasePreferences>().highQualityRenderer.get()) {
|
||||
return when (fromPreference(preference)) {
|
||||
LEFT_TO_RIGHT -> WebGpuViewer(activity, isReversed = false, isVertical = false)
|
||||
RIGHT_TO_LEFT -> WebGpuViewer(activity, isReversed = true, isVertical = false)
|
||||
VERTICAL -> WebGpuViewer(activity, isReversed = false, isVertical = true)
|
||||
WEBTOON -> WebGpuViewerContinuous(activity)
|
||||
CONTINUOUS_VERTICAL -> WebGpuViewerContinuous(activity)
|
||||
DEFAULT -> throw IllegalStateException("Preference value must be resolved: $preference")
|
||||
}
|
||||
}
|
||||
return when (fromPreference(preference)) {
|
||||
LEFT_TO_RIGHT -> L2RPagerViewer(activity)
|
||||
RIGHT_TO_LEFT -> R2LPagerViewer(activity)
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.webgpu
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerConfig
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.DisabledNavigation
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
/**
|
||||
* Configuration used by pager viewers.
|
||||
*/
|
||||
class WebGpuConfig(
|
||||
private val viewer: WebGpuViewer,
|
||||
scope: CoroutineScope,
|
||||
readerPreferences: ReaderPreferences = Injekt.get(),
|
||||
) : ViewerConfig(readerPreferences, scope) {
|
||||
|
||||
var theme = readerPreferences.readerTheme.get()
|
||||
private set
|
||||
|
||||
var automaticBackground = false
|
||||
private set
|
||||
|
||||
var dualPageSplitChangedListener: ((Boolean) -> Unit)? = null
|
||||
|
||||
var imageScaleType = 1
|
||||
private set
|
||||
|
||||
var imageZoomType = ReaderPageImageView.ZoomStartPosition.LEFT
|
||||
private set
|
||||
|
||||
var imageCropBorders = false
|
||||
private set
|
||||
|
||||
var navigateToPan = false
|
||||
private set
|
||||
|
||||
var landscapeZoom = false
|
||||
private set
|
||||
|
||||
var transitionAnimation = ReaderPreferences.TransitionAnimation.DEFAULT
|
||||
private set
|
||||
|
||||
var cutoutMode = ReaderPreferences.CutoutMode.AVOID
|
||||
private set
|
||||
|
||||
var dualPageView = ReaderPreferences.DualPageView.NEVER
|
||||
private set
|
||||
|
||||
init {
|
||||
readerPreferences.readerTheme
|
||||
.register(
|
||||
{
|
||||
theme = it
|
||||
automaticBackground = it == 3
|
||||
},
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
|
||||
readerPreferences.imageScaleType
|
||||
.register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })
|
||||
|
||||
readerPreferences.zoomStart
|
||||
.register({ zoomTypeFromPreference(it) }, { imagePropertyChangedListener?.invoke() })
|
||||
|
||||
readerPreferences.cropBorders
|
||||
.register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() })
|
||||
|
||||
readerPreferences.navigateToPan
|
||||
.register({ navigateToPan = it })
|
||||
|
||||
readerPreferences.landscapeZoom
|
||||
.register({ landscapeZoom = it }, { imagePropertyChangedListener?.invoke() })
|
||||
|
||||
readerPreferences.navigationModePager
|
||||
.register({ navigationMode = it }, { updateNavigation(navigationMode) })
|
||||
|
||||
readerPreferences.pagerNavInverted
|
||||
.register({ tappingInverted = it }, { navigator.invertMode = it })
|
||||
readerPreferences.pagerNavInverted.changes()
|
||||
.drop(1)
|
||||
.onEach { navigationModeChangedListener?.invoke() }
|
||||
.launchIn(scope)
|
||||
|
||||
readerPreferences.dualPageSplitPaged
|
||||
.register(
|
||||
{ dualPageSplit = it },
|
||||
{
|
||||
imagePropertyChangedListener?.invoke()
|
||||
dualPageSplitChangedListener?.invoke(it)
|
||||
},
|
||||
)
|
||||
|
||||
readerPreferences.dualPageInvertPaged
|
||||
.register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
|
||||
|
||||
readerPreferences.dualPageRotateToFit
|
||||
.register(
|
||||
{ dualPageRotateToFit = it },
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
|
||||
readerPreferences.dualPageRotateToFitInvert
|
||||
.register(
|
||||
{ dualPageRotateToFitInvert = it },
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
|
||||
readerPreferences.transitionAnimation
|
||||
.register(
|
||||
{ transitionAnimation = it },
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
|
||||
readerPreferences.cutoutMode
|
||||
.register(
|
||||
{ cutoutMode = it },
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
|
||||
readerPreferences.dualPageView
|
||||
.register(
|
||||
{ dualPageView = it },
|
||||
{ imagePropertyChangedListener?.invoke() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun zoomTypeFromPreference(value: Int) {
|
||||
imageZoomType = when (value) {
|
||||
// Auto
|
||||
1 -> if (viewer.isReversed) {
|
||||
ReaderPageImageView.ZoomStartPosition.RIGHT
|
||||
} else {
|
||||
ReaderPageImageView.ZoomStartPosition.LEFT
|
||||
}
|
||||
// Left
|
||||
2 -> ReaderPageImageView.ZoomStartPosition.LEFT
|
||||
// Right
|
||||
3 -> ReaderPageImageView.ZoomStartPosition.RIGHT
|
||||
// Center
|
||||
else -> ReaderPageImageView.ZoomStartPosition.CENTER
|
||||
}
|
||||
}
|
||||
|
||||
override var navigator: ViewerNavigation = defaultNavigation()
|
||||
set(value) {
|
||||
field = value.also { it.invertMode = this.tappingInverted }
|
||||
}
|
||||
|
||||
override fun defaultNavigation(): ViewerNavigation {
|
||||
return if (viewer.isVertical) {
|
||||
LNavigation()
|
||||
} else {
|
||||
RightAndLeftNavigation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateNavigation(navigationMode: Int) {
|
||||
navigator = when (navigationMode) {
|
||||
0 -> defaultNavigation()
|
||||
1 -> LNavigation()
|
||||
2 -> KindlishNavigation()
|
||||
3 -> EdgeNavigation()
|
||||
4 -> RightAndLeftNavigation()
|
||||
5 -> DisabledNavigation()
|
||||
else -> defaultNavigation()
|
||||
}
|
||||
navigationModeChangedListener?.invoke()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+26
@@ -0,0 +1,26 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.webgpu
|
||||
|
||||
import ca.mpreg.webgpuviewer.ImageViewContinuous
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
|
||||
class WebGpuViewerContinuous(activity: ReaderActivity) :
|
||||
WebGpuViewer(activity, isReversed = false, isVertical = true, pager = ImageViewContinuous(activity)) {
|
||||
|
||||
override val isContinuous: Boolean = true
|
||||
|
||||
private fun scrollByHalfPage(direction: Int) {
|
||||
val state = (pager as ImageViewContinuous).state
|
||||
val totalDistance = direction * state.height / 2f
|
||||
state.animateScroll(totalDistance)
|
||||
}
|
||||
|
||||
override fun moveRight() = scrollByHalfPage(1)
|
||||
|
||||
override fun moveLeft() = scrollByHalfPage(-1)
|
||||
|
||||
override fun moveToPage(page: ReaderPage) {
|
||||
super.moveToPage(page)
|
||||
(pager as ImageViewContinuous).state.scrollY = 0f
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user