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