Skip to content

Commit

Permalink
Support more image types (#700)
Browse files Browse the repository at this point in the history
  • Loading branch information
Syer10 authored Oct 5, 2023
1 parent ef0a6f5 commit a9987e6
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 10 deletions.
5 changes: 4 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@
*.pyc binary
*.swp binary
*.pdf binary
*.exe binary
*.exe binary
*.avif binary
*.heif binary
*.jxl binary
Original file line number Diff line number Diff line change
Expand Up @@ -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
* 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.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.WEBP
import java.io.InputStream
Expand All @@ -31,7 +34,7 @@ object ImageUtil {

fun findImageType(stream: InputStream): ImageType? {
try {
val bytes = ByteArray(8)
val bytes = ByteArray(12)

val length = if (stream.markSupported()) {
stream.mark(bytes.size)
Expand All @@ -45,7 +48,7 @@ object ImageUtil {
}

if (bytes.compareWith(charByteArrayOf(0xFF, 0xD8, 0xFF))) {
return JPG
return JPEG
}
if (bytes.compareWith(charByteArrayOf(0x89, 0x50, 0x4E, 0x47))) {
return PNG
Expand All @@ -56,11 +59,88 @@ object ImageUtil {
if (bytes.compareWith("RIFF".toByteArray())) {
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
}

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 {
return magic.indices.none { this[it] != magic[it] }
}
Expand All @@ -73,10 +153,13 @@ object ImageUtil {
}
}

enum class ImageType(val mime: String) {
JPG("image/jpeg"),
PNG("image/png"),
GIF("image/gif"),
WEBP("image/webp")
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"),
JXL("image/jxl", "jxl"),
PNG("image/png", "png"),
WEBP("image/webp", "webp")
}
}
25 changes: 25 additions & 0 deletions server/src/test/kotlin/suwayomi/tachidesk/ImageUtilTest.kt
Original file line number Diff line number Diff line change
@@ -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 added server/src/test/resources/dice.jxl
Binary file not shown.
Binary file not shown.
Binary file added server/src/test/resources/sample1.heif
Binary file not shown.

0 comments on commit a9987e6

Please sign in to comment.