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 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,
),
),
)
}

View file

@ -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

View file

@ -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())
}
/**

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -91,10 +91,30 @@
android:visibility="gone"
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
android:id="@+id/tapping_prefs_group"
android:layout_width="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>

View file

@ -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.
*/

View file

@ -314,6 +314,8 @@
<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_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_cutout_short">Show content in cutout area</string>
<string name="pref_page_transitions">Animate page transitions</string>