Handle HEIF images (partly addresses #4756)

This commit is contained in:
arkon 2021-06-04 23:26:37 -04:00
parent 64f95af3e5
commit 74381ef59e

View File

@ -18,6 +18,7 @@ import androidx.core.graphics.green
import androidx.core.graphics.red import androidx.core.graphics.red
import tachiyomi.decoder.Format import tachiyomi.decoder.Format
import tachiyomi.decoder.ImageDecoder import tachiyomi.decoder.ImageDecoder
import tachiyomi.decoder.ImageType
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
@ -41,30 +42,14 @@ object ImageUtil {
fun findImageType(stream: InputStream): ImageType? { fun findImageType(stream: InputStream): ImageType? {
try { try {
val bytes = ByteArray(8) return when (getImageType(stream)?.format) {
// TODO: image-decoder library currently doesn't detect this
val length = if (stream.markSupported()) { // Format.Avif -> ImageType.AVIF
stream.mark(bytes.size) Format.Gif -> ImageType.GIF
stream.read(bytes, 0, bytes.size).also { stream.reset() } Format.Heif -> ImageType.HEIF
} else { Format.Jpeg -> ImageType.JPEG
stream.read(bytes, 0, bytes.size) Format.Png -> ImageType.PNG
} Format.Webp -> ImageType.WEBP
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) {
} }
@ -73,20 +58,7 @@ object ImageUtil {
fun isAnimatedAndSupported(stream: InputStream): Boolean { fun isAnimatedAndSupported(stream: InputStream): Boolean {
try { try {
val bytes = ByteArray(32) val type = getImageType(stream) ?: return false
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 false
}
val type = ImageDecoder.findType(bytes) ?: return false
return when (type.format) { return when (type.format) {
Format.Gif -> true Format.Gif -> true
// Coil supports animated WebP on Android 9.0+ // Coil supports animated WebP on Android 9.0+
@ -99,23 +71,30 @@ object ImageUtil {
return false return false
} }
private fun ByteArray.compareWith(magic: ByteArray): Boolean { private fun getImageType(stream: InputStream): tachiyomi.decoder.ImageType? {
return magic.indices.none { this[it] != magic[it] } 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)
} }
private fun charByteArrayOf(vararg bytes: Int): ByteArray { if (length == -1) {
return ByteArray(bytes.size).apply { return null
for (i in bytes.indices) {
set(i, bytes[i].toByte())
}
} }
return ImageDecoder.findType(bytes)
} }
enum class ImageType(val mime: String, val extension: String) { enum class ImageType(val mime: String, val extension: String) {
JPG("image/jpeg", "jpg"), // AVIF("image/avif", "avif"),
PNG("image/png", "png"),
GIF("image/gif", "gif"), GIF("image/gif", "gif"),
WEBP("image/webp", "webp") HEIF("image/heif", "heif"),
JPEG("image/jpeg", "jpg"),
PNG("image/png", "png"),
WEBP("image/webp", "webp"),
} }
/** /**