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:
parent
5807920636
commit
89600fc7aa
@ -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).
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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 <--
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 <--
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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>
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user