Drop legacy decoder (#3786)
This commit is contained in:
@@ -32,7 +32,6 @@ dependencies {
|
||||
api(libs.okio)
|
||||
|
||||
implementation(libs.image.decoder)
|
||||
implementation(libs.image.decoder2)
|
||||
|
||||
implementation(libs.unifile)
|
||||
implementation(libs.archive)
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package eu.kanade.tachiyomi.util.system
|
||||
|
||||
import javax.microedition.khronos.egl.EGL10
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.egl.EGLContext
|
||||
import kotlin.math.max
|
||||
|
||||
object GLUtil {
|
||||
val DEVICE_TEXTURE_LIMIT: Int by lazy {
|
||||
// Get EGL Display
|
||||
val egl = EGLContext.getEGL() as EGL10
|
||||
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
|
||||
|
||||
// Initialise
|
||||
val version = IntArray(2)
|
||||
egl.eglInitialize(display, version)
|
||||
|
||||
// Query total number of configurations
|
||||
val totalConfigurations = IntArray(1)
|
||||
egl.eglGetConfigs(display, null, 0, totalConfigurations)
|
||||
|
||||
// Query actual list configurations
|
||||
val configurationsList = arrayOfNulls<EGLConfig>(totalConfigurations[0])
|
||||
egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations)
|
||||
|
||||
val textureSize = IntArray(1)
|
||||
var maximumTextureSize = 0
|
||||
|
||||
// Iterate through all the configurations to located the maximum texture size
|
||||
for (i in 0..<totalConfigurations[0]) {
|
||||
// Only need to check for width since opengl textures are always squared
|
||||
egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize)
|
||||
|
||||
// Keep track of the maximum texture size
|
||||
if (maximumTextureSize < textureSize[0]) maximumTextureSize = textureSize[0]
|
||||
}
|
||||
|
||||
// Release
|
||||
egl.eglTerminate(display)
|
||||
|
||||
// Return largest texture size found (after making it a multiplier of [Multiplier]), or default
|
||||
max(maximumTextureSize, SAFE_TEXTURE_LIMIT)
|
||||
}
|
||||
|
||||
const val SAFE_TEXTURE_LIMIT: Int = 2048
|
||||
|
||||
val CUSTOM_TEXTURE_LIMIT_OPTIONS: List<Int> by lazy {
|
||||
val steps = DEVICE_TEXTURE_LIMIT / MULTIPLIER
|
||||
buildList(steps) {
|
||||
add(DEVICE_TEXTURE_LIMIT)
|
||||
for (step in steps downTo 2) {
|
||||
val value = step * MULTIPLIER
|
||||
if (value >= DEVICE_TEXTURE_LIMIT) continue
|
||||
add(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val MULTIPLIER: Int = 1024
|
||||
@@ -21,13 +21,11 @@ import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.graphics.get
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import ca.mpreg.imagedecoder.ImageDecoder
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||
import logcat.LogPriority
|
||||
import okio.Buffer
|
||||
import okio.BufferedSource
|
||||
import tachiyomi.decoder.Format
|
||||
import tachiyomi.decoder.ImageDecoder
|
||||
import java.io.InputStream
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
@@ -49,17 +47,19 @@ object ImageUtil {
|
||||
|
||||
fun findImageType(stream: InputStream): ImageType? {
|
||||
return try {
|
||||
when (getImageType(stream)?.format) {
|
||||
Format.Avif -> ImageType.AVIF
|
||||
Format.Gif -> ImageType.GIF
|
||||
Format.Heif -> ImageType.HEIF
|
||||
Format.Jpeg -> ImageType.JPEG
|
||||
Format.Jxl -> ImageType.JXL
|
||||
Format.Png -> ImageType.PNG
|
||||
Format.Webp -> ImageType.WEBP
|
||||
val decoder = ImageDecoder.new(stream)
|
||||
when (decoder.format) {
|
||||
"jpeg" -> ImageType.JPEG
|
||||
"png" -> ImageType.PNG
|
||||
"webp" -> ImageType.WEBP
|
||||
"gif" -> ImageType.GIF
|
||||
"heif" -> ImageType.HEIF
|
||||
"jxl" -> ImageType.JXL
|
||||
"jp2" -> ImageType.JP2
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "findImageType: ${e.message}" }
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -71,38 +71,23 @@ object ImageUtil {
|
||||
|
||||
fun isAnimatedAndSupported(source: BufferedSource): Boolean {
|
||||
return try {
|
||||
val type = getImageType(source.peek().inputStream()) ?: return false
|
||||
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
||||
when (type.format) {
|
||||
Format.Gif -> true
|
||||
// Animated WebP on Android 9+
|
||||
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
// Animated Heif on Android 11+
|
||||
Format.Heif -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
val type = findImageType(source.peek().inputStream()) ?: return false
|
||||
when (type) {
|
||||
ImageType.GIF -> true
|
||||
ImageType.WEBP, ImageType.HEIF -> {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return false
|
||||
val decoder = ImageDecoder.new(source.peek().inputStream())
|
||||
decoder.pages > 1
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "isAnimatedAndSupported: ${e.message}" }
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageType(stream: InputStream): tachiyomi.decoder.ImageType? {
|
||||
val bytes = ByteArray(32)
|
||||
|
||||
val length = if (stream.markSupported()) {
|
||||
stream.mark(bytes.size)
|
||||
stream.read(bytes, 0, bytes.size).also { stream.reset() }
|
||||
} else {
|
||||
stream.read(bytes, 0, bytes.size)
|
||||
}
|
||||
|
||||
if (length == -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
return ImageDecoder.findType(bytes)
|
||||
}
|
||||
|
||||
enum class ImageType(val mime: String, val extension: String) {
|
||||
AVIF("image/avif", "avif"),
|
||||
GIF("image/gif", "gif"),
|
||||
@@ -111,6 +96,8 @@ object ImageUtil {
|
||||
JXL("image/jxl", "jxl"),
|
||||
PNG("image/png", "png"),
|
||||
WEBP("image/webp", "webp"),
|
||||
JP2("image/jp2", "jp2"),
|
||||
JPX("image/jpx", "jpx"),
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,30 +299,23 @@ object ImageUtil {
|
||||
val bottomOffset = topOffset + splitHeight
|
||||
}
|
||||
|
||||
fun canUseHardwareBitmap(bitmap: Bitmap): Boolean {
|
||||
return canUseHardwareBitmap(bitmap.width, bitmap.height)
|
||||
}
|
||||
|
||||
fun canUseHardwareBitmap(imageSource: BufferedSource): Boolean {
|
||||
return with(extractImageOptions(imageSource)) {
|
||||
canUseHardwareBitmap(outWidth, outHeight)
|
||||
}
|
||||
}
|
||||
|
||||
var hardwareBitmapThreshold: Int = GLUtil.SAFE_TEXTURE_LIMIT
|
||||
|
||||
private fun canUseHardwareBitmap(width: Int, height: Int): Boolean {
|
||||
if (HARDWARE_BITMAP_UNSUPPORTED) return false
|
||||
return maxOf(width, height) <= hardwareBitmapThreshold
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm for determining what background to accompany a comic/manga page
|
||||
*/
|
||||
fun chooseBackground(context: Context, imageStream: InputStream): Drawable {
|
||||
val decoder = ImageDecoder.newInstance(imageStream)
|
||||
val image = decoder?.decode()
|
||||
decoder?.recycle()
|
||||
val decoder = try {
|
||||
ImageDecoder.new(imageStream)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) { "chooseBackground: ${e.message}" }
|
||||
null
|
||||
}
|
||||
val result = decoder?.decode()
|
||||
val image = result?.let {
|
||||
createBitmap(it.width, it.height).also { bitmap ->
|
||||
it.image.rewind()
|
||||
bitmap.copyPixelsFromBuffer(it.image)
|
||||
}
|
||||
}
|
||||
|
||||
val whiteColor = Color.WHITE
|
||||
if (image == null) return whiteColor.toDrawable()
|
||||
@@ -575,121 +555,6 @@ object ImageUtil {
|
||||
}
|
||||
|
||||
private val optimalImageHeight = getDisplayMaxHeightInPx * 2
|
||||
|
||||
/**
|
||||
* Taken from Coil
|
||||
* (https://github.com/coil-kt/coil/blob/1674d3516f061aeacbe749a435b1924f9648fd41/coil-core/src/androidMain/kotlin/coil3/util/hardwareBitmaps.kt)
|
||||
* ---
|
||||
* Maintains a list of devices with broken/incomplete/unstable hardware bitmap implementations.
|
||||
*
|
||||
* Model names are retrieved from
|
||||
* [Google's official device list](https://support.google.com/googleplay/answer/1727131?hl=en).
|
||||
*
|
||||
*/
|
||||
val HARDWARE_BITMAP_UNSUPPORTED = when (Build.VERSION.SDK_INT) {
|
||||
26 -> run {
|
||||
val model = Build.MODEL ?: return@run false
|
||||
|
||||
// Samsung Galaxy (ALL)
|
||||
if (model.removePrefix("SAMSUNG-").startsWith("SM-")) return@run true
|
||||
|
||||
val device = Build.DEVICE ?: return@run false
|
||||
|
||||
return@run device in arrayOf(
|
||||
"nora", "nora_8917", "nora_8917_n", // Moto E5
|
||||
"james", "rjames_f", "rjames_go", "pettyl", // Moto E5 Play
|
||||
"hannah", "ahannah", "rhannah", // Moto E5 Plus
|
||||
|
||||
"ali", "ali_n", // Moto G6
|
||||
"aljeter", "aljeter_n", "jeter", // Moto G6 Play
|
||||
"evert", "evert_n", "evert_nt", // Moto G6 Plus
|
||||
|
||||
"G3112", "G3116", "G3121", "G3123", "G3125", // Xperia XA1
|
||||
"G3412", "G3416", "G3421", "G3423", "G3426", // Xperia XA1 Plus
|
||||
"G3212", "G3221", "G3223", "G3226", // Xperia XA1 Ultra
|
||||
|
||||
"BV6800Pro", // BlackView BV6800Pro
|
||||
"CatS41", // Cat S41
|
||||
"Hi9Pro", // CHUWI Hi9 Pro
|
||||
"manning", // Lenovo K8 Note
|
||||
"N5702L", // NUU Mobile G3
|
||||
)
|
||||
}
|
||||
|
||||
27 -> run {
|
||||
val device = Build.DEVICE ?: return@run false
|
||||
|
||||
return@run device in arrayOf(
|
||||
"mcv1s", // LG Tribute Empire
|
||||
"mcv3", // LG K11
|
||||
"mcv5a", // LG Q7
|
||||
"mcv7a", // LG Stylo 4
|
||||
|
||||
"A30ATMO", // T-Mobile REVVL 2
|
||||
"A70AXLTMO", // T-Mobile REVVL 2 PLUS
|
||||
|
||||
"A3A_8_4G_TMO", // Alcatel 9027W
|
||||
"Edison_CKT", // Alcatel ONYX
|
||||
"EDISON_TF", // Alcatel TCL XL2
|
||||
"FERMI_TF", // Alcatel A501DL
|
||||
"U50A_ATT", // Alcatel TETRA
|
||||
"U50A_PLUS_ATT", // Alcatel 5059R
|
||||
"U50A_PLUS_TF", // Alcatel TCL LX
|
||||
"U50APLUSTMO", // Alcatel 5059Z
|
||||
"U5A_PLUS_4G", // Alcatel 1X
|
||||
|
||||
"RCT6513W87DK5e", // RCA Galileo Pro
|
||||
"RCT6873W42BMF9A", // RCA Voyager
|
||||
"RCT6A03W13", // RCA 10 Viking
|
||||
"RCT6B03W12", // RCA Atlas 10 Pro
|
||||
"RCT6B03W13", // RCA Atlas 10 Pro+
|
||||
"RCT6T06E13", // RCA Artemis 10
|
||||
|
||||
"A3_Pro", // Umidigi A3 Pro
|
||||
"One", // Umidigi One
|
||||
"One_Max", // Umidigi One Max
|
||||
"One_Pro", // Umidigi One Pro
|
||||
"Z2", // Umidigi Z2
|
||||
"Z2_PRO", // Umidigi Z2 Pro
|
||||
|
||||
"Armor_3", // Ulefone Armor 3
|
||||
"Armor_6", // Ulefone Armor 6
|
||||
|
||||
"Blackview", // Blackview BV6000
|
||||
"BV9500", // Blackview BV9500
|
||||
"BV9500Pro", // Blackview BV9500Pro
|
||||
|
||||
"A6L-C", // Nuu A6L-C
|
||||
"N5002LA", // Nuu A7L
|
||||
"N5501LA", // Nuu A5L
|
||||
|
||||
"Power_2_Pro", // Leagoo Power 2 Pro
|
||||
"Power_5", // Leagoo Power 5
|
||||
"Z9", // Leagoo Z9
|
||||
|
||||
"V0310WW", // Blu VIVO VI+
|
||||
"V0330WW", // Blu VIVO XI
|
||||
|
||||
"A3", // BenQ A3
|
||||
"ASUS_X018_4", // Asus ZenFone Max Plus M1 (ZB570TL)
|
||||
"C210AE", // Wiko Life
|
||||
"fireball", // DROID Incredible 4G LTE
|
||||
"ILA_X1", // iLA X1
|
||||
"Infinix-X605_sprout", // Infinix NOTE 5 Stylus
|
||||
"j7maxlte", // Samsung Galaxy J7 Max
|
||||
"KING_KONG_3", // Cubot King Kong 3
|
||||
"M10500", // Packard Bell M10500
|
||||
"S70", // Altice ALTICE S70
|
||||
"S80Lite", // Doogee S80Lite
|
||||
"SGINO6", // SGiNO 6
|
||||
"st18c10bnn", // Barnes and Noble BNTV650
|
||||
"TECNO-CA8", // Tecno CAMON X Pro,
|
||||
"SHIFT6m", // SHIFT 6m
|
||||
)
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val getDisplayMaxHeightInPx: Int
|
||||
|
||||
Reference in New Issue
Block a user