Center padding option for Surface Duos/other foldables (#634)

* Add center margin option for horizontal dual page view

* Update readme to make the point of the fork obvious

* Center margins now added to large single images that would display in place of two smaller pages

* Cleanup; reworked preference into an int selector to specify which margins to add

* Suggested fixes and and readme reversion which somehow got skipped last commit

* Missed the build block in readme. Oops.
This commit is contained in:
bucketmouse 2022-07-28 16:03:09 -07:00 committed by GitHub
parent 5807920636
commit 89600fc7aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 129 additions and 17 deletions

View File

@ -85,8 +85,8 @@ Please make sure to read the full guidelines. Your issue may be closed without w
<details><summary>Bugs</summary>
* Include version (More → About → Version)
* If not latest, try updating, it may have already been solved
* Preview version is equal to the number of commits as seen in the main page
* If not latest, try updating, it may have already been solved
* Preview version is equal to the number of commits as seen in the main page
* Include steps to reproduce (if not obvious from description)
* Include screenshot (if needed)
* If it could be device-dependent, try reproducing on another device (if possible)
@ -119,4 +119,4 @@ See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
## FAQ
[See our website.](https://tachiyomi.org/)
You can also reach out to us on [Discord](https://discord.gg/tachiyomi).
You can also reach out to us on [Discord](https://discord.gg/tachiyomi).

View File

@ -517,5 +517,7 @@ class PreferencesHelper(val context: Context) {
fun pageLayout() = flowPrefs.getInt("page_layout", PagerConfig.PageLayout.AUTOMATIC)
fun centerMarginType() = flowPrefs.getInt("center_margin_type", PagerConfig.CenterMarginType.NONE)
fun invertDoublePages() = flowPrefs.getBoolean("invert_double_pages", false)
}

View File

@ -856,7 +856,7 @@ class ReaderPresenter(
return imageSaver.save(
image = Image.Page(
inputStream = { ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, bg) },
inputStream = { ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, 0, bg) },
name = filename,
location = location,
),

View File

@ -97,6 +97,7 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
binding.pagerPrefsGroup.pageTransitionsPager.bindToPreference(preferences.pageTransitionsPager())
binding.pagerPrefsGroup.pageLayout.bindToPreference(preferences.pageLayout())
binding.pagerPrefsGroup.invertDoublePages.bindToPreference(preferences.invertDoublePages())
binding.pagerPrefsGroup.centerMarginType.bindToPreference(preferences.centerMarginType())
// SY <--
}

View File

@ -71,6 +71,9 @@ class PagerConfig(
@ColorInt
var pageCanvasColor = Color.WHITE
var centerMarginType = CenterMarginType.NONE
// SY <--
init {
@ -150,6 +153,9 @@ class PagerConfig(
},
)
preferences.centerMarginType()
.register({ centerMarginType = it }, { imagePropertyChangedListener?.invoke() })
preferences.invertDoublePages()
.register({ invertDoublePages = it && dualPageSplit == false }, { imagePropertyChangedListener?.invoke() })
// SY <--
@ -197,6 +203,13 @@ class PagerConfig(
navigationModeChangedListener?.invoke()
}
object CenterMarginType {
const val NONE = 0
const val DOUBLE_PAGE_CENTER_MARGIN = 1
const val WIDE_PAGE_CENTER_MARGIN = 2
const val DOUBLE_AND_WIDE_CENTER_MARGIN = 3
}
object PageLayout {
const val SINGLE_PAGE = 0
const val DOUBLE_PAGES = 1

View File

@ -28,6 +28,7 @@ import java.io.BufferedInputStream
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.math.roundToInt
/**
@ -353,7 +354,18 @@ class PagerPageHolder(
}
private fun mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
imageStream2 ?: return imageStream
// Handle adding a center margin to wide images if requested
if (imageStream2 == null) {
if (imageStream is BufferedInputStream && ImageUtil.isWideImage(imageStream) &&
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
!viewer.config.imageCropBorders
) {
return ImageUtil.AddHorizontalCenterMargin(imageStream, getHeight(), context)
} else {
return imageStream
}
}
if (page.fullPage) return imageStream
if (ImageUtil.isAnimatedAndSupported(imageStream)) {
page.fullPage = true
@ -424,7 +436,12 @@ class PagerPageHolder(
imageStream.close()
imageStream2.close()
return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, viewer.config.pageCanvasColor) {
val centerMargin = if (viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN > 0 &&
!viewer.config.imageCropBorders
) 96 / (max(1, getHeight()) / max(height, height2)) else 0
return ImageUtil.mergeBitmaps(imageBitmap, imageBitmap2, isLTR, centerMargin, viewer.config.pageCanvasColor) {
viewer.scope.launchUI {
if (it == 100) {
progressIndicator.hide()
@ -461,7 +478,11 @@ class PagerPageHolder(
}
}
return ImageUtil.splitInHalf(imageStream, side)
val sideMargin = if ((viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN) > 0 &&
viewer.config.doublePages && !viewer.config.imageCropBorders
) 48 else 0
return ImageUtil.splitInHalf(imageStream, side, sideMargin)
}
private fun onPageSplit(page: ReaderPage) {

View File

@ -508,6 +508,18 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.invert_double_pages
visibleIf(preferences.pageLayout()) { it != PagerConfig.PageLayout.SINGLE_PAGE }
}
intListPreference {
bindTo(preferences.centerMarginType())
titleRes = R.string.center_margin
entriesRes = arrayOf(
R.string.center_margin_none,
R.string.center_margin_double_page,
R.string.center_margin_wide_page,
R.string.center_margin_double_and_wide_page,
)
entryValues = arrayOf("0", "1", "2", "3")
}
}
// SY <--
}

View File

@ -1,5 +1,4 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.content.res.Configuration
import android.graphics.Bitmap
@ -128,16 +127,16 @@ object ImageUtil {
/**
* Extract the 'side' part from imageStream and return it as InputStream.
*/
fun splitInHalf(imageStream: InputStream, side: Side): InputStream {
fun splitInHalf(imageStream: InputStream, side: Side, sidePadding: Int): InputStream {
val imageBytes = imageStream.readBytes()
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
val height = imageBitmap.height
val width = imageBitmap.width
val singlePage = Rect(0, 0, width / 2, height)
val singlePage = Rect(0, 0, width / 2 + sidePadding, height)
val half = createBitmap(width / 2, height)
val half = createBitmap(width / 2 + sidePadding, height)
val part = when (side) {
Side.RIGHT -> Rect(width - width / 2, 0, width, height)
Side.LEFT -> Rect(0, 0, width / 2, height)
@ -152,7 +151,8 @@ object ImageUtil {
}
/**
* 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 vertically-aligned image.
*/
fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream {
val imageBytes = imageStream.readBytes()
@ -187,6 +187,40 @@ object ImageUtil {
enum class Side {
RIGHT, LEFT
}
// SY -->
/**
* Split the image into left and right parts, then merge them into a
* new image with added center padding scaled relative to the height of the display view
* to compensate for scaling.
*/
fun AddHorizontalCenterMargin(imageStream: InputStream, viewHeight: Int, backgroundContext: Context): InputStream {
val imageBitmap = ImageDecoder.newInstance(imageStream)?.decode()!!
val height = imageBitmap.height
val width = imageBitmap.width
val centerPadding = 96 / (max(1, viewHeight) / height)
val leftSourcePart = Rect(0, 0, width / 2, height)
val rightSourcePart = Rect(width / 2, 0, width, height)
val leftTargetPart = Rect(0, 0, width / 2, height)
val rightTargetPart = Rect(width / 2 + centerPadding, 0, width + centerPadding, height)
val bgColor = chooseBackground(backgroundContext, imageStream)
bgColor.setBounds(width / 2, 0, width / 2 + centerPadding, height)
val result = createBitmap(width + centerPadding, height)
result.applyCanvas {
drawBitmap(imageBitmap, leftSourcePart, leftTargetPart, null)
drawBitmap(imageBitmap, rightSourcePart, rightTargetPart, null)
bgColor.draw(this)
}
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
return ByteArrayInputStream(output.toByteArray())
}
// SY <--
/**
* Check whether the image is considered a tall image.
@ -534,31 +568,37 @@ object ImageUtil {
imageBitmap: Bitmap,
imageBitmap2: Bitmap,
isLTR: Boolean,
centerMargin: Int,
@ColorInt background: Int = Color.WHITE,
progressCallback: ((Int) -> Unit)? = null,
): ByteArrayInputStream {
val height = imageBitmap.height
val width = imageBitmap.width
val height2 = imageBitmap2.height
val width2 = imageBitmap2.width
val maxHeight = max(height, height2)
val result = Bitmap.createBitmap(width + width2, max(height, height2), Bitmap.Config.ARGB_8888)
val result = Bitmap.createBitmap(width + width2 + centerMargin, max(height, height2), Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
canvas.drawColor(background)
val upperPart = Rect(
if (isLTR) 0 else width2,
if (isLTR) 0 else width2 + centerMargin,
(maxHeight - imageBitmap.height) / 2,
(if (isLTR) 0 else width2) + imageBitmap.width,
(if (isLTR) 0 else width2 + centerMargin) + imageBitmap.width,
imageBitmap.height + (maxHeight - imageBitmap.height) / 2,
)
canvas.drawBitmap(imageBitmap, imageBitmap.rect, upperPart, null)
progressCallback?.invoke(98)
val bottomPart = Rect(
if (!isLTR) 0 else width,
if (!isLTR) 0 else width + centerMargin,
(maxHeight - imageBitmap2.height) / 2,
(if (!isLTR) 0 else width) + imageBitmap2.width,
(if (!isLTR) 0 else width + centerMargin) + imageBitmap2.width,
imageBitmap2.height + (maxHeight - imageBitmap2.height) / 2,
)
canvas.drawBitmap(imageBitmap2, imageBitmap2.rect, bottomPart, null)
progressCallback?.invoke(99)

View File

@ -116,6 +116,13 @@
android:textColor="?android:attr/textColorSecondary"
android:text="@string/invert_double_pages" />
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
android:id="@+id/center_margin_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/center_margin_types"
app:title="@string/pref_center_margin" />
<androidx.constraintlayout.widget.Group
android:id="@+id/tapping_prefs_group"
android:layout_width="wrap_content"

View File

@ -21,4 +21,11 @@
<item>@string/double_pages</item>
<item>@string/automatic_orientation</item>
</string-array>
<string-array name="center_margin_types">
<item>@string/center_margin_none</item>
<item>@string/center_margin_double_page</item>
<item>@string/center_margin_wide_page</item>
<item>@string/center_margin_double_and_wide_page</item>
</string-array>
</resources>

View File

@ -314,6 +314,15 @@
<string name="automatic_can_still_switch">While using automatic page layout, you can still switch between layouts while reading without overriding this setting</string>
<string name="invert_double_pages">Invert double pages</string>
<!-- Center margin -->
<string name="center_margin">Center Margin</string>
<string name="center_margin_none">None</string>
<string name="center_margin_double_page">Add to double Page</string>
<string name="center_margin_wide_page">Add to wide Page</string>
<string name="center_margin_double_and_wide_page">Add to both</string>
<string name="pref_center_margin">Center margin type</string>
<string name="pref_center_margin_summary">Insert spacer to accommodate deadspace on foldable devices.</string>
<!-- Manga Page -->
<!-- Manga Info -->
<string name="az_recommends">See Recommendations</string>