Fix image recycle racing

This commit is contained in:
Dude so hot authored and renovate[bot] committed 2024-11-14 23:02:49 +08:00
1 parent 81ee3c47f5
commit ab6dfa0f08
3 files changed
+40 -37

No files matched your search

@@ -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
@@ -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()
}
@@ -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<Painter?>(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(