mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 06:42:07 +01:00
Support more image types (#700)
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -26,3 +26,6 @@
|
|||||||
*.swp binary
|
*.swp binary
|
||||||
*.pdf binary
|
*.pdf binary
|
||||||
*.exe binary
|
*.exe binary
|
||||||
|
*.avif binary
|
||||||
|
*.heif binary
|
||||||
|
*.jxl binary
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
server/src/test/kotlin/suwayomi/tachidesk/ImageUtilTest.kt
Normal file
25
server/src/test/kotlin/suwayomi/tachidesk/ImageUtilTest.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
server/src/test/resources/dice.jxl
Normal file
BIN
server/src/test/resources/dice.jxl
Normal file
Binary file not shown.
BIN
server/src/test/resources/fox.profile0.8bpc.yuv420.avif
Normal file
BIN
server/src/test/resources/fox.profile0.8bpc.yuv420.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
BIN
server/src/test/resources/sample1.heif
Normal file
BIN
server/src/test/resources/sample1.heif
Normal file
Binary file not shown.
Reference in New Issue
Block a user