-
Notifications
You must be signed in to change notification settings - Fork 86
Description
I've created this tiny sample, which needs the entire file read into the byte-array, to parse animation files:
fun parseAnimationWithByteArray(data: ByteArray) {
val source = ByteBuffer.wrap(data)
val loader = object : ByteBufferLoader() {
override fun getByteBuffer(): ByteBuffer {
source.position(0)
return source
}
}
var decoder: FrameSeqDecoder<*, *>? = null
if (WebPParser.isAWebP(ByteBufferReader(source))) {
decoder = WebPDecoder(loader, null)
} else if (APNGParser.isAPNG(ByteBufferReader(source))) {
decoder = APNGDecoder(loader, null)
} else if (GifParser.isGif(ByteBufferReader(source))) {
decoder = GifDecoder(loader, null)
} else if (AVIFParser.isAVIF(ByteBufferReader(source))) {
decoder = AVIFDecoder(loader, null)
}
if (decoder == null) return
val width = decoder.bounds.width()
val height = decoder.bounds.height()
val frameCount = decoder.frameCount
Log.d("AppLog", "animations size:${width}x$height frameCount:$frameCount")
for (i in 0 until frameCount) {
val frame = decoder.getFrame(i) ?: break
Log.d("AppLog", "frame $i delay:${frame.frameDuration} ${frame.frameWidth}x${frame.frameHeight} ${frame.frameX}x${frame.frameY}")
try {
val bitmap = decoder.getFrameBitmap(i) ?: break
Log.d("AppLog", "bitmap for frame $i delay:${frame.frameDuration} ${bitmap.width}x${bitmap.height}")
} catch (e: IOException) {
e.printStackTrace()
}
}
}
usage in background thread:
Log.d("AppLog", "testing using byte array from resources, of webp")
Utils.testWithByteArray(resources.openRawResource(R.raw.webp).readBytes())
Log.d("AppLog", "testing using byte array from resources, of gif")
Utils.testWithByteArray(resources.openRawResource(R.raw.gif).readBytes())
This has 2 issues:
- It needs to read the entire file, which could be an issue if it's a huge file, as it can cause a crash.
- It doesn't really work as it should. I failed to reach all frames for some reason. It shows the information about the files fine, but about reaching all the frames, it fails on the second frame for animated WEBP, and right on the beginning for animated GIF (no idea about the others).
I know that if I want to get the images as they are played, I could use this instead, which works well:
val bitmap = createBitmap(decoder.getBounds().width(), decoder.getBounds().height())
val frameCount = decoder.frameCount
decoder.addRenderListener(object : EmptyRenderListener() {
override fun onRender(byteBuffer: ByteBuffer) {
super.onRender(byteBuffer)
byteBuffer.rewind()
if (byteBuffer.remaining() < bitmap.byteCount) {
return
}
bitmap.copyPixelsFromBuffer(byteBuffer)
val frameIndex = decoder.frameIndex
...
}
})
Any idea how to fix the function parseAnimationWithByteArray and make it better, so that it could handle content with minimal memory footprint, and yet be able to reach each of the frames, their bitmap, duration and other important information?
Maybe use one of the Loader classes here? But I'm not sure they are focused on memory usage...
On Android it's possible for example to re-create the InputStream when needed from a given Uri, and I'm sure that even for files there are better solutions...