qoi-java
qoi-java copied to clipboard
Question: Not worth to use QOI decoding over PNG decoding on Android?
Whether I use QOIUtil.readFile vs BitmapFactory.decodeFile of Android, or I use QOIUtil.readImage vs BitmapFactory.decodeStream , it seems that Android's implementation is faster to parse PNG files over QOI:
From inputStream of the resource (files are within the app) : QOI took 84 ms PNG took 25 ms
From a file outside the app: QOI took 36 ms PNG took 30 ms
And that's before getting Bitmap instance out of the QOIImage instance, which I don't understand how to do.
Tested on Pixel 4 with Android API 32 on the "wikipedia_008" image files, from here (got from here) : https://qoiformat.org/qoi_test_images.zip
See attached project:
How could it be? Is there anything that can be improved in the implementation? And how do you convert the output of the function to a working Bitmap instance?
it seems that Android's implementation is faster to parse PNG files over QOI
I don't know exactly, but most probably Android's PNG decoder is a native library, whereas qoi-java decoder is a Java library. To get expected performance boost from using QOI, it would make more sense to use original C library or one of its optimized implementations like in Rust.
Note that I've compared qoi-java with Java AWT PNG parser, and it mostly does make sense, because AWT's PNG parser is mostly written in Java.
Is there anything that can be improved in the implementation?
Java implementation of QOI is already optimized well (although I have some ideas, but implementing them will not give much performance boost). To decode even faster, as I've said above, you need to use native libraries.
Most performant version of QOI decoder should be written in a native language (C/Rust/etc.) and output an int array, suitable to be passed into Bitmap#createBitmap.
And how do you convert the output of the function to a working Bitmap instance?
You need to get RGB(A) values from a QOIImage and set pixels on a Bitmap: https://stackoverflow.com/a/59788350
Have you tested it using C library instead of Java (on Android) that you say it's faster? If so, can you please share the code you've used? Maybe it's better to have a library for Android based on this, instead. How did you measure?
As I remember, on new Android versions, the ART makes Java/Kotlin as good as C/C++ in terms of performance, usually. Here: https://source.android.com/devices/tech/dalvik/jit-compiler They also keep improving Java/Kotlin performance on every version of Android. So I don't think it's a valid argument anymore (on new Android versions).
Have you tested the conversion to Android's Bitmap object?
Have you tested it using C library instead of Java (on Android) that you say it's faster?
No, I just guess -- I assume that C is faster than Java, but of course I can't argue for or against it because no becnhmarks were done.
Have you tested the conversion to Android's Bitmap object?
No -- I'm not an Android developer and do not have Android development environment set up, so I can't do benchmarks/tests. But the conversion is so simple that I do not think anything needs to be tested here -- you can just try.
I see.
Can you please try to have a sample that runs on Java, but uses the C/C++ library you talked about? You can compare what you did here to this one of using C/C++ on a PC instead of Android. I'm sure that Java on PC got improved in speed too, and not just what Google did on Android.
As for the conversion from QOIImage to Bitmap, this worked for me:
fun convertQoiImageToBitmap(qoiImage: QOIImage): Bitmap {
val width = qoiImage.width
val height = qoiImage.height
val bytes = qoiImage.pixelData
val hasAlpha = qoiImage.channels == 4
val pixels = IntArray(if (hasAlpha) bytes.size / 4 else bytes.size / 3)
var j = 0
for (i in pixels.indices) {
val red: Int = bytes[j++].toInt() and 0xff
val green: Int = bytes[j++].toInt() and 0xff
val blue: Int = bytes[j++].toInt() and 0xff
val alpha: Int = if (hasAlpha) bytes[j++].toInt() and 0xff else 0xff
val pixel = alpha shl 24 or (red shl 16) or (green shl 8) or blue
pixels[i] = pixel
}
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
return bitmap
}
Now I calculated including the conversion to Bitmap. Somehow for the same input files the time taken has changed (probably some app was/is running in the background, or something) :
From inputStream of the resource (files are within the app) : QOI took 61 ms PNG took 31 ms
From a file outside the app: QOI took 38 ms PNG took 27 ms
Still slower than PNG, of course.
Attached here:
Can you please try to have a sample that runs on Java, but uses the C/C++ library you talked about?
Probably I can, using JNI/JNR (although there will be overhead, it would be interesting to see). But I can not say for now when I will have time to do it.
Can you please create a Java JNI wrapper and see if it performs better than what you did here?
Can you please create a Java JNI wrapper and see if it performs better than what you did here?
Yea, I can, it's just that I'm busy with my job and don't know when I will have time to work on QOI.
OK thanks. I'm just curious. :)