In my tests, a Bitmap
created by BitmapFactory.decodeFile()
doesn\'t respect EXIF header.
For example, with portrait images taken by a devices
You can do this with Glide now. See the "Background Threads" section here:
https://bumptech.github.io/glide/doc/getting-started.html
Bitmap bitmap = Glide.with(context).asBitmap().load(new File(fileName)).skipMemoryCache(true).submit().get();
Glide takes into consideration EXIF. You'll need to load it on a background thread. I was using Glide 4.9.0
Is there a way to make BitmapFactory.decodeFile() respect EXIF and produce a correct Bitmap?
No, sorry.
what is the recommended pattern to handle this issue?
Use the support library's ExifInterface
to determine the desired orientation. Then, depending upon your use of the Bitmap
, either rotate the view (e.g., ImageView
) or rotate the Bitmap
. This sample project illustrates both approaches, though I use a separate set of EXIF code, as some things that I use in that sample are not supported by the support library's ExifInterface
.
As it turned out, resolving the initial issue turned into the set of new ones. Here is the complete tutorial for further readers.
First of all, as @CommonsWare pointed in his answer, we have to handle orientation manually using EXIF orientation tag. To avoid buggy/security flawed android.media.ExifInterface
as well as 3rd party dependencies, the best option seems to be a new com.android.support:exifinterface
library, which we add to build.gradle
as:
dependencies {
compile 'com.android.support:exifinterface:26.0.0'
}
However for me, it led to a Gradle sync error caused by that this specific newest version of the library is not found in Google repositories, though this version is the latest according to the Support Library Packages page.
After an hour of trials, I figured out that jcenter()
repository is not enough and Gradle sync was fixed by adding Google Maven repository:
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
// Alternative URL is 'https://dl.google.com/dl/android/maven2/'
}
}
}
Ok, now we're able to use android.support.media.ExifInterface
.
The next disappointment was that width and height stored in EXIF tags also do not respect orientation, i.e. for images taken in portrait mode you get the same width and height that are returned by a Bitmap
created with BitmapFactory.decodeFile()
. So the only way is to manually look at the EXIF ExifInterface.TAG_ORIENTATION
tag and if it says that image is rotated 90 or 270 degree - swap width and height:
ExifInterface exif = new ExifInterface(fileNameFull);
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
BitmapFactory.Options optsForBounds = new BitmapFactory.Options();
optsForBounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, optsForBounds);
int width = optsForBounds.outWidth, height = optsForBounds.outHeight;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270)
{
int temp = width;
//noinspection SuspiciousNameCombination
width = height;
height = temp;
}
I'm not sure if this approach and code covers 100% of cases, so if you encounter any issues across various devices/image sources - feel free to comment.
In general, dealing with EXIF doesn't seem to be a pleasant experience. Implementations of the EXIF standard even from big companies seem to treat nuances very differently. See extensive analysis here.