Display animated webp whenever possible

Support AVIF and HEIF images
Handle HEIF images
Using new decoder

Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
Jays2Kings 2021-07-04 14:37:37 -04:00
parent a8a23c153d
commit 756f285675
4 changed files with 61 additions and 37 deletions

View File

@ -89,7 +89,10 @@ android {
dependencies { dependencies {
// Modified dependencies // Modified dependencies
implementation("com.github.jays2kings:subsampling-scale-image-view:dfd3e43") implementation("com.github.jays2kings:subsampling-scale-image-view:dfd3e43") {
exclude(module = "image-decoder")
}
implementation("com.github.tachiyomiorg:image-decoder:0e91111")
// Source models and interfaces from Tachiyomi 1.x // Source models and interfaces from Tachiyomi 1.x
implementation("tachiyomi.sourceapi:source-api:1.1") implementation("tachiyomi.sourceapi:source-api:1.1")

View File

@ -349,8 +349,8 @@ class PagerPageHolder(
val stream2 = if (extraPage != null) streamFn2?.invoke()?.buffered(16) else null val stream2 = if (extraPage != null) streamFn2?.invoke()?.buffered(16) else null
openStream = this@PagerPageHolder.mergePages(stream, stream2) openStream = this@PagerPageHolder.mergePages(stream, stream2)
ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF || ImageUtil.isAnimatedAndSupported(stream) ||
if (stream2 != null) ImageUtil.findImageType(stream2) == ImageUtil.ImageType.GIF else false if (stream2 != null) ImageUtil.isAnimatedAndSupported(stream2) else false
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -654,11 +654,11 @@ class PagerPageHolder(
private fun mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream { private fun mergePages(imageStream: InputStream, imageStream2: InputStream?): InputStream {
imageStream2 ?: return imageStream imageStream2 ?: return imageStream
if (page.fullPage) return imageStream if (page.fullPage) return imageStream
if (ImageUtil.findImageType(imageStream) == ImageUtil.ImageType.GIF) { if (ImageUtil.isAnimatedAndSupported(imageStream)) {
page.fullPage = true page.fullPage = true
skipExtra = true skipExtra = true
return imageStream return imageStream
} else if (ImageUtil.findImageType(imageStream2) == ImageUtil.ImageType.GIF) { } else if (ImageUtil.isAnimatedAndSupported(imageStream)) {
page.isolatedPage = true page.isolatedPage = true
extraPage?.fullPage = true extraPage?.fullPage = true
skipExtra = true skipExtra = true

View File

@ -280,7 +280,7 @@ class WebtoonPageHolder(
val stream = streamFn().buffered(16) val stream = streamFn().buffered(16)
openStream = stream openStream = stream
ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF ImageUtil.isAnimatedAndSupported(stream)
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -9,8 +9,11 @@ import android.graphics.Rect
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Build
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.decoder.Format
import tachiyomi.decoder.ImageDecoder
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
@ -35,7 +38,46 @@ object ImageUtil {
fun findImageType(stream: InputStream): ImageType? { fun findImageType(stream: InputStream): ImageType? {
try { try {
val bytes = ByteArray(8) return when (getImageType(stream)?.format) {
Format.Avif -> ImageType.AVIF
Format.Gif -> ImageType.GIF
Format.Heif -> ImageType.HEIF
Format.Jpeg -> ImageType.JPEG
Format.Png -> ImageType.PNG
Format.Webp -> ImageType.WEBP
else -> null
}
} catch (e: Exception) {
}
return null
}
fun isAnimatedAndSupported(stream: InputStream): Boolean {
try {
val type = getImageType(stream) ?: return false
return when (type.format) {
Format.Gif -> true
// Coil supports animated WebP on Android 9.0+
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
else -> false
}
} catch (e: Exception) {
}
return false
}
enum class ImageType(val mime: String, val extension: String) {
AVIF("image/avif", "avif"),
GIF("image/gif", "gif"),
HEIF("image/heif", "heif"),
JPEG("image/jpeg", "jpg"),
PNG("image/png", "png"),
WEBP("image/webp", "webp"),
}
private fun getImageType(stream: InputStream): tachiyomi.decoder.ImageType? {
val bytes = ByteArray(32)
val length = if (stream.markSupported()) { val length = if (stream.markSupported()) {
stream.mark(bytes.size) stream.mark(bytes.size)
@ -48,21 +90,7 @@ object ImageUtil {
return null return null
} }
if (bytes.compareWith(charByteArrayOf(0xFF, 0xD8, 0xFF))) { return ImageDecoder.findType(bytes)
return ImageType.JPG
}
if (bytes.compareWith(charByteArrayOf(0x89, 0x50, 0x4E, 0x47))) {
return ImageType.PNG
}
if (bytes.compareWith("GIF8".toByteArray())) {
return ImageType.GIF
}
if (bytes.compareWith("RIFF".toByteArray())) {
return ImageType.WEBP
}
} catch (e: Exception) {
}
return null
} }
fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable { fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable {
@ -325,11 +353,4 @@ object ImageUtil {
} }
} }
} }
enum class ImageType(val mime: String, val extension: String) {
JPG("image/jpeg", "jpg"),
PNG("image/png", "png"),
GIF("image/gif", "gif"),
WEBP("image/webp", "webp")
}
} }