Do not modify the original file when rewriting GIF

Make it a decode-time thing.
This commit is contained in:
FooIbar committed 2023-10-14 15:25:33 +08:00
1 parent 78f61b2396
commit d99d9008dd
4 files changed
+65 -57

No files matched your search

+11 -5
View File
@@ -49,13 +49,19 @@ Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource(JNIEnv *env, jclass clazz
doRewrite(addr, size);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource2(JNIEnv *env, jclass clazz, jint fd) {
JNIEXPORT jobject JNICALL
Java_com_hippo_ehviewer_image_ImageKt_mmap(JNIEnv *env, jclass clazz, jint fd) {
struct stat64 st;
fstat64(fd, &st);
size_t size = st.st_size;
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) return;
doRewrite(addr, size);
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) return NULL;
return (*env)->NewDirectByteBuffer(env, addr, size);
}
JNIEXPORT void JNICALL
Java_com_hippo_ehviewer_image_ImageKt_munmap(JNIEnv *env, jclass clazz, jobject buffer) {
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
munmap(addr, size);
}
@@ -21,9 +21,7 @@ import android.os.ParcelFileDescriptor
import android.util.Log
import com.hippo.ehviewer.Settings.archivePasswds
import com.hippo.ehviewer.image.Image
import com.hippo.ehviewer.image.rewriteGifSource
import com.hippo.ehviewer.util.FileUtils
import com.hippo.ehviewer.util.isAtLeastU
import com.hippo.unifile.UniFile
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
@@ -102,12 +100,8 @@ class ArchivePageLoader(context: Context, private val uri: Uri, passwdFlow: Flow
val buffer = extractToByteBuffer(index)
buffer ?: return
check(buffer.isDirect)
if (!isAtLeastU) {
rewriteGifSource(buffer)
}
val src = object : Image.ByteBufferSource {
override val source
get() = buffer!!
override val source: ByteBuffer = buffer
override fun close() {
releaseByteBuffer(buffer)
@@ -33,13 +33,13 @@ import androidx.annotation.RequiresApi
import coil.decode.BitmapFactoryDecoder
import coil.decode.DecodeUtils
import coil.decode.ImageSource
import coil.decode.isGif
import coil.request.Options
import coil.size.Scale
import coil.size.Size
import com.hippo.ehviewer.R
import com.hippo.ehviewer.util.isAtLeastO
import com.hippo.ehviewer.util.isAtLeastP
import com.hippo.ehviewer.util.isAtLeastU
import com.hippo.unifile.UniFile
import com.hippo.unifile.openInputStream
import okio.buffer
@@ -48,8 +48,8 @@ import splitties.init.appCtx
import java.nio.ByteBuffer
import kotlin.math.min
class Image private constructor(private val src: AutoCloseable) {
var mObtainedDrawable: Drawable? = null
class Image private constructor(drawable: Drawable, private val src: AutoCloseable) {
var mObtainedDrawable: Drawable? = drawable
private set
val size: Int
@@ -92,28 +92,58 @@ class Image private constructor(private val src: AutoCloseable) {
@RequiresApi(Build.VERSION_CODES.O)
lateinit var colorSpace: ColorSpace
suspend fun decode(src: UniFileSource): Image? {
suspend fun decode(src: AutoCloseable): Image? {
return runCatching {
Image(src).apply {
mObtainedDrawable = if (isAtLeastP) {
decodeDrawable(src.source.imageSource)
} else {
ImageSource(src.source.openInputStream().source().buffer(), appCtx).use {
if (DecodeUtils.isGif(it.source())) {
TODO("Unsupported")
when (src) {
is UniFileSource -> {
if (isAtLeastP) {
if (!isAtLeastU) {
val buffer = src.source.openFileDescriptor("rw").use {
mmap(it.fd)!!
}
val source = object : ByteBufferSource {
override val source = buffer
override fun close() {
munmap(buffer)
src.close()
}
}
decode(source)
} else {
val options = Options(
appCtx,
colorSpace = if (isAtLeastO) colorSpace else null,
size = Size(targetWidth, targetHeight),
scale = Scale.FILL,
)
val drawable = decodeDrawable(src.source.imageSource)
if (drawable !is Animatable) src.close()
Image(drawable, src)
}
} else {
val options = Options(
appCtx,
colorSpace = if (isAtLeastO) colorSpace else null,
size = Size(targetWidth, targetHeight),
scale = Scale.FILL,
)
val drawable = ImageSource(src.source.openInputStream().source().buffer(), appCtx).use {
BitmapFactoryDecoder(it, options).decode().drawable
}
src.close()
Image(drawable, src)
}
}.also {
if (it !is Animatable) src.close()
}
is ByteBufferSource -> {
if (isAtLeastP) {
if (!isAtLeastU) {
rewriteGifSource(src.source)
}
val source = ImageDecoder.createSource(src.source)
val drawable = decodeDrawable(source)
if (drawable !is Animatable) src.close()
Image(drawable, src)
} else {
TODO("Unsupported")
}
}
else -> TODO("Unsupported")
}
}.onFailure {
src.close()
@@ -121,22 +151,6 @@ class Image private constructor(private val src: AutoCloseable) {
}.getOrNull()
}
fun decode(src: ByteBufferSource) = runCatching {
Image(src).apply {
mObtainedDrawable = if (isAtLeastP) {
val source = ImageDecoder.createSource(src.source)
decodeDrawable(source)
} else {
TODO("Unsupported")
}.also {
if (it !is Animatable) src.close()
}
}
}.onFailure {
src.close()
it.printStackTrace()
}.getOrNull()
@RequiresApi(Build.VERSION_CODES.P)
private fun decodeDrawable(src: Source) = ImageDecoder.decodeDrawable(src) { decoder, info, _ ->
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
@@ -184,4 +198,5 @@ class Image private constructor(private val src: AutoCloseable) {
}
external fun rewriteGifSource(buffer: ByteBuffer)
external fun rewriteGifSource2(fd: Int)
external fun mmap(fd: Int): ByteBuffer?
external fun munmap(buffer: ByteBuffer)
@@ -31,9 +31,7 @@ import com.hippo.ehviewer.cronet.execute
import com.hippo.ehviewer.download.downloadLocation
import com.hippo.ehviewer.gallery.SUPPORT_IMAGE_EXTENSIONS
import com.hippo.ehviewer.image.Image.UniFileSource
import com.hippo.ehviewer.image.rewriteGifSource2
import com.hippo.ehviewer.util.FileUtils
import com.hippo.ehviewer.util.isAtLeastU
import com.hippo.ehviewer.util.isCronetSupported
import com.hippo.ehviewer.util.sendTo
import com.hippo.unifile.UniFile
@@ -162,13 +160,8 @@ class SpiderDen(mGalleryInfo: GalleryInfo) {
}
private suspend fun saveResponseMeta(index: Int, ext: String, length: Long, fops: suspend (UniFile) -> Unit): Boolean {
suspend fun realFops(f: UniFile) = fops(f).apply {
if (ext.lowercase() == "gif" && !isAtLeastU) {
f.openFileDescriptor("rw").use { rewriteGifSource2(it.fd) }
}
}
findDownloadFileForIndex(index, ext)?.run {
realFops(this)
fops(this)
return true
}
@@ -177,7 +170,7 @@ class SpiderDen(mGalleryInfo: GalleryInfo) {
val key = getImageKey(mGid, index)
return sCache.suspendEdit(key) {
metadata.toFile().writeText(ext)
realFops(UniFile.fromFile(data.toFile())!!)
fops(UniFile.fromFile(data.toFile())!!)
}
}
return false