diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c3f7e575..f8d8b76a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ All notable changes to this project will be documented in this file. - upgraded Flutter to stable v3.29.3 +### Fixed + +- region decoding failing to access decoder pool + ## [v1.12.9] - 2025-04-06 ### Added diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt index f3d761fbb..0762a4df9 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt @@ -20,6 +20,8 @@ import deckers.thibault.aves.utils.MemoryUtils import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.StorageUtils import io.flutter.plugin.common.MethodChannel +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock import kotlin.math.max import kotlin.math.roundToInt @@ -60,7 +62,7 @@ class RegionFetcher internal constructor( } try { - val decoder = getOrCreateDecoder(uri, requestKey) + val decoder = getOrCreateDecoder(context, uri, requestKey) if (decoder == null) { result.error("fetch-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null) return @@ -143,26 +145,6 @@ class RegionFetcher internal constructor( } } - private fun getOrCreateDecoder(uri: Uri, requestKey: Pair): 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 { Log.d(LOG_TAG, "create JPEG export for uri=$uri mimeType=$mimeType pageId=$pageId") val target = Glide.with(context) @@ -195,5 +177,29 @@ class RegionFetcher internal constructor( private const val DECODER_POOL_SIZE = 3 private val decoderPool = ArrayList() private val exportUris = HashMap, Uri>() + + private val poolLock = ReentrantLock() + + private fun getOrCreateDecoder(context: Context, uri: Uri, requestKey: Pair): 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 + } + } } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt index 7208bc6a3..03b8d4df4 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/SvgRegionFetcher.kt @@ -17,6 +17,8 @@ import deckers.thibault.aves.utils.BitmapUtils import deckers.thibault.aves.utils.MemoryUtils import deckers.thibault.aves.utils.StorageUtils import io.flutter.plugin.common.MethodChannel +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock import kotlin.math.ceil class SvgRegionFetcher internal constructor( @@ -38,7 +40,7 @@ class SvgRegionFetcher internal constructor( } try { - val svg = getOrCreateDecoder(uri) + val svg = getOrCreateDecoder(context, uri) if (svg == null) { result.error("fetch-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null) 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( val uri: Uri, val decoder: SVG, @@ -125,5 +106,30 @@ class SvgRegionFetcher internal constructor( private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888 private const val DECODER_POOL_SIZE = 3 private val decoderPool = ArrayList() + + 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 + } + } } }