mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Use Coil pipeline instead of SSIV for image decode (#692)
(cherry picked from commit c3e7bb12f4
)
This commit is contained in:
parent
008a906055
commit
d8f812a474
4 changed files with 123 additions and 17 deletions
|
@ -1,14 +1,13 @@
|
|||
package eu.kanade.tachiyomi.data.image.coil
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import coil.ImageLoader
|
||||
import coil.decode.DecodeResult
|
||||
import coil.decode.Decoder
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.decode.ImageSource
|
||||
import coil.decode.*
|
||||
import coil.fetch.SourceResult
|
||||
import coil.request.Options
|
||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import okio.BufferedSource
|
||||
import tachiyomi.decoder.ImageDecoder
|
||||
|
@ -20,26 +19,52 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||
|
||||
override suspend fun decode(): DecodeResult {
|
||||
val decoder = resources.sourceOrNull()?.use {
|
||||
ImageDecoder.newInstance(it.inputStream())
|
||||
ImageDecoder.newInstance(it.inputStream(), options.cropBorders, displayProfile)
|
||||
}
|
||||
|
||||
check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder." }
|
||||
|
||||
val bitmap = decoder.decode()
|
||||
val srcWidth = decoder.width
|
||||
val srcHeight = decoder.height
|
||||
|
||||
val dstWidth = options.size.widthPx(options.scale) { srcWidth }
|
||||
val dstHeight = options.size.heightPx(options.scale) { srcHeight }
|
||||
|
||||
val sampleSize = DecodeUtils.calculateInSampleSize(
|
||||
srcWidth = srcWidth,
|
||||
srcHeight = srcHeight,
|
||||
dstWidth = dstWidth,
|
||||
dstHeight = dstHeight,
|
||||
scale = options.scale,
|
||||
)
|
||||
|
||||
var bitmap = decoder.decode(sampleSize = sampleSize)
|
||||
decoder.recycle()
|
||||
|
||||
check(bitmap != null) { "Failed to decode image." }
|
||||
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||
options.bitmapConfig == Bitmap.Config.HARDWARE &&
|
||||
maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
|
||||
) {
|
||||
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
|
||||
if (hwBitmap != null) {
|
||||
bitmap.recycle()
|
||||
bitmap = hwBitmap
|
||||
}
|
||||
}
|
||||
|
||||
return DecodeResult(
|
||||
drawable = bitmap.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
isSampled = sampleSize > 1,
|
||||
)
|
||||
}
|
||||
|
||||
class Factory : Decoder.Factory {
|
||||
|
||||
override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? {
|
||||
if (!isApplicable(result.source.source())) return null
|
||||
if (!isApplicable(result.source.source()) || !options.customDecoder) return null
|
||||
return TachiyomiImageDecoder(result.source, options)
|
||||
}
|
||||
|
||||
|
@ -54,8 +79,12 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other is ImageDecoderDecoder.Factory
|
||||
override fun equals(other: Any?) = other is Factory
|
||||
|
||||
override fun hashCode() = javaClass.hashCode()
|
||||
}
|
||||
|
||||
companion object {
|
||||
var displayProfile: ByteArray? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package eu.kanade.tachiyomi.data.image.coil
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.Options
|
||||
import coil.size.Dimension
|
||||
import coil.size.Scale
|
||||
import coil.size.Size
|
||||
import coil.size.isOriginal
|
||||
import coil.size.pxOrElse
|
||||
|
||||
internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
|
||||
return if (isOriginal) original() else width.toPx(scale)
|
||||
}
|
||||
|
||||
internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int {
|
||||
return if (isOriginal) original() else height.toPx(scale)
|
||||
}
|
||||
|
||||
internal fun Dimension.toPx(scale: Scale): Int = pxOrElse {
|
||||
when (scale) {
|
||||
Scale.FILL -> Int.MIN_VALUE
|
||||
Scale.FIT -> Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply {
|
||||
setParameter(cropBordersKey, enable)
|
||||
}
|
||||
|
||||
val Options.cropBorders: Boolean
|
||||
get() = parameters.value(cropBordersKey) ?: false
|
||||
|
||||
private val cropBordersKey = "crop_borders"
|
||||
|
||||
fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply {
|
||||
setParameter(customDecoderKey, enable)
|
||||
}
|
||||
|
||||
val Options.customDecoder: Boolean
|
||||
get() = parameters.value(customDecoderKey) ?: false
|
||||
|
||||
private val customDecoderKey = "custom_decoder"
|
||||
|
||||
val Options.bitmapConfig: Bitmap.Config
|
||||
get() = parameters.value(bitmapConfigKey) ?: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Bitmap.Config.HARDWARE else Bitmap.Config.RGB_565
|
||||
|
||||
private val bitmapConfigKey = "bitmap_config"
|
|
@ -81,6 +81,7 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.core.preference.toggle
|
||||
import eu.kanade.tachiyomi.data.image.coil.TachiyomiImageDecoder
|
||||
import eu.kanade.tachiyomi.data.preference.changesIn
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
|
||||
|
@ -2018,7 +2019,9 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
|
|||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray())
|
||||
val data = outputStream.toByteArray()
|
||||
SubsamplingScaleImageView.setDisplayProfile(data)
|
||||
TachiyomiImageDecoder.displayProfile = data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,15 @@ import coil.dispose
|
|||
import coil.imageLoader
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Precision
|
||||
import coil.size.ViewSizeResolver
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
||||
import com.github.chrisbanes.photoview.PhotoView
|
||||
import dev.yokai.domain.ui.settings.ReaderPreferences.CutoutBehaviour
|
||||
import eu.kanade.tachiyomi.data.image.coil.cropBorders
|
||||
import eu.kanade.tachiyomi.data.image.coil.customDecoder
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
|
@ -219,15 +223,36 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
|||
},
|
||||
)
|
||||
|
||||
when (image) {
|
||||
is Drawable -> {
|
||||
val bitmap = (image as BitmapDrawable).bitmap
|
||||
setImage(ImageSource.bitmap(bitmap))
|
||||
if (isWebtoon) {
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(image)
|
||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||
.diskCachePolicy(CachePolicy.DISABLED)
|
||||
.target(
|
||||
onSuccess = { result ->
|
||||
val drawable = result as BitmapDrawable
|
||||
setImage(ImageSource.bitmap(drawable.bitmap))
|
||||
isVisible = true
|
||||
},
|
||||
onError = {
|
||||
this@ReaderPageImageView.onImageLoadError()
|
||||
},
|
||||
)
|
||||
.size(ViewSizeResolver(this@ReaderPageImageView))
|
||||
.precision(Precision.INEXACT)
|
||||
.cropBorders(config.cropBorders)
|
||||
.customDecoder(true)
|
||||
.crossfade(false)
|
||||
.build()
|
||||
context.imageLoader.enqueue(request)
|
||||
} else {
|
||||
when (image) {
|
||||
is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap))
|
||||
is InputStream -> setImage(ImageSource.inputStream(image))
|
||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
||||
}
|
||||
is InputStream -> setImage(ImageSource.inputStream(image))
|
||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
||||
isVisible = true
|
||||
}
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
private fun prepareAnimatedImageView() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue