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) .launchIn(scope)
preferences.readerTheme().asFlow() // SY -->
/*preferences.readerTheme().asFlow()
.drop(1) // We only care about updates .drop(1) // We only care about updates
.onEach { recreate() } .onEach { recreate() }
.launchIn(scope) .launchIn(scope)*/
// SY <--
preferences.showPageNumber().asFlow() preferences.showPageNumber().asFlow()
.onEach { setPageNumberVisibility(it) } .onEach { setPageNumberVisibility(it) }

View File

@ -1,12 +1,15 @@
package eu.kanade.tachiyomi.ui.reader.loader package eu.kanade.tachiyomi.ui.reader.loader
import android.graphics.BitmapFactory
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage 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.lang.plusAssign
import eu.kanade.tachiyomi.util.system.ImageUtil
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.PriorityBlockingQueue
@ -258,6 +261,18 @@ class HttpPageLoader(
} }
} }
.doOnNext { .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.stream = { chapterCache.getImageFile(imageUrl).inputStream() }
page.status = Page.READY page.status = Page.READY
} }

View File

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

View File

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

View File

@ -1,6 +1,8 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.PointF import android.graphics.PointF
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.GestureDetector import android.view.GestureDetector
@ -27,20 +29,26 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.github.chrisbanes.photoview.PhotoView import com.github.chrisbanes.photoview.PhotoView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.glide.GlideApp 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.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
import eu.kanade.tachiyomi.ui.webview.WebViewActivity 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.ImageUtil
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import exh.util.isInNightMode
import java.io.InputStream import java.io.InputStream
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
/** /**
* View of the ViewPager that contains a page of a chapter. * View of the ViewPager that contains a page of a chapter.
@ -242,7 +250,34 @@ class PagerPageHolder(
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { isAnimated -> .doOnNext { isAnimated ->
if (!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 { } else {
initImageView().setImage(openStream!!) initImageView().setImage(openStream!!)
} }
@ -253,6 +288,20 @@ class PagerPageHolder(
.subscribe({}, {}) .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. * Called when the page has an error.
*/ */
@ -464,4 +513,14 @@ class PagerPageHolder(
}) })
.into(this) .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 { intListPreference {
key = Keys.readerTheme key = Keys.readerTheme
titleRes = R.string.pref_reader_theme titleRes = R.string.pref_reader_theme
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background) 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") entryValues = arrayOf("1", "2", "0", "3", "4")
defaultValue = "1" defaultValue = "1"
summary = "%s" summary = "%s"
} }

View File

@ -1,7 +1,15 @@
package eu.kanade.tachiyomi.util.system 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.io.InputStream
import java.net.URLConnection import java.net.URLConnection
import kotlin.math.abs
object ImageUtil { object ImageUtil {
@ -71,4 +79,205 @@ object ImageUtil {
GIF("image/gif", "gif"), GIF("image/gif", "gif"),
WEBP("image/webp", "webp") 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.app.job.JobScheduler
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
/** /**
@ -16,3 +17,8 @@ val Context.clipboardManager: ClipboardManager
val Context.jobScheduler: JobScheduler val Context.jobScheduler: JobScheduler
get() = applicationContext.getSystemService(Context.JOB_SCHEDULER_SERVICE) as 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/black_background</item>
<item>@string/gray_background</item> <item>@string/gray_background</item>
<item>@string/white_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>
<string-array name="reader_themes_values"> <string-array name="reader_themes_values">
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
<item>0</item> <item>0</item>
<item>3</item>
<item>4</item>
</string-array> </string-array>
<string-array name="image_scale_type"> <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">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="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="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 -->
<!-- Reader Actions --> <!-- Reader Actions -->