Fix image recycle racing
This commit is contained in:
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(
|
||||
|
||||
Reference in new issue
Block a user