Add "Rotate wide pages to fit" setting for paged reader

Originally authored in #7983

Co-authored-by: timothyng-164 <timothyng-164@users.noreply.github.com>
(cherry picked from commit 953720472fe64ef488ecae7ae7fea453b8c7c68b)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
#	app/src/main/res/layout/reader_pager_settings.xml
This commit is contained in:
arkon 2023-03-24 22:48:59 -04:00 committed by Jobobby04
parent be17682e3c
commit 880b06dd3b
9 changed files with 113 additions and 3 deletions

View File

@ -183,10 +183,12 @@ object SettingsReaderScreen : SearchableSettings {
val navModePref = readerPreferences.navigationModePager() val navModePref = readerPreferences.navigationModePager()
val imageScaleTypePref = readerPreferences.imageScaleType() val imageScaleTypePref = readerPreferences.imageScaleType()
val dualPageSplitPref = readerPreferences.dualPageSplitPaged() val dualPageSplitPref = readerPreferences.dualPageSplitPaged()
val rotateToFitPref = readerPreferences.dualPageRotateToFit()
val navMode by navModePref.collectAsState() val navMode by navModePref.collectAsState()
val imageScaleType by imageScaleTypePref.collectAsState() val imageScaleType by imageScaleTypePref.collectAsState()
val dualPageSplit by dualPageSplitPref.collectAsState() val dualPageSplit by dualPageSplitPref.collectAsState()
val rotateToFit by rotateToFitPref.collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
title = stringResource(R.string.pager_viewer), title = stringResource(R.string.pager_viewer),
@ -255,6 +257,10 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = dualPageSplitPref, pref = dualPageSplitPref,
title = stringResource(R.string.pref_dual_page_split), title = stringResource(R.string.pref_dual_page_split),
onValueChanged = {
rotateToFitPref.set(false)
true
},
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.dualPageInvertPaged(), pref = readerPreferences.dualPageInvertPaged(),
@ -262,6 +268,19 @@ object SettingsReaderScreen : SearchableSettings {
subtitle = stringResource(R.string.pref_dual_page_invert_summary), subtitle = stringResource(R.string.pref_dual_page_invert_summary),
enabled = dualPageSplit, 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,
),
), ),
) )
} }

View File

@ -78,6 +78,10 @@ class ReaderPreferences(
fun dualPageInvertWebtoon() = preferenceStore.getBoolean("pref_dual_page_invert_webtoon", false) 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 // endregion
// region Color filter // region Color filter

View File

@ -92,12 +92,27 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders()) binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders())
binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged()) 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() 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) .launchIn((context as ReaderActivity).lifecycleScope)
binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged()) 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())
// SY --> // SY -->
binding.pagerPrefsGroup.pageTransitionsPager.bindToPreference(readerPreferences.pageTransitionsPager()) binding.pagerPrefsGroup.pageTransitionsPager.bindToPreference(readerPreferences.pageTransitionsPager())
binding.pagerPrefsGroup.pageLayout.bindToPreference(readerPreferences.pageLayout()) binding.pagerPrefsGroup.pageLayout.bindToPreference(readerPreferences.pageLayout())

View File

@ -36,6 +36,12 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc
var dualPageInvert = false var dualPageInvert = false
protected set protected set
var dualPageRotateToFit = false
protected set
var dualPageRotateToFitInvert = false
protected set
abstract var navigator: ViewerNavigation abstract var navigator: ViewerNavigation
protected set protected set

View File

@ -123,6 +123,18 @@ class PagerConfig(
readerPreferences.dualPageInvertPaged() readerPreferences.dualPageInvertPaged()
.register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() }) .register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
readerPreferences.dualPageRotateToFit()
.register(
{ dualPageRotateToFit = it },
{ imagePropertyChangedListener?.invoke() },
)
readerPreferences.dualPageRotateToFitInvert()
.register(
{ dualPageRotateToFitInvert = it },
{ imagePropertyChangedListener?.invoke() },
)
// SY --> // SY -->
readerPreferences.pageTransitionsPager() readerPreferences.pageTransitionsPager()
.register({ usePageTransitions = it }, { imagePropertyChangedListener?.invoke() }) .register({ usePageTransitions = it }, { imagePropertyChangedListener?.invoke() })

View File

@ -210,6 +210,10 @@ class PagerPageHolder(
} }
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream { private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
if (viewer.config.dualPageRotateToFit) {
return rotateDualPage(imageStream)
}
if (!viewer.config.dualPageSplit) { if (!viewer.config.dualPageSplit) {
return imageStream return imageStream
} }
@ -228,6 +232,16 @@ class PagerPageHolder(
return splitInHalf(imageStream) 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 mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream { private fun mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
// Handle adding a center margin to wide images if requested // Handle adding a center margin to wide images if requested
if (imageStream2 == null) { if (imageStream2 == null) {

View File

@ -98,6 +98,26 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/dual_page_rotate_to_fit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:text="@string/pref_page_rotate"
android:textColor="?android:attr/textColorSecondary" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/dual_page_rotate_to_fit_invert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:text="@string/pref_page_rotate_invert"
android:textColor="?android:attr/textColorSecondary"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/page_transitions_pager" android:id="@+id/page_transitions_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -127,6 +147,6 @@
android:id="@+id/tapping_prefs_group" android:id="@+id/tapping_prefs_group"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:constraint_referenced_ids="pager_nav,tapping_inverted,dual_page_split,dual_page_invert,page_transitions_pager" /> 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,page_transitions_pager" />
</LinearLayout> </LinearLayout>

View File

@ -8,6 +8,7 @@ import android.graphics.BitmapFactory
import android.graphics.BitmapRegionDecoder import android.graphics.BitmapRegionDecoder
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Rect import android.graphics.Rect
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@ -152,6 +153,23 @@ object ImageUtil {
return ByteArrayInputStream(output.toByteArray()) 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 * Split the image into left and right parts, then merge them into a
* new vertically-aligned image. * new vertically-aligned image.

View File

@ -314,6 +314,8 @@
<string name="pref_dual_page_split">Split wide pages</string> <string name="pref_dual_page_split">Split wide pages</string>
<string name="pref_dual_page_invert">Invert split page placement</string> <string name="pref_dual_page_invert">Invert split page placement</string>
<string name="pref_dual_page_invert_summary">If the placement of the split wide pages don\'t match reading direction</string> <string name="pref_dual_page_invert_summary">If the placement of the split wide pages don\'t match reading direction</string>
<string name="pref_page_rotate">Rotate wide pages to fit</string>
<string name="pref_page_rotate_invert">Flip orientation of rotated wide pages</string>
<string name="pref_long_strip_split">Split tall images (BETA)</string> <string name="pref_long_strip_split">Split tall images (BETA)</string>
<string name="pref_cutout_short">Show content in cutout area</string> <string name="pref_cutout_short">Show content in cutout area</string>
<string name="pref_page_transitions">Animate page transitions</string> <string name="pref_page_transitions">Animate page transitions</string>