svg decoder pool

This commit is contained in:
Thibault Deckers 2025-04-02 23:14:09 +02:00
parent 85a1b33e83
commit 503757da0e

View file

@ -22,8 +22,6 @@ import kotlin.math.ceil
class SvgRegionFetcher internal constructor( class SvgRegionFetcher internal constructor(
private val context: Context, private val context: Context,
) { ) {
private var lastSvgRef: LastSvgRef? = null
fun fetch( fun fetch(
uri: Uri, uri: Uri,
sizeBytes: Long?, sizeBytes: Long?,
@ -39,33 +37,13 @@ class SvgRegionFetcher internal constructor(
return return
} }
var currentSvgRef = lastSvgRef
if (currentSvgRef != null && currentSvgRef.uri != uri) {
currentSvgRef = null
}
try { try {
if (currentSvgRef == null) { val svg = getOrCreateDecoder(uri)
val newSvg = StorageUtils.openInputStream(context, uri)?.use { input -> if (svg == null) {
try {
SVG.getFromInputStream(SVGParserBufferedInputStream(input))
} catch (ex: SVGParseException) {
result.error("fetch-parse", "failed to parse SVG for uri=$uri regionRect=$regionRect", null)
return
}
}
if (newSvg == 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
} }
newSvg.normalizeSize()
currentSvgRef = LastSvgRef(uri, newSvg)
}
val svg = currentSvgRef.svg
lastSvgRef = currentSvgRef
// we scale the requested region accordingly to the viewbox size // we scale the requested region accordingly to the viewbox size
val viewBox = svg.documentViewBox val viewBox = svg.documentViewBox
val svgWidth = viewBox.width() val svgWidth = viewBox.width()
@ -110,17 +88,42 @@ class SvgRegionFetcher internal constructor(
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight) bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
val bytes = BitmapUtils.getRawBytes(bitmap, recycle = true) val bytes = BitmapUtils.getRawBytes(bitmap, recycle = true)
result.success(bytes) result.success(bytes)
} catch (e: SVGParseException) {
result.error("fetch-parse", "failed to parse SVG for uri=$uri regionRect=$regionRect", null)
} catch (e: Exception) { } catch (e: Exception) {
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message) result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
} }
} }
private data class LastSvgRef( 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 uri: Uri,
val svg: SVG, val decoder: SVG,
) )
companion object { companion object {
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888 private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
private const val DECODER_POOL_SIZE = 3
private val decoderPool = ArrayList<DecoderRef>()
} }
} }