How to detect an image format in Android application

Alexander Shevelev
2 min readJan 31, 2021
Give me that image!

Many of us use Intent with the Intent.ACTION_PICK action to open the Gallery app, select a photo from it, and then process it in our app (via the Content Uri). The problem is that not all image formats can be handled by your application. In my case, the troubles started when I enabled Raw format support on my Google Pixel in the Camera settings.

I tried to find a way to determine the received image format on the Internet, but none of them was sufficiently reliable and universal. And then it hit me! Why don’t we read the image header from the data stream and use it to calculate the image format? I was only interested in JPEG and PNG in my app, so I quickly made a working solution and ready to present it to you (full sources can be found in a demo project).

So, to detect a type of image file, you should read several bytes from its header (which is located at the very beginning of the image) to an array of bytes and match it to a reference one.

After reading file format documentation, I found out that the JPEG format's reference bytes are: 0xFF 0xD8 0xFF.

For PNG reference bytes are: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A.

Let’s start coding now.

First, we should define a set of images format, like so:

It’s time to add a heart of our detector — a matching class. It looks quite straightforward:

It takes as input arguments a reference array of bytes and a checking format type. The purpose of the size property we’ll see a bit later. Now, take a look at the check() method. It matches byte-to-byte the reference array and the tested one. It’s pretty simple.

We have a matching class, but how to create an instance of it? I use a bare-bones factory for this purpose, like the next one:

And, finally, let’s see a key class of our detector:

It takes a list of matches as an argument. All the magic happens in getImageType(InputStream?) method. The purpose of the size property of ImageMatching class becomes clear now — it’s used to calculate the size of a buffer that contains a checked part of an image header. Beyond that, the algorithm is quite simple — it looks for the first success checking and returns an image type from its matcher.

That’s all, folks! All sources code for the article you can find in my tiny demo project.

--

--

Alexander Shevelev

An Android developer from Yandex LLC, Moscow, Russia. That’s all.