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>
This commit is contained in:
arkon 2023-03-24 22:48:59 -04:00
parent f94d902bb6
commit 953720472f
9 changed files with 113 additions and 3 deletions

View file

@ -150,10 +150,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),
@ -216,6 +218,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(),
@ -223,6 +229,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

@ -73,6 +73,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,11 +92,26 @@ 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())
} }
/** /**

View file

@ -37,6 +37,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

@ -94,6 +94,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() },
)
} }
private fun zoomTypeFromPreference(value: Int) { private fun zoomTypeFromPreference(value: Int) {

View file

@ -180,6 +180,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
} }
@ -198,6 +202,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 splitInHalf(imageStream: InputStream): InputStream { private fun splitInHalf(imageStream: InputStream): InputStream {
var side = when { var side = when {
viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT

View file

@ -91,10 +91,30 @@
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" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
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" /> 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" />
</LinearLayout> </LinearLayout>

View file

@ -7,6 +7,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.BitmapRegionDecoder import android.graphics.BitmapRegionDecoder
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
@ -151,6 +152,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 new image. * Split the image into left and right parts, then merge them into a new 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>