Do not modify the original file when rewriting GIF
Make it a decode-time thing.
This commit is contained in:
1 parent
78f61b2396
commit
d99d9008dd
4 files changed
+65
-57
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user