diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt index cb8f5578b..916ef77de 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt @@ -117,6 +117,10 @@ internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) label = stringResource(MR.strings.pref_inverted_colors), pref = screenModel.preferences.invertedColors(), ) + CheckboxItem( + label = stringResource(MR.strings.pref_flip_horizontally), + pref = screenModel.preferences.flipHorizontally(), + ) } private fun getColorValue(currentColor: Int, color: Int, mask: Long, bitShift: Int): Int { 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 63162788c..d4eb5f19e 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 @@ -109,6 +109,8 @@ class ReaderPreferences( fun invertedColors() = preferenceStore.getBoolean("pref_inverted_colors", false) + fun flipHorizontally() = preferenceStore.getBoolean("flip_horizontally", false) + // endregion // region Controls 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 b319fd928..38164d21a 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 @@ -48,6 +48,9 @@ class PagerConfig( var landscapeZoom = false private set + var flipHorizontally = false + private set + init { readerPreferences.readerTheme() .register( @@ -106,6 +109,9 @@ class PagerConfig( { dualPageRotateToFitInvert = it }, { imagePropertyChangedListener?.invoke() }, ) + + readerPreferences.flipHorizontally() + .register({ flipHorizontally = 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 71d499c38..4e4204247 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 @@ -174,6 +174,10 @@ class PagerPageHolder( } private fun process(page: ReaderPage, imageSource: BufferedSource): BufferedSource { + if (viewer.config.flipHorizontally) { + return ImageUtil.flipImage(imageSource, true, false) + } + if (viewer.config.dualPageRotateToFit) { return rotateDualPage(imageSource) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt index f55478862..7fbf2b77e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt @@ -44,6 +44,9 @@ class WebtoonConfig( val theme = readerPreferences.readerTheme().get() + var flipHorizontally = false + private set + init { readerPreferences.cropBordersWebtoon() .register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() }) @@ -96,6 +99,9 @@ class WebtoonConfig( .distinctUntilChanged() .onEach { themeChangedListener?.invoke() } .launchIn(scope) + + readerPreferences.flipHorizontally() + .register({ flipHorizontally = it }, { imagePropertyChangedListener?.invoke() }) } override var navigator: ViewerNavigation = defaultNavigation() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index 488db7bb6..3c162b0fb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -213,6 +213,10 @@ class WebtoonPageHolder( } private fun process(imageSource: BufferedSource): BufferedSource { + if (viewer.config.flipHorizontally) { + return ImageUtil.flipImage(imageSource, true, false) + } + if (viewer.config.dualPageRotateToFit) { return rotateDualPage(imageSource) } diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index f5e9a8098..a9408b687 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -163,11 +163,26 @@ object ImageUtil { return output } + fun flipImage(imageSource: BufferedSource, flipX: Boolean, flipY: Boolean): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) + val flipped = flipBitMap(imageBitmap, flipX, flipY) + + val output = Buffer() + flipped.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) + + return output + } + 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) } + private fun flipBitMap(bitmap: Bitmap, flipX: Boolean, flipY: Boolean): Bitmap { + val matrix = Matrix().apply { postScale(if (flipX) -1f else 1f, if (flipY) -1f else 1f) } + 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/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index bd30e2900..598f5a8a5 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -375,6 +375,7 @@ Custom brightness Grayscale Inverted + Flip horizontally Custom color filter Color filter blend mode Overlay