diff --git a/app/src/main/cpp/fdutils.c b/app/src/main/cpp/fdutils.c index d13b6b161..a8360c0e5 100644 --- a/app/src/main/cpp/fdutils.c +++ b/app/src/main/cpp/fdutils.c @@ -1,4 +1,13 @@ #include +#include + +#include +#include +#include + +#define LOG_TAG "mmap_utils" + +#include "ehviewer.h" JNIEXPORT jint JNICALL Java_com_hippo_Native_getFd(JNIEnv *env, jclass clazz, jobject fileDescriptor) { @@ -13,4 +22,22 @@ Java_com_hippo_Native_getFd(JNIEnv *env, jclass clazz, jobject fileDescriptor) { } return fd; -} \ No newline at end of file +} + +JNIEXPORT jobject JNICALL +Java_com_hippo_Native_mapFd(JNIEnv *env, jclass clazz, jint fd, jlong capability) { + void *addr = mmap(0, capability, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (addr != MAP_FAILED) { + return (*env)->NewDirectByteBuffer(env, addr, capability); + } else { + LOGE("%s%s", "mmap failed with error ", strerror(errno)); + return NULL; + } +} + +JNIEXPORT void JNICALL +Java_com_hippo_Native_unmapDirectByteBuffer(JNIEnv *env, jclass clazz, jobject buffer) { + void *addr = (*env)->GetDirectBufferAddress(env, buffer); + size_t size = (*env)->GetDirectBufferCapacity(env, buffer); + if (munmap(addr, size)) LOGE("munmap addr:%p, size%d failed!", addr, size); +} diff --git a/app/src/main/java/com/hippo/Native.kt b/app/src/main/java/com/hippo/Native.kt index 66c98888d..ce192a270 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,10 @@ object Native { @JvmStatic external fun getFd(fd: FileDescriptor?): Int -} \ No newline at end of file + + @JvmStatic + external fun mapFd(fd: Int, capability: Long): ByteBuffer? + + @JvmStatic + external fun unmapDirectByteBuffer(buffer: ByteBuffer) +} diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java index 74fa9d083..8693db906 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java @@ -21,16 +21,20 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.hippo.Native; +import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhCacheKeyFactory; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.gallery.PageLoader2; +import com.hippo.image.Image; import com.hippo.io.UniFileInputStreamPipe; import com.hippo.io.UniFileOutputStreamPipe; import com.hippo.streampipe.InputStreamPipe; import com.hippo.streampipe.OutputStreamPipe; +import com.hippo.unifile.RawFile; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; @@ -43,6 +47,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.Locale; import coil.disk.DiskCache; @@ -261,6 +268,7 @@ public final class SpiderDen { } return new OutputStreamPipe() { private OutputStream os = null; + @Override public void release() { editor.commit(); @@ -382,4 +390,94 @@ public final class SpiderDen { } } + @Nullable + public Image.ByteBufferSource getImageSource(int index) { + if (mMode == SpiderQueen.MODE_READ) { + String key = EhCacheKeyFactory.getImageKey(mGid, index); + if (sCache != null) { + var snapshot = sCache.get(key); + if (snapshot != null) { + try { + var file = new RandomAccessFile(snapshot.getData().toFile(), "rw"); + var chan = file.getChannel(); + var map = chan.map(FileChannel.MapMode.PRIVATE, 0, file.length()); + return new Image.ByteBufferSource() { + @NonNull + @Override + public ByteBuffer getByteBuffer() { + return map; + } + + @Override + public void close() throws Exception { + chan.close(); + file.close(); + snapshot.close(); + } + }; + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + UniFile dir = getDownloadDir(); + if (dir == null) { + return null; + } + UniFile file = findImageFile(dir, index); + if (file != null && mMode == SpiderQueen.MODE_DOWNLOAD) { + if (copyFromCacheToDownloadDir(index)) { + file = findImageFile(dir, index); + } + } + if (file instanceof RawFile rawFile) { + try { + var file2 = new RandomAccessFile(rawFile.mFile, "rw"); + var chan = file2.getChannel(); + var map = chan.map(FileChannel.MapMode.PRIVATE, 0, file2.length()); + return new Image.ByteBufferSource() { + @NonNull + @Override + public ByteBuffer getByteBuffer() { + return map; + } + + @Override + public void close() throws Exception { + chan.close(); + file2.close(); + } + }; + } catch (Throwable e) { + e.printStackTrace(); + } + } + if (file != null) { + try { + var pfd = EhApplication.getApplication().getContentResolver().openFileDescriptor(file.getUri(), "rw"); + var map = Native.mapFd(pfd.getFd(), pfd.getStatSize()); + if (map != null) { + return new Image.ByteBufferSource() { + @NonNull + @Override + public ByteBuffer getByteBuffer() { + return map; + } + + @Override + public void close() throws Exception { + Native.unmapDirectByteBuffer(map); + pfd.close(); + } + }; + } else { + pfd.close(); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + return null; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java index 6381f9701..2280556d1 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java @@ -17,7 +17,6 @@ package com.hippo.ehviewer.spider; import android.graphics.BitmapFactory; -import android.graphics.ImageDecoder; import android.os.AsyncTask; import android.os.Process; import android.text.TextUtils; @@ -1623,8 +1622,8 @@ public final class SpiderQueen implements Runnable { continue; } - InputStreamPipe pipe = mSpiderDen.openInputStreamPipe(index); - if (pipe == null) { + Image.ByteBufferSource src = mSpiderDen.getImageSource(index); + if (src == null) { resetDecodeIndex(); // Can't find the file, it might be removed from cache, // Reset it state and request it @@ -1633,35 +1632,11 @@ public final class SpiderQueen implements Runnable { continue; } - Image image = null; + Image image = Image.decode(src); String error = null; - InputStream is; - try { - is = pipe.open(); - } catch (IOException e) { - // Can't open pipe - error = GetText.getString(R.string.error_reading_failed); - is = null; - pipe.close(); - pipe.release(); - } - - if (is != null) { - try { - image = Image.decode(((FileInputStream) is)); - } catch (ImageDecoder.DecodeException e) { - e.printStackTrace(); - } finally { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (image == null) { - error = GetText.getString(R.string.error_decoding_failed); - } + if (image == null) { + error = GetText.getString(R.string.error_decoding_failed); } // Notify diff --git a/app/src/main/java/com/hippo/image/Image.kt b/app/src/main/java/com/hippo/image/Image.kt index 006b434a1..d6344275d 100644 --- a/app/src/main/java/com/hippo/image/Image.kt +++ b/app/src/main/java/com/hippo/image/Image.kt @@ -19,16 +19,13 @@ package com.hippo.image import android.graphics.ColorSpace import android.graphics.ImageDecoder -import android.graphics.ImageDecoder.DecodeException import android.graphics.ImageDecoder.ImageInfo import android.graphics.ImageDecoder.Source import android.graphics.drawable.Animatable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import com.hippo.ehviewer.EhApplication -import java.io.FileInputStream import java.nio.ByteBuffer -import java.nio.channels.FileChannel import kotlin.math.min class Image private constructor(source: Source, private var releaseCall: (() -> Unit)? = null) { @@ -79,18 +76,6 @@ class Image private constructor(source: Source, private var releaseCall: (() -> ) ColorSpace.Named.DISPLAY_P3 else ColorSpace.Named.SRGB ) - @Throws(DecodeException::class) - @JvmStatic - fun decode(stream: FileInputStream): Image { - val src = ImageDecoder.createSource( - stream.channel.map( - FileChannel.MapMode.READ_ONLY, 0, - stream.available().toLong() - ) - ) - return Image(src) - } - @JvmStatic fun decode(src: ByteBufferSource): Image? { val directBuffer = src.getByteBuffer() diff --git a/app/src/main/java/com/hippo/unifile/RawFile.java b/app/src/main/java/com/hippo/unifile/RawFile.java index bc4207397..bf95e5679 100644 --- a/app/src/main/java/com/hippo/unifile/RawFile.java +++ b/app/src/main/java/com/hippo/unifile/RawFile.java @@ -32,11 +32,11 @@ import java.io.OutputStream; import java.io.RandomAccessFile; import java.util.ArrayList; -class RawFile extends UniFile { +public class RawFile extends UniFile { private static final String TAG = RawFile.class.getSimpleName(); - private File mFile; + public File mFile; RawFile(UniFile parent, File file) { super(parent);