#1527 fixed concurrent region decoder pool access
This commit is contained in:
parent
af4ca96da8
commit
7f54befb72
3 changed files with 59 additions and 43 deletions
|
@ -13,6 +13,10 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
- upgraded Flutter to stable v3.29.3
|
- upgraded Flutter to stable v3.29.3
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- region decoding failing to access decoder pool
|
||||||
|
|
||||||
## <a id="v1.12.9"></a>[v1.12.9] - 2025-04-06
|
## <a id="v1.12.9"></a>[v1.12.9] - 2025-04-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -20,6 +20,8 @@ import deckers.thibault.aves.utils.MemoryUtils
|
||||||
import deckers.thibault.aves.utils.MimeTypes
|
import deckers.thibault.aves.utils.MimeTypes
|
||||||
import deckers.thibault.aves.utils.StorageUtils
|
import deckers.thibault.aves.utils.StorageUtils
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ class RegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val decoder = getOrCreateDecoder(uri, requestKey)
|
val decoder = getOrCreateDecoder(context, uri, requestKey)
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
result.error("fetch-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null)
|
result.error("fetch-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null)
|
||||||
return
|
return
|
||||||
|
@ -143,26 +145,6 @@ class RegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrCreateDecoder(uri: Uri, requestKey: Pair<Uri, Int?>): BitmapRegionDecoder? {
|
|
||||||
var decoderRef = decoderPool.firstOrNull { it.requestKey == requestKey }
|
|
||||||
if (decoderRef == null) {
|
|
||||||
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
|
||||||
BitmapRegionDecoderCompat.newInstance(input)
|
|
||||||
}
|
|
||||||
if (newDecoder == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
decoderRef = DecoderRef(requestKey, newDecoder)
|
|
||||||
} else {
|
|
||||||
decoderPool.remove(decoderRef)
|
|
||||||
}
|
|
||||||
decoderPool.add(0, decoderRef)
|
|
||||||
while (decoderPool.size > DECODER_POOL_SIZE) {
|
|
||||||
decoderPool.removeAt(decoderPool.size - 1)
|
|
||||||
}
|
|
||||||
return decoderRef.decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTemporaryJpegExport(uri: Uri, mimeType: String, pageId: Int?): Uri {
|
private fun createTemporaryJpegExport(uri: Uri, mimeType: String, pageId: Int?): Uri {
|
||||||
Log.d(LOG_TAG, "create JPEG export for uri=$uri mimeType=$mimeType pageId=$pageId")
|
Log.d(LOG_TAG, "create JPEG export for uri=$uri mimeType=$mimeType pageId=$pageId")
|
||||||
val target = Glide.with(context)
|
val target = Glide.with(context)
|
||||||
|
@ -195,5 +177,29 @@ class RegionFetcher internal constructor(
|
||||||
private const val DECODER_POOL_SIZE = 3
|
private const val DECODER_POOL_SIZE = 3
|
||||||
private val decoderPool = ArrayList<DecoderRef>()
|
private val decoderPool = ArrayList<DecoderRef>()
|
||||||
private val exportUris = HashMap<Pair<Uri, Int?>, Uri>()
|
private val exportUris = HashMap<Pair<Uri, Int?>, Uri>()
|
||||||
|
|
||||||
|
private val poolLock = ReentrantLock()
|
||||||
|
|
||||||
|
private fun getOrCreateDecoder(context: Context, uri: Uri, requestKey: Pair<Uri, Int?>): BitmapRegionDecoder? {
|
||||||
|
poolLock.withLock {
|
||||||
|
var decoderRef = decoderPool.firstOrNull { it.requestKey == requestKey }
|
||||||
|
if (decoderRef == null) {
|
||||||
|
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||||
|
BitmapRegionDecoderCompat.newInstance(input)
|
||||||
|
}
|
||||||
|
if (newDecoder == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
decoderRef = DecoderRef(requestKey, newDecoder)
|
||||||
|
} else {
|
||||||
|
decoderPool.remove(decoderRef)
|
||||||
|
}
|
||||||
|
decoderPool.add(0, decoderRef)
|
||||||
|
while (decoderPool.size > DECODER_POOL_SIZE) {
|
||||||
|
decoderPool.removeAt(decoderPool.size - 1)
|
||||||
|
}
|
||||||
|
return decoderRef.decoder
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ import deckers.thibault.aves.utils.BitmapUtils
|
||||||
import deckers.thibault.aves.utils.MemoryUtils
|
import deckers.thibault.aves.utils.MemoryUtils
|
||||||
import deckers.thibault.aves.utils.StorageUtils
|
import deckers.thibault.aves.utils.StorageUtils
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
class SvgRegionFetcher internal constructor(
|
class SvgRegionFetcher internal constructor(
|
||||||
|
@ -38,7 +40,7 @@ class SvgRegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val svg = getOrCreateDecoder(uri)
|
val svg = getOrCreateDecoder(context, uri)
|
||||||
if (svg == null) {
|
if (svg == null) {
|
||||||
result.error("fetch-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null)
|
result.error("fetch-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null)
|
||||||
return
|
return
|
||||||
|
@ -95,27 +97,6 @@ class SvgRegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrCreateDecoder(uri: Uri): SVG? {
|
|
||||||
var decoderRef = decoderPool.firstOrNull { it.uri == uri }
|
|
||||||
if (decoderRef == null) {
|
|
||||||
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
|
||||||
SVG.getFromInputStream(SVGParserBufferedInputStream(input))
|
|
||||||
}
|
|
||||||
if (newDecoder == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
newDecoder.normalizeSize()
|
|
||||||
decoderRef = DecoderRef(uri, newDecoder)
|
|
||||||
} else {
|
|
||||||
decoderPool.remove(decoderRef)
|
|
||||||
}
|
|
||||||
decoderPool.add(0, decoderRef)
|
|
||||||
while (decoderPool.size > DECODER_POOL_SIZE) {
|
|
||||||
decoderPool.removeAt(decoderPool.size - 1)
|
|
||||||
}
|
|
||||||
return decoderRef.decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class DecoderRef(
|
private data class DecoderRef(
|
||||||
val uri: Uri,
|
val uri: Uri,
|
||||||
val decoder: SVG,
|
val decoder: SVG,
|
||||||
|
@ -125,5 +106,30 @@ class SvgRegionFetcher internal constructor(
|
||||||
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
||||||
private const val DECODER_POOL_SIZE = 3
|
private const val DECODER_POOL_SIZE = 3
|
||||||
private val decoderPool = ArrayList<DecoderRef>()
|
private val decoderPool = ArrayList<DecoderRef>()
|
||||||
|
|
||||||
|
private val poolLock = ReentrantLock()
|
||||||
|
|
||||||
|
private fun getOrCreateDecoder(context: Context, uri: Uri): SVG? {
|
||||||
|
poolLock.withLock {
|
||||||
|
var decoderRef = decoderPool.firstOrNull { it.uri == uri }
|
||||||
|
if (decoderRef == null) {
|
||||||
|
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||||
|
SVG.getFromInputStream(SVGParserBufferedInputStream(input))
|
||||||
|
}
|
||||||
|
if (newDecoder == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
newDecoder.normalizeSize()
|
||||||
|
decoderRef = DecoderRef(uri, newDecoder)
|
||||||
|
} else {
|
||||||
|
decoderPool.remove(decoderRef)
|
||||||
|
}
|
||||||
|
decoderPool.add(0, decoderRef)
|
||||||
|
while (decoderPool.size > DECODER_POOL_SIZE) {
|
||||||
|
decoderPool.removeAt(decoderPool.size - 1)
|
||||||
|
}
|
||||||
|
return decoderRef.decoder
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue