From eada1bc023fc3528f3cf8dfcfe5551d1f907ef1b Mon Sep 17 00:00:00 2001 From: tarsin Date: Sat, 14 Jan 2023 19:56:55 +0800 Subject: [PATCH] Avoid unnecessary overhead --- app/src/main/cpp/CMakeLists.txt | 2 +- app/src/main/cpp/gifUtils.c | 11 ++++ app/src/main/java/coil/decode/DecodeUtils.kt | 66 -------------------- app/src/main/java/com/hippo/Native.kt | 3 + app/src/main/java/com/hippo/image/Image.kt | 37 ++++++----- 5 files changed, 36 insertions(+), 83 deletions(-) create mode 100644 app/src/main/cpp/gifUtils.c delete mode 100644 app/src/main/java/coil/decode/DecodeUtils.kt diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 42cb3e2ef..ac453646e 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -45,6 +45,6 @@ SET(LIBARCHIVE_CUSTOM_LIBS "liblzma" "nettle") add_subdirectory(libarchive) # Build and link our app's native lib -add_library(${PROJECT_NAME} SHARED archive.c fdutils.c) +add_library(${PROJECT_NAME} SHARED archive.c fdutils.c gifUtils.c) target_include_directories(${PROJECT_NAME} PUBLIC libarchive/libarchive) target_link_libraries(${PROJECT_NAME} archive_static log -Wl,--gc-sections) diff --git a/app/src/main/cpp/gifUtils.c b/app/src/main/cpp/gifUtils.c new file mode 100644 index 000000000..5e5899648 --- /dev/null +++ b/app/src/main/cpp/gifUtils.c @@ -0,0 +1,11 @@ +#include +#include + +#define GIF_HEADER_87A "GIF87a" +#define GIF_HEADER_89A "GIF89a" + +JNIEXPORT jboolean JNICALL +Java_com_hippo_Native_isGif(JNIEnv *env, jobject thiz, jobject buffer) { + void *addr = (*env)->GetDirectBufferAddress(env, buffer); + return !memcmp(addr, GIF_HEADER_87A, 6) || !memcmp(addr, GIF_HEADER_89A, 6); +} diff --git a/app/src/main/java/coil/decode/DecodeUtils.kt b/app/src/main/java/coil/decode/DecodeUtils.kt deleted file mode 100644 index 0d530af66..000000000 --- a/app/src/main/java/coil/decode/DecodeUtils.kt +++ /dev/null @@ -1,66 +0,0 @@ -@file:JvmName("GifDecodeUtils") - -package coil.decode - -import okio.BufferedSource -import okio.ByteString.Companion.encodeUtf8 -import kotlin.experimental.and - -// https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp -private val GIF_HEADER_87A = "GIF87a".encodeUtf8() -private val GIF_HEADER_89A = "GIF89a".encodeUtf8() - -// https://developers.google.com/speed/webp/docs/riff_container -private val WEBP_HEADER_RIFF = "RIFF".encodeUtf8() -private val WEBP_HEADER_WEBP = "WEBP".encodeUtf8() -private val WEBP_HEADER_VPX8 = "VP8X".encodeUtf8() - -// https://nokiatech.github.io/heif/technical.html -private val HEIF_HEADER_FTYP = "ftyp".encodeUtf8() -private val HEIF_HEADER_MSF1 = "msf1".encodeUtf8() -private val HEIF_HEADER_HEVC = "hevc".encodeUtf8() -private val HEIF_HEADER_HEVX = "hevx".encodeUtf8() - -/** - * Return 'true' if the [source] contains a GIF image. The [source] is not consumed. - */ -fun DecodeUtils.isGif(source: BufferedSource): Boolean { - return source.rangeEquals(0, GIF_HEADER_89A) || - source.rangeEquals(0, GIF_HEADER_87A) -} - -/** - * Return 'true' if the [source] contains a WebP image. The [source] is not consumed. - */ -fun DecodeUtils.isWebP(source: BufferedSource): Boolean { - return source.rangeEquals(0, WEBP_HEADER_RIFF) && - source.rangeEquals(8, WEBP_HEADER_WEBP) -} - -/** - * Return 'true' if the [source] contains an animated WebP image. The [source] is not consumed. - */ -fun DecodeUtils.isAnimatedWebP(source: BufferedSource): Boolean { - return isWebP(source) && - source.rangeEquals(12, WEBP_HEADER_VPX8) && - source.request(17) && - (source.buffer[16] and 0b00000010) > 0 -} - -/** - * Return 'true' if the [source] contains an HEIF image. The [source] is not consumed. - */ -fun DecodeUtils.isHeif(source: BufferedSource): Boolean { - return source.rangeEquals(4, HEIF_HEADER_FTYP) -} - -/** - * Return 'true' if the [source] contains an animated HEIF image sequence. The [source] is not - * consumed. - */ -fun DecodeUtils.isAnimatedHeif(source: BufferedSource): Boolean { - return isHeif(source) && - (source.rangeEquals(8, HEIF_HEADER_MSF1) || - source.rangeEquals(8, HEIF_HEADER_HEVC) || - source.rangeEquals(8, HEIF_HEADER_HEVX)) -} diff --git a/app/src/main/java/com/hippo/Native.kt b/app/src/main/java/com/hippo/Native.kt index 66c98888d..cf2e35d72 100644 --- a/app/src/main/java/com/hippo/Native.kt +++ b/app/src/main/java/com/hippo/Native.kt @@ -18,6 +18,7 @@ package com.hippo import java.io.FileDescriptor +import java.nio.ByteBuffer object Native { fun initialize() { @@ -26,4 +27,6 @@ object Native { @JvmStatic external fun getFd(fd: FileDescriptor?): Int + + external fun isGif(buffer: ByteBuffer): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/hippo/image/Image.kt b/app/src/main/java/com/hippo/image/Image.kt index f2fb26eff..36e52b750 100644 --- a/app/src/main/java/com/hippo/image/Image.kt +++ b/app/src/main/java/com/hippo/image/Image.kt @@ -25,9 +25,8 @@ import android.graphics.ImageDecoder.Source import android.graphics.drawable.Animatable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable -import coil.decode.DecodeUtils import coil.decode.FrameDelayRewritingSource -import coil.decode.isGif +import com.hippo.Native import com.hippo.UriArchiveAccessor import com.hippo.ehviewer.EhApplication import okio.Buffer @@ -87,35 +86,41 @@ class Image private constructor(source: Source, private val byteBuffer: ByteBuff @Throws(DecodeException::class) @JvmStatic fun decode(stream: FileInputStream): Image { - val source = stream.source().buffer() - val buffer = if (DecodeUtils.isGif(source)) { - rewriteSource(source) + val buffer = stream.channel.map( + FileChannel.MapMode.READ_ONLY, 0, + stream.available().toLong() + ) + val source = if (checkIsGif(buffer)) { + rewriteSource(stream.source().buffer()) } else { - stream.channel.map( - FileChannel.MapMode.READ_ONLY, 0, - stream.available().toLong() - ) + buffer } - val src = ImageDecoder.createSource(buffer) + val src = ImageDecoder.createSource(source) return Image(src) } @Throws(DecodeException::class) @JvmStatic fun decode(buffer: ByteBuffer): Image { - val source = Buffer().apply { write(buffer) } - val byteBuffer = if (DecodeUtils.isGif(source)) { - rewriteSource(source) + return if (checkIsGif(buffer)) { + val rewritten = rewriteSource(Buffer().apply { + write(buffer) + UriArchiveAccessor.releaseByteBuffer(buffer) + }) + Image(ImageDecoder.createSource(rewritten)) } else { - buffer.position(0) as ByteBuffer + Image(ImageDecoder.createSource(buffer), buffer) } - val src = ImageDecoder.createSource(byteBuffer) - return Image(src, buffer) } private fun rewriteSource(source: BufferedSource): ByteBuffer { val bufferedSource = FrameDelayRewritingSource(source).buffer() return ByteBuffer.wrap(bufferedSource.use { it.readByteArray() }) } + + private fun checkIsGif(buffer: ByteBuffer): Boolean { + check(buffer.isDirect) + return Native.isGif(buffer) + } } } \ No newline at end of file