diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index dad6859c1..d9b357495 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -150,10 +150,12 @@ object SettingsReaderScreen : SearchableSettings { val navModePref = readerPreferences.navigationModePager() val imageScaleTypePref = readerPreferences.imageScaleType() val dualPageSplitPref = readerPreferences.dualPageSplitPaged() + val rotateToFitPref = readerPreferences.dualPageRotateToFit() val navMode by navModePref.collectAsState() val imageScaleType by imageScaleTypePref.collectAsState() val dualPageSplit by dualPageSplitPref.collectAsState() + val rotateToFit by rotateToFitPref.collectAsState() return Preference.PreferenceGroup( title = stringResource(R.string.pager_viewer), @@ -216,6 +218,10 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.SwitchPreference( pref = dualPageSplitPref, title = stringResource(R.string.pref_dual_page_split), + onValueChanged = { + rotateToFitPref.set(false) + true + }, ), Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.dualPageInvertPaged(), @@ -223,6 +229,19 @@ object SettingsReaderScreen : SearchableSettings { subtitle = stringResource(R.string.pref_dual_page_invert_summary), enabled = dualPageSplit, ), + Preference.PreferenceItem.SwitchPreference( + pref = rotateToFitPref, + title = stringResource(R.string.pref_page_rotate), + onValueChanged = { + dualPageSplitPref.set(false) + true + }, + ), + Preference.PreferenceItem.SwitchPreference( + pref = readerPreferences.dualPageRotateToFitInvert(), + title = stringResource(R.string.pref_page_rotate_invert), + enabled = rotateToFit, + ), ), ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index 75636f0bc..6527d8de6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -73,6 +73,10 @@ class ReaderPreferences( fun dualPageInvertWebtoon() = preferenceStore.getBoolean("pref_dual_page_invert_webtoon", false) + fun dualPageRotateToFit() = preferenceStore.getBoolean("pref_dual_page_rotate", false) + + fun dualPageRotateToFitInvert() = preferenceStore.getBoolean("pref_dual_page_rotate_invert", false) + // endregion // region Color filter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt index b5e315b9e..da9eb941f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt @@ -92,11 +92,26 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders()) binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged()) - // Makes it so that dual page invert gets hidden away when dual page split is turned off readerPreferences.dualPageSplitPaged() - .asHotFlow { binding.pagerPrefsGroup.dualPageInvert.isVisible = it } + .asHotFlow { + binding.pagerPrefsGroup.dualPageInvert.isVisible = it + if (it) { + binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false + } + } .launchIn((context as ReaderActivity).lifecycleScope) binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged()) + + binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit()) + readerPreferences.dualPageRotateToFit() + .asHotFlow { + binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it + if (it) { + binding.pagerPrefsGroup.dualPageSplit.isChecked = false + } + } + .launchIn((context as ReaderActivity).lifecycleScope) + binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert()) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt index b852911cd..44dd19db4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt @@ -37,6 +37,12 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc var dualPageInvert = false protected set + var dualPageRotateToFit = false + protected set + + var dualPageRotateToFitInvert = false + protected set + abstract var navigator: ViewerNavigation protected set diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt index c31627425..b319fd928 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt @@ -94,6 +94,18 @@ class PagerConfig( readerPreferences.dualPageInvertPaged() .register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() }) + + readerPreferences.dualPageRotateToFit() + .register( + { dualPageRotateToFit = it }, + { imagePropertyChangedListener?.invoke() }, + ) + + readerPreferences.dualPageRotateToFitInvert() + .register( + { dualPageRotateToFitInvert = it }, + { imagePropertyChangedListener?.invoke() }, + ) } private fun zoomTypeFromPreference(value: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt index 801e03c4b..76a920c22 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt @@ -180,6 +180,10 @@ class PagerPageHolder( } private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream { + if (viewer.config.dualPageRotateToFit) { + return rotateDualPage(imageStream) + } + if (!viewer.config.dualPageSplit) { return imageStream } @@ -198,6 +202,16 @@ class PagerPageHolder( return splitInHalf(imageStream) } + private fun rotateDualPage(imageStream: BufferedInputStream): InputStream { + val isDoublePage = ImageUtil.isWideImage(imageStream) + return if (isDoublePage) { + val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f + ImageUtil.rotateImage(imageStream, rotation) + } else { + imageStream + } + } + private fun splitInHalf(imageStream: InputStream): InputStream { var side = when { viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT diff --git a/app/src/main/res/layout/reader_pager_settings.xml b/app/src/main/res/layout/reader_pager_settings.xml index 3da1b46be..a18091889 100644 --- a/app/src/main/res/layout/reader_pager_settings.xml +++ b/app/src/main/res/layout/reader_pager_settings.xml @@ -91,10 +91,30 @@ android:visibility="gone" tools:visibility="visible" /> + + + + + app:constraint_referenced_ids="pager_nav,tapping_inverted,dual_page_split,dual_page_invert,dual_page_rotate_to_fit,dual_page_rotate_to_fit_invert" /> diff --git a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt index 638f33726..5f29c7d85 100644 --- a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt +++ b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt @@ -7,6 +7,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.BitmapRegionDecoder import android.graphics.Color +import android.graphics.Matrix import android.graphics.Rect import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable @@ -151,6 +152,23 @@ object ImageUtil { return ByteArrayInputStream(output.toByteArray()) } + fun rotateImage(imageStream: InputStream, degrees: Float): InputStream { + val imageBytes = imageStream.readBytes() + + val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + val rotated = rotateBitMap(imageBitmap, degrees) + + val output = ByteArrayOutputStream() + rotated.compress(Bitmap.CompressFormat.JPEG, 100, output) + + return ByteArrayInputStream(output.toByteArray()) + } + + private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap { + val matrix = Matrix().apply { postRotate(degrees) } + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) + } + /** * Split the image into left and right parts, then merge them into a new image. */ diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index e37db94c0..67087aff2 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -314,6 +314,8 @@ Split wide pages Invert split page placement If the placement of the split wide pages don\'t match reading direction + Rotate wide pages to fit + Flip orientation of rotated wide pages Split tall images (BETA) Show content in cutout area Animate page transitions