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,36 +38,61 @@ 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
val length = if (stream.markSupported()) { Format.Gif -> ImageType.GIF
stream.mark(bytes.size) Format.Heif -> ImageType.HEIF
stream.read(bytes, 0, bytes.size).also { stream.reset() } Format.Jpeg -> ImageType.JPEG
} else { Format.Png -> ImageType.PNG
stream.read(bytes, 0, bytes.size) Format.Webp -> ImageType.WEBP
} else -> null
if (length == -1) {
return null
}
if (bytes.compareWith(charByteArrayOf(0xFF, 0xD8, 0xFF))) {
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) { } catch (e: Exception) {
} }
return null 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()) {
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)
}
fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable { fun autoSetBackground(image: Bitmap?, alwaysUseWhite: Boolean, context: Context): Drawable {
val backgroundColor = if (alwaysUseWhite) Color.WHITE else { val backgroundColor = if (alwaysUseWhite) Color.WHITE else {
context.getResourceColor(R.attr.readerBackground) context.getResourceColor(R.attr.readerBackground)
@ -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")
}
} }