diff --git a/app/src/main/kotlin/com/hippo/ehviewer/gallery/PageLoader.kt b/app/src/main/kotlin/com/hippo/ehviewer/gallery/PageLoader.kt index 5501c07b0..6ab559885 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/gallery/PageLoader.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/gallery/PageLoader.kt @@ -44,14 +44,7 @@ abstract class PageLoader(val gid: Long, var startPage: Int, val size: Int, val (OSUtils.appMaxMemory / 3 * 2).toInt() }, sizeOf = { _, v -> v.allocationSize.toInt() }, - onEntryRemoved = { k, o, n, _ -> - if (o.isRecyclable) { - n ?: notifyPageWait(k) - o.recycle() - } else { - o.isRecyclable = true - } - }, + onEntryRemoved = { k, o, n, _ -> if (o.unpin()) n ?: notifyPageWait(k) }, ) fun decodePreloadRange(index: Int) = index - 3..index + 3 diff --git a/app/src/main/kotlin/com/hippo/ehviewer/image/Image.kt b/app/src/main/kotlin/com/hippo/ehviewer/image/Image.kt index 46937c7ea..043d48889 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/image/Image.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/image/Image.kt @@ -50,10 +50,17 @@ import com.hippo.ehviewer.util.isAtLeastU import com.hippo.files.openFileDescriptor import com.hippo.files.toUri import java.nio.ByteBuffer +import java.util.concurrent.atomic.AtomicInteger import okio.Path import splitties.init.appCtx class Image private constructor(image: CoilImage, private val src: ImageSource) { + val refcnt = AtomicInteger(1) + + fun pin() = refcnt.updateAndGet { if (it != 0) it + 1 else 0 } != 0 + + fun unpin() = (refcnt.decrementAndGet() == 0).also { if (it) recycle() } + val intrinsicSize = with(image) { IntSize(width, height) } val allocationSize = image.size val hasQrCode = when (image) { @@ -66,11 +73,8 @@ class Image private constructor(image: CoilImage, private val src: ImageSource) else -> image } - var isRecyclable = true - - @Synchronized - fun recycle() { - when (val image = innerImage ?: return) { + private fun recycle() { + when (val image = innerImage!!) { is DrawableImage -> src.close() is BitmapImage -> image.bitmap.recycle() } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt index e3bea04a5..0129c7a0a 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PagerItem.kt @@ -11,15 +11,17 @@ import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorMatrix +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -42,6 +44,7 @@ import com.hippo.ehviewer.gallery.statusObserved import com.hippo.ehviewer.image.Image import com.hippo.ehviewer.util.AdsPlaceholderFile import eu.kanade.tachiyomi.ui.reader.viewer.CombinedCircularProgressIndicator +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.drop import moe.tarsin.kt.unreachable @@ -74,32 +77,35 @@ fun PagerItem( } is PageStatus.Ready -> { val image = state.image - val painter = remember(image) { image.toPainter() } - val grayScale by Settings.grayScale.collectAsState() - val invert by Settings.invertedColors.collectAsState() - DisposableEffect(image) { - image.isRecyclable = false - onDispose { - if (image.isRecyclable) { - pageLoader.notifyPageWait(page.index) - image.recycle() - } else { - image.isRecyclable = true + var painter by remember { mutableStateOf(null) } + LaunchedEffect(image) { + if (image.pin()) { + painter = image.toPainter() + try { + awaitCancellation() + } finally { + if (image.unpin()) { + pageLoader.notifyPageWait(page.index) + } } } } - Image( - painter = painter, - contentDescription = null, - modifier = contentModifier.fillMaxSize(), - contentScale = contentScale, - colorFilter = when { - grayScale && invert -> grayScaleAndInvertFilter - grayScale -> grayScaleFilter - invert -> invertFilter - else -> null - }, - ) + painter?.let { painter -> + val grayScale by Settings.grayScale.collectAsState() + val invert by Settings.invertedColors.collectAsState() + Image( + painter = painter, + contentDescription = null, + modifier = contentModifier.fillMaxSize(), + contentScale = contentScale, + colorFilter = when { + grayScale && invert -> grayScaleAndInvertFilter + grayScale -> grayScaleFilter + invert -> invertFilter + else -> null + }, + ) + } } is PageStatus.Blocked -> { AdsPlaceholder(