Support more image types (#700)

This commit is contained in:
Mitchell Syer
2023-10-04 22:02:20 -04:00
committed by GitHub
parent ef0a6f54b8
commit a9987e6ab0
6 changed files with 121 additions and 10 deletions

3
.gitattributes vendored
View File

@@ -26,3 +26,6 @@
*.swp binary *.swp binary
*.pdf binary *.pdf binary
*.exe binary *.exe binary
*.avif binary
*.heif binary
*.jxl binary

View File

@@ -7,8 +7,11 @@ package suwayomi.tachidesk.manga.impl.util.storage
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.AVIF
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.GIF import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.GIF
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.JPG import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.HEIF
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.JPEG
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.JXL
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.PNG import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.PNG
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.WEBP import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil.ImageType.WEBP
import java.io.InputStream import java.io.InputStream
@@ -31,7 +34,7 @@ object ImageUtil {
fun findImageType(stream: InputStream): ImageType? { fun findImageType(stream: InputStream): ImageType? {
try { try {
val bytes = ByteArray(8) val bytes = ByteArray(12)
val length = if (stream.markSupported()) { val length = if (stream.markSupported()) {
stream.mark(bytes.size) stream.mark(bytes.size)
@@ -45,7 +48,7 @@ object ImageUtil {
} }
if (bytes.compareWith(charByteArrayOf(0xFF, 0xD8, 0xFF))) { if (bytes.compareWith(charByteArrayOf(0xFF, 0xD8, 0xFF))) {
return JPG return JPEG
} }
if (bytes.compareWith(charByteArrayOf(0x89, 0x50, 0x4E, 0x47))) { if (bytes.compareWith(charByteArrayOf(0x89, 0x50, 0x4E, 0x47))) {
return PNG return PNG
@@ -56,11 +59,88 @@ object ImageUtil {
if (bytes.compareWith("RIFF".toByteArray())) { if (bytes.compareWith("RIFF".toByteArray())) {
return WEBP return WEBP
} }
} catch (e: Exception) { if (bytes.copyOfRange(4, 12).compareWith("ftypavif".toByteArray())) {
return AVIF
}
if (isHEIF(bytes)) {
return HEIF
}
if (bytes.compareWith(charByteArrayOf(0xFF, 0x0A))) {
return JXL
}
} catch (_: Exception) {
} }
return null return null
} }
private fun isHEIF(bytes: ByteArray): Boolean {
// ftypheic
if (bytes[4] == 0x66.toByte() &&
bytes[5] == 0x74.toByte() &&
bytes[6] == 0x79.toByte() &&
bytes[7] == 0x70.toByte() &&
bytes[8] == 0x68.toByte() &&
bytes[9] == 0x65.toByte() &&
bytes[10] == 0x69.toByte() &&
bytes[11] == 0x63.toByte()
) {
return true
}
// ftypmif1
if (bytes[4] == 0x66.toByte() &&
bytes[5] == 0x74.toByte() &&
bytes[6] == 0x79.toByte() &&
bytes[7] == 0x70.toByte() &&
bytes[8] == 0x6D.toByte() &&
bytes[9] == 0x69.toByte() &&
bytes[10] == 0x66.toByte() &&
bytes[11] == 0x31.toByte()
) {
return true
}
// ftypmsf1
if (bytes[4] == 0x66.toByte() &&
bytes[5] == 0x74.toByte() &&
bytes[6] == 0x79.toByte() &&
bytes[7] == 0x70.toByte() &&
bytes[8] == 0x6D.toByte() &&
bytes[9] == 0x73.toByte() &&
bytes[10] == 0x66.toByte() &&
bytes[11] == 0x31.toByte()
) {
return true
}
// ftypheis
if (bytes[4] == 0x66.toByte() &&
bytes[5] == 0x74.toByte() &&
bytes[6] == 0x79.toByte() &&
bytes[7] == 0x70.toByte() &&
bytes[8] == 0x68.toByte() &&
bytes[9] == 0x65.toByte() &&
bytes[10] == 0x69.toByte() &&
bytes[11] == 0x73.toByte()
) {
return true
}
// ftyphevc
if (bytes[4] == 0x66.toByte() &&
bytes[5] == 0x74.toByte() &&
bytes[6] == 0x79.toByte() &&
bytes[7] == 0x70.toByte() &&
bytes[8] == 0x68.toByte() &&
bytes[9] == 0x65.toByte() &&
bytes[10] == 0x76.toByte() &&
bytes[11] == 0x63.toByte()
) {
return true
}
return false
}
private fun ByteArray.compareWith(magic: ByteArray): Boolean { private fun ByteArray.compareWith(magic: ByteArray): Boolean {
return magic.indices.none { this[it] != magic[it] } return magic.indices.none { this[it] != magic[it] }
} }
@@ -73,10 +153,13 @@ object ImageUtil {
} }
} }
enum class ImageType(val mime: String) { enum class ImageType(val mime: String, val extension: String) {
JPG("image/jpeg"), AVIF("image/avif", "avif"),
PNG("image/png"), GIF("image/gif", "gif"),
GIF("image/gif"), HEIF("image/heif", "heif"),
WEBP("image/webp") JPEG("image/jpeg", "jpg"),
JXL("image/jxl", "jxl"),
PNG("image/png", "png"),
WEBP("image/webp", "webp")
} }
} }

View File

@@ -0,0 +1,25 @@
package suwayomi.tachidesk
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
import kotlin.test.Test
import kotlin.test.assertEquals
class ImageUtilTest {
@Test
fun jxlTest() {
val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("dice.jxl")!!)
assertEquals(ImageUtil.ImageType.JXL, type)
}
@Test
fun avifTest() {
val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("fox.profile0.8bpc.yuv420.avif")!!)
assertEquals(ImageUtil.ImageType.AVIF, type)
}
@Test
fun heifTest() {
val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("sample1.heif")!!)
assertEquals(ImageUtil.ImageType.HEIF, type)
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.