Merge branch 'master' of github.com:jmir1/tachiyomi

This commit is contained in:
jmir1 2021-05-06 03:08:10 +02:00
commit d20a0c4228
7 changed files with 245 additions and 3 deletions

View file

@ -198,7 +198,7 @@ dependencies {
implementation("com.github.inorichi.injekt:injekt-core:65b0440")
// Image library
val coilVersion = "1.2.0"
val coilVersion = "1.2.1"
implementation("io.coil-kt:coil:$coilVersion")
implementation("io.coil-kt:coil-gif:$coilVersion")

View file

@ -23,6 +23,9 @@ class PagerConfig(
preferences: PreferencesHelper = Injekt.get()
) : ViewerConfig(preferences, scope) {
var automaticBackground = false
private set
var dualPageSplitChangedListener: ((Boolean) -> Unit)? = null
var imageScaleType = 1
@ -35,6 +38,9 @@ class PagerConfig(
private set
init {
preferences.readerTheme()
.register({ automaticBackground = it == 3 }, { imagePropertyChangedListener?.invoke() })
preferences.imageScaleType()
.register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })

View file

@ -238,7 +238,12 @@ class PagerPageHolder(
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { isAnimated ->
if (!isAnimated) {
initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!))
initSubsamplingImageView().apply {
if (viewer.config.automaticBackground) {
background = ImageUtil.chooseBackground(context, openStream!!)
}
setImage(ImageSource.inputStream(openStream!!))
}
} else {
initImageView().setImage(openStream!!)
}

View file

@ -1,14 +1,25 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.createBitmap
import androidx.core.graphics.green
import androidx.core.graphics.red
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.net.URLConnection
import kotlin.math.abs
object ImageUtil {
@ -153,4 +164,221 @@ object ImageUtil {
enum class Side {
RIGHT, LEFT
}
/**
* Algorithm for determining what background to accompany a comic/manga page
*/
fun chooseBackground(context: Context, imageStream: InputStream): Drawable {
imageStream.mark(imageStream.available() + 1)
val image = BitmapFactory.decodeStream(imageStream)
imageStream.reset()
val whiteColor = Color.WHITE
if (image == null) return ColorDrawable(whiteColor)
if (image.width < 50 || image.height < 50) {
return ColorDrawable(whiteColor)
}
val top = 5
val bot = image.height - 5
val left = (image.width * 0.0275).toInt()
val right = image.width - left
val midX = image.width / 2
val midY = image.height / 2
val offsetX = (image.width * 0.01).toInt()
val leftOffsetX = left - offsetX
val rightOffsetX = right + offsetX
val topLeftPixel = image.getPixel(left, top)
val topRightPixel = image.getPixel(right, top)
val midLeftPixel = image.getPixel(left, midY)
val midRightPixel = image.getPixel(right, midY)
val topCenterPixel = image.getPixel(midX, top)
val botLeftPixel = image.getPixel(left, bot)
val bottomCenterPixel = image.getPixel(midX, bot)
val botRightPixel = image.getPixel(right, bot)
val topLeftIsDark = topLeftPixel.isDark()
val topRightIsDark = topRightPixel.isDark()
val midLeftIsDark = midLeftPixel.isDark()
val midRightIsDark = midRightPixel.isDark()
val topMidIsDark = topCenterPixel.isDark()
val botLeftIsDark = botLeftPixel.isDark()
val botRightIsDark = botRightPixel.isDark()
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
val topAndBotPixels = listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
!color.isWhite() && color.isCloseTo(other)
}
if (isNotWhiteAndCloseTo.all { it }) {
return ColorDrawable(topLeftPixel)
}
val cornerPixels = listOf(topLeftPixel, topRightPixel, botLeftPixel, botRightPixel)
val numberOfWhiteCorners = cornerPixels.map { cornerPixel -> cornerPixel.isWhite() }
.filter { it }
.size
if (numberOfWhiteCorners > 2) {
darkBG = false
}
var blackColor = when {
topLeftIsDark -> topLeftPixel
topRightIsDark -> topRightPixel
botLeftIsDark -> botLeftPixel
botRightIsDark -> botRightPixel
else -> whiteColor
}
var overallWhitePixels = 0
var overallBlackPixels = 0
var topBlackStreak = 0
var topWhiteStreak = 0
var botBlackStreak = 0
var botWhiteStreak = 0
outer@ for (x in intArrayOf(left, right, leftOffsetX, rightOffsetX)) {
var whitePixelsStreak = 0
var whitePixels = 0
var blackPixelsStreak = 0
var blackPixels = 0
var blackStreak = false
var whiteStreak = false
val notOffset = x == left || x == right
inner@ for ((index, y) in (0 until image.height step image.height / 25).withIndex()) {
val pixel = image.getPixel(x, y)
val pixelOff = image.getPixel(x + (if (x < image.width / 2) -offsetX else offsetX), y)
if (pixel.isWhite()) {
whitePixelsStreak++
whitePixels++
if (notOffset) {
overallWhitePixels++
}
if (whitePixelsStreak > 14) {
whiteStreak = true
}
if (whitePixelsStreak > 6 && whitePixelsStreak >= index - 1) {
topWhiteStreak = whitePixelsStreak
}
} else {
whitePixelsStreak = 0
if (pixel.isDark() && pixelOff.isDark()) {
blackPixels++
if (notOffset) {
overallBlackPixels++
}
blackPixelsStreak++
if (blackPixelsStreak >= 14) {
blackStreak = true
}
continue@inner
}
}
if (blackPixelsStreak > 6 && blackPixelsStreak >= index - 1) {
topBlackStreak = blackPixelsStreak
}
blackPixelsStreak = 0
}
if (blackPixelsStreak > 6) {
botBlackStreak = blackPixelsStreak
} else if (whitePixelsStreak > 6) {
botWhiteStreak = whitePixelsStreak
}
when {
blackPixels > 22 -> {
if (x == right || x == rightOffsetX) {
blackColor = when {
topRightIsDark -> topRightPixel
botRightIsDark -> botRightPixel
else -> blackColor
}
}
darkBG = true
overallWhitePixels = 0
break@outer
}
blackStreak -> {
darkBG = true
if (x == right || x == rightOffsetX) {
blackColor = when {
topRightIsDark -> topRightPixel
botRightIsDark -> botRightPixel
else -> blackColor
}
}
if (blackPixels > 18) {
overallWhitePixels = 0
break@outer
}
}
whiteStreak || whitePixels > 22 -> darkBG = false
}
}
val topIsBlackStreak = topBlackStreak > topWhiteStreak
val bottomIsBlackStreak = botBlackStreak > botWhiteStreak
if (overallWhitePixels > 9 && overallWhitePixels > overallBlackPixels) {
darkBG = false
}
if (topIsBlackStreak && bottomIsBlackStreak) {
darkBG = true
}
val isLandscape = context.resources.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE
if (isLandscape) {
return when {
darkBG -> ColorDrawable(blackColor)
else -> ColorDrawable(whiteColor)
}
}
val botCornersIsWhite = botLeftPixel.isWhite() && botRightPixel.isWhite()
val topCornersIsWhite = topLeftPixel.isWhite() && topRightPixel.isWhite()
val topCornersIsDark = topLeftIsDark && topRightIsDark
val botCornersIsDark = botLeftIsDark && botRightIsDark
val topOffsetCornersIsDark = image.getPixel(leftOffsetX, top).isDark() && image.getPixel(rightOffsetX, top).isDark()
val botOffsetCornersIsDark = image.getPixel(leftOffsetX, bot).isDark() && image.getPixel(rightOffsetX, bot).isDark()
val gradient = when {
darkBG && botCornersIsWhite -> {
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
}
darkBG && topCornersIsWhite -> {
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
}
darkBG -> {
return ColorDrawable(blackColor)
}
topIsBlackStreak || (topCornersIsDark && topOffsetCornersIsDark && (topMidIsDark || overallBlackPixels > 9)) -> {
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
}
bottomIsBlackStreak || (botCornersIsDark && botOffsetCornersIsDark && (bottomCenterPixel.isDark() || overallBlackPixels > 9)) -> {
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
}
else -> {
return ColorDrawable(whiteColor)
}
}
return GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
gradient
)
}
private fun Int.isDark(): Boolean =
red < 40 && blue < 40 && green < 40 && alpha > 200
private fun Int.isCloseTo(other: Int): Boolean =
abs(red - other.red) < 30 && abs(green - other.green) < 30 && abs(blue - other.blue) < 30
private fun Int.isWhite(): Boolean =
red + blue + green > 740
}

View file

@ -13,12 +13,14 @@
<item>@string/black_background</item>
<item>@string/gray_background</item>
<item>@string/white_background</item>
<item>@string/automatic_background</item>
</string-array>
<string-array name="reader_themes_values">
<item>1</item>
<item>2</item>
<item>0</item>
<item>3</item>
</string-array>
<string-array name="image_scale_type">

View file

@ -307,6 +307,7 @@
<string name="white_background">White</string>
<string name="gray_background">Gray</string>
<string name="black_background">Black</string>
<string name="automatic_background">Automatic</string>
<string name="pref_viewer_type">Default reading mode</string>
<string name="default_viewer">Default</string>
<string name="default_nav">Default</string>

View file

@ -1,5 +1,5 @@
object BuildPluginsVersion {
const val AGP = "4.1.3"
const val AGP = "4.2.0"
const val KOTLIN = "1.4.32"
const val KOTLINTER = "3.4.0"
const val VERSIONS_PLUGIN = "0.38.0"