Copy smart background from J2k

This commit is contained in:
Jobobby04 2020-08-06 16:11:00 -04:00
parent c340884adb
commit c2adf2fe0a
10 changed files with 318 additions and 5 deletions

View File

@ -901,10 +901,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
}
.launchIn(scope)
preferences.readerTheme().asFlow()
// SY -->
/*preferences.readerTheme().asFlow()
.drop(1) // We only care about updates
.onEach { recreate() }
.launchIn(scope)
.launchIn(scope)*/
// SY <--
preferences.showPageNumber().asFlow()
.onEach { setPageNumberVisibility(it) }

View File

@ -1,12 +1,15 @@
package eu.kanade.tachiyomi.ui.reader.loader
import android.graphics.BitmapFactory
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerPageHolder
import eu.kanade.tachiyomi.util.lang.plusAssign
import eu.kanade.tachiyomi.util.system.ImageUtil
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import java.util.concurrent.PriorityBlockingQueue
@ -258,6 +261,18 @@ class HttpPageLoader(
}
}
.doOnNext {
// SY -->
val readerTheme = prefs.readerTheme().get()
if (readerTheme >= 3) {
val stream = chapterCache.getImageFile(imageUrl).inputStream()
val image = BitmapFactory.decodeStream(stream)
page.bg = ImageUtil.autoSetBackground(
image, readerTheme == 2, prefs.context
)
page.bgType = PagerPageHolder.getBGType(readerTheme, prefs.context)
stream.close()
}
// SY <--
page.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
page.status = Page.READY
}

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.reader.model
import android.graphics.drawable.Drawable
import eu.kanade.tachiyomi.source.model.Page
import java.io.InputStream
@ -7,7 +8,12 @@ class ReaderPage(
index: Int,
url: String = "",
imageUrl: String? = null,
// SY -->
var bg: Drawable? = null,
var bgType: Int? = null,
// SY <--
var stream: (() -> InputStream)? = null
) : Page(index, url, imageUrl, null) {
lateinit var chapter: ReaderChapter

View File

@ -20,6 +20,11 @@ class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelpe
var imageCropBorders = false
private set
// SY -->
var readerTheme = 0
private set
// SY <--
init {
preferences.imageScaleType()
.register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() })
@ -29,6 +34,11 @@ class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelpe
preferences.cropBorders()
.register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() })
// SY -->
preferences.readerTheme()
.register({ readerTheme = it }, { imagePropertyChangedListener?.invoke() })
// SY <--
}
private fun zoomTypeFromPreference(value: Int) {

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.PointF
import android.graphics.drawable.Drawable
import android.view.GestureDetector
@ -27,20 +29,26 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.github.chrisbanes.photoview.PhotoView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import exh.util.isInNightMode
import java.io.InputStream
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
/**
* View of the ViewPager that contains a page of a chapter.
@ -242,7 +250,34 @@ class PagerPageHolder(
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { isAnimated ->
if (!isAnimated) {
initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!))
// SY -->
if (viewer.config.readerTheme >= 3) {
val imageView = initSubsamplingImageView()
if (page.bg != null && page.bgType == getBGType(
viewer.config.readerTheme,
context
)
) {
imageView.setImage(ImageSource.inputStream(openStream!!))
imageView.background = page.bg
}
// if the user switches to automatic when pages are already cached, the bg needs to be loaded
else {
val bytesArray = openStream!!.readBytes()
val bytesStream = bytesArray.inputStream()
imageView.setImage(ImageSource.inputStream(bytesStream))
bytesStream.close()
launchUI {
imageView.background = setBG(bytesArray)
page.bg = imageView.background
page.bgType = getBGType(viewer.config.readerTheme, context)
}
}
} else {
initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!))
}
// SY <--
} else {
initImageView().setImage(openStream!!)
}
@ -253,6 +288,20 @@ class PagerPageHolder(
.subscribe({}, {})
}
// SY -->
private suspend fun setBG(bytesArray: ByteArray): Drawable {
return withContext(Dispatchers.Default) {
val preferences by injectLazy<PreferencesHelper>()
ImageUtil.autoSetBackground(
BitmapFactory.decodeByteArray(
bytesArray, 0, bytesArray.size
),
preferences.readerTheme().get() == 3, context
)
}
}
// SY <--
/**
* Called when the page has an error.
*/
@ -464,4 +513,14 @@ class PagerPageHolder(
})
.into(this)
}
// SY -->
companion object {
fun getBGType(readerTheme: Int, context: Context): Int {
return if (readerTheme == 3) {
if (context.isInNightMode()) 2 else 1
} else 0
}
}
// SY <--
}

View File

@ -78,8 +78,8 @@ class SettingsReaderController : SettingsController() {
intListPreference {
key = Keys.readerTheme
titleRes = R.string.pref_reader_theme
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background)
entryValues = arrayOf("1", "2", "0")
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background, R.string.smart_based_on_page, R.string.smart_based_on_page_and_theme)
entryValues = arrayOf("1", "2", "0", "3", "4")
defaultValue = "1"
summary = "%s"
}

View File

@ -1,7 +1,15 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import eu.kanade.tachiyomi.R
import java.io.InputStream
import java.net.URLConnection
import kotlin.math.abs
object ImageUtil {
@ -71,4 +79,205 @@ object ImageUtil {
GIF("image/gif", "gif"),
WEBP("image/webp", "webp")
}
// SY -->
fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable {
val backgroundColor = if (alwaysUseWhite) Color.WHITE else {
context.getResourceColor(R.attr.colorPrimary)
}
if (image == null) return ColorDrawable(backgroundColor)
if (image.width < 50 || image.height < 50) {
return ColorDrawable(backgroundColor)
}
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 offsetY = (image.height * 0.01).toInt()
val topLeftIsDark = isDark(image.getPixel(left, top))
val topRightIsDark = isDark(image.getPixel(right, top))
val midLeftIsDark = isDark(image.getPixel(left, midY))
val midRightIsDark = isDark(image.getPixel(right, midY))
val topMidIsDark = isDark(image.getPixel(midX, top))
val botLeftIsDark = isDark(image.getPixel(left, bot))
val botRightIsDark = isDark(image.getPixel(right, bot))
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
if (!isWhite(image.getPixel(left, top)) && pixelIsClose(image.getPixel(left, top), image.getPixel(midX, top)) &&
!isWhite(image.getPixel(midX, top)) && pixelIsClose(image.getPixel(midX, top), image.getPixel(right, top)) &&
!isWhite(image.getPixel(right, top)) && pixelIsClose(image.getPixel(right, top), image.getPixel(right, bot)) &&
!isWhite(image.getPixel(right, bot)) && pixelIsClose(image.getPixel(right, bot), image.getPixel(midX, bot)) &&
!isWhite(image.getPixel(midX, bot)) && pixelIsClose(image.getPixel(midX, bot), image.getPixel(left, bot)) &&
!isWhite(image.getPixel(left, bot)) && pixelIsClose(image.getPixel(left, bot), image.getPixel(left, top))
) {
return ColorDrawable(image.getPixel(left, top))
}
if (isWhite(image.getPixel(left, top)).toInt() +
isWhite(image.getPixel(right, top)).toInt() +
isWhite(image.getPixel(left, bot)).toInt() +
isWhite(image.getPixel(right, bot)).toInt() > 2
) {
darkBG = false
}
var blackPixel = when {
topLeftIsDark -> image.getPixel(left, top)
topRightIsDark -> image.getPixel(right, top)
botLeftIsDark -> image.getPixel(left, bot)
botRightIsDark -> image.getPixel(right, bot)
else -> backgroundColor
}
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, left - offsetX, right + offsetX)) {
var whitePixelsStreak = 0
var whitePixels = 0
var blackPixelsStreak = 0
var blackPixels = 0
var blackStreak = false
var whiteStrak = false
val notOffset = x == left || x == right
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 (isWhite(pixel)) {
whitePixelsStreak++
whitePixels++
if (notOffset) {
overallWhitePixels++
}
if (whitePixelsStreak > 14) {
whiteStrak = true
}
if (whitePixelsStreak > 6 && whitePixelsStreak >= index - 1) {
topWhiteStreak = whitePixelsStreak
}
} else {
whitePixelsStreak = 0
if (isDark(pixel) && isDark(pixelOff)) {
blackPixels++
if (notOffset) {
overallBlackPixels++
}
blackPixelsStreak++
if (blackPixelsStreak >= 14) {
blackStreak = true
}
continue
}
}
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 == right + offsetX) {
blackPixel = when {
topRightIsDark -> image.getPixel(right, top)
botRightIsDark -> image.getPixel(right, bot)
else -> blackPixel
}
}
darkBG = true
overallWhitePixels = 0
break@outer
}
blackStreak -> {
darkBG = true
if (x == right || x == right + offsetX) {
blackPixel = when {
topRightIsDark -> image.getPixel(right, top)
botRightIsDark -> image.getPixel(right, bot)
else -> blackPixel
}
}
if (blackPixels > 18) {
overallWhitePixels = 0
break@outer
}
}
whiteStrak || whitePixels > 22 -> darkBG = false
}
}
val topIsBlackStreak = topBlackStreak > topWhiteStreak
val bottomIsBlackStreak = botBlackStreak > botWhiteStreak
if (overallWhitePixels > 9 && overallWhitePixels > overallBlackPixels) {
darkBG = false
}
if (topIsBlackStreak && bottomIsBlackStreak) {
darkBG = true
}
if (darkBG) {
return if (isWhite(image.getPixel(left, bot)) && isWhite(image.getPixel(right, bot))) {
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
)
} else if (isWhite(image.getPixel(left, top)) && isWhite(image.getPixel(right, top))) {
GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(backgroundColor, backgroundColor, blackPixel, blackPixel)
)
} else ColorDrawable(blackPixel)
}
if (topIsBlackStreak || (
topLeftIsDark && topRightIsDark &&
isDark(image.getPixel(left - offsetX, top)) && isDark(image.getPixel(right + offsetX, top)) &&
(topMidIsDark || overallBlackPixels > 9)
)
) {
return GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(blackPixel, blackPixel, backgroundColor, backgroundColor)
)
} else if (bottomIsBlackStreak || (
botLeftIsDark && botRightIsDark &&
isDark(image.getPixel(left - offsetX, bot)) && isDark(image.getPixel(right + offsetX, bot)) &&
(isDark(image.getPixel(midX, bot)) || overallBlackPixels > 9)
)
) {
return GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
intArrayOf(backgroundColor, backgroundColor, blackPixel, blackPixel)
)
}
return ColorDrawable(backgroundColor)
}
private fun isDark(color: Int): Boolean {
return Color.red(color) < 40 && Color.blue(color) < 40 && Color.green(color) < 40 &&
Color.alpha(color) > 200
}
private fun isWhite(color: Int): Boolean {
return Color.red(color) + Color.blue(color) + Color.green(color) > 740
}
private fun Boolean.toInt() = if (this) 1 else 0
private fun pixelIsClose(color1: Int, color2: Int): Boolean {
return abs(Color.red(color1) - Color.red(color2)) < 30 &&
abs(Color.green(color1) - Color.green(color2)) < 30 &&
abs(Color.blue(color1) - Color.blue(color2)) < 30
}
// SY <--
}

View File

@ -3,6 +3,7 @@ package exh.util
import android.app.job.JobScheduler
import android.content.ClipboardManager
import android.content.Context
import android.content.res.Configuration
import android.net.wifi.WifiManager
/**
@ -16,3 +17,8 @@ val Context.clipboardManager: ClipboardManager
val Context.jobScheduler: JobScheduler
get() = applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
fun Context.isInNightMode(): Boolean {
val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return currentNightMode == Configuration.UI_MODE_NIGHT_YES
}

View File

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

View File

@ -187,6 +187,8 @@
<string name="auto_webtoon_mode">Auto Webtoon Mode</string>
<string name="auto_webtoon_mode_summary">Use auto webtoon mode for manga that are detected to likely use the long strip format</string>
<string name="enable_zoom_out">Enable zoom out</string>
<string name="smart_based_on_page">Smart (based on page)</string>
<string name="smart_based_on_page_and_theme">Smart (based on page and theme)</string>
<!-- Reader -->
<!-- Reader Actions -->