When I load an image from the media gallery into a Bitmap, everything is working fine, except that pictures that were shot with the camera while holding the phone vertically, are rotated so that I always get a horizontal picture even though it appears vertical in the gallery. Why is that and how can I load it correctly?
Have you looked at the EXIF data of the images? It may know the orientation of the camera when the picture was taken.
So, as an example...
First you need to create an ExifInterface:
ExifInterface exif = new ExifInterface(filename);
You can then grab the orientation of the image:
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
Here's what the orientation values mean: http://sylvana.net/jpegcrop/exif_orientation.html
So, the most important values are 3, 6 and 8.
If the orientation is ExifInterface.ORIENTATION_ROTATE_90
(which is 6), for example, you can rotate the image like this:
Matrix matrix = new Matrix();
matrix.postRotate(90);
rotatedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), matrix, true);
That's just a quick example, though. I'm sure there are other ways of performing the actual rotation. But you will find those on StackOverflow as well.
OutOfMemoryError
when use this approach as you hold two bitmaps in memory at the same time. –
Maneuver Solved it in my case with this code using help of this post:
Bitmap myBitmap = getBitmap(imgFile.getAbsolutePath());
try {
ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
Log.d("EXIF", "Exif: " + orientation);
Matrix matrix = new Matrix();
if (orientation == 6) {
matrix.postRotate(90);
}
else if (orientation == 3) {
matrix.postRotate(180);
}
else if (orientation == 8) {
matrix.postRotate(270);
}
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true); // rotating bitmap
}
catch (Exception e) {
}
ImageView img = (ImageView) findViewById(R.id.imgTakingPic);
img.setImageBitmap(myBitmap);
Hope it saves someone's time!
This is a full solution (found in the Hackbook example from the Facebook SDK). It has the advantage of not needing access to the file itself. This is extremely useful if you are loading an image from the content resolver thingy (e.g. if your app is responding to a share-photo intent).
public static int getOrientation(Context context, Uri photoUri) {
/* it's on the external media. */
Cursor cursor = context.getContentResolver().query(photoUri,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() != 1) {
return -1;
}
cursor.moveToFirst();
return cursor.getInt(0);
}
And then you can get a rotated Bitmap as follows. This code also scales down the image (badly unfortunately) to MAX_IMAGE_DIMENSION. Otherwise you may run out of memory.
public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
InputStream is = context.getContentResolver().openInputStream(photoUri);
BitmapFactory.Options dbo = new BitmapFactory.Options();
dbo.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, dbo);
is.close();
int rotatedWidth, rotatedHeight;
int orientation = getOrientation(context, photoUri);
if (orientation == 90 || orientation == 270) {
rotatedWidth = dbo.outHeight;
rotatedHeight = dbo.outWidth;
} else {
rotatedWidth = dbo.outWidth;
rotatedHeight = dbo.outHeight;
}
Bitmap srcBitmap;
is = context.getContentResolver().openInputStream(photoUri);
if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
float maxRatio = Math.max(widthRatio, heightRatio);
// Create the bitmap from file
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) maxRatio;
srcBitmap = BitmapFactory.decodeStream(is, null, options);
} else {
srcBitmap = BitmapFactory.decodeStream(is);
}
is.close();
/*
* if the orientation is not 0 (or -1, which means we don't know), we
* have to do a rotation.
*/
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
srcBitmap.getHeight(), matrix, true);
}
return srcBitmap;
}
ExifInterface exif = new ExifInterface(photoUri.getPath());
and then exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1)
to get orientation (e.g. ORIENTATION_ROTATE_90
, ORIENTATION_ROTATE_180
) –
Cornwallis Use a Utility to do the Heavy Lifting.
9re created a simple utility to handle the heavy lifting of dealing with EXIF data and rotating images to their correct orientation.
You can find the utility code here: https://gist.github.com/9re/1990019
Simply download this, add it to your project's src
directory and use ExifUtil.rotateBitmap()
to get the correct orientation, like so:
String imagePath = photoFile.getAbsolutePath(); // photoFile is a File class.
Bitmap myBitmap = BitmapFactory.decodeFile(imagePath);
Bitmap orientedBitmap = ExifUtil.rotateBitmap(imagePath, myBitmap);
rotateBitmap
is actually where to save the rotated bitmap file to. In our case, we're saving overtop of the existing file so we pass in the imagePath
for that argument. If you wanted to save a new file (i.e. to not lose the original file), you could provide a different path to save the rotated bitmap to. –
Rich Have you looked at the EXIF data of the images? It may know the orientation of the camera when the picture was taken.
Kotlin code:
if (file.exists()){
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
val exif = ExifInterface(file.absoluteFile.toString())
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
val matrix = Matrix()
when(orientation){
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
}
val rotatedBitmap = Bitmap.createBitmap(bitmap, 0,0 , bitmap.width, bitmap.height, matrix, true)
bitmap.recycle()
iv_capture.setImageBitmap(rotatedBitmap)
}
its because gallery correct displaying rotated images but not ImageView look at here:
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(),optionss);
ExifInterface exif = new ExifInterface(selectedImagePath);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
deg = rotationInDegrees;
Matrix matrix = new Matrix();
if (rotation != 0f) {
matrix.preRotate(rotationInDegrees);
myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true);
}
and you need this:
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) { return 180; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) { return 270; }
return 0;
}
Got it to work after many attempts thanks to a post I can no longer find :-(
Exif seems to work always, the difficulty was to get the filepath. The code I found makes a different between API older than 4.4 and after 4.4. Basically the picture URI for 4.4+ contains "com.android.providers". For this type of URI, the code uses DocumentsContract to get the picture id and then runs a query using the ContentResolver, while for older SDK, the code goes straight to query the URI with the ContentResolver.
Here is the code (sorry I cannot credit who posted it):
/**
* Handles pre V19 uri's
* @param context
* @param contentUri
* @return
*/
public static String getPathForPreV19(Context context, Uri contentUri) {
String res = null;
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
if(cursor.moveToFirst()){;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
}
cursor.close();
return res;
}
/**
* Handles V19 and up uri's
* @param context
* @param contentUri
* @return path
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPathForV19AndUp(Context context, Uri contentUri) {
String wholeID = DocumentsContract.getDocumentId(contentUri);
// Split at colon, use second item in the array
String id = wholeID.split(":")[1];
String[] column = { MediaStore.Images.Media.DATA };
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
Cursor cursor = context.getContentResolver().
query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[]{ id }, null);
String filePath = "";
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
}
cursor.close();
return filePath;
}
public static String getRealPathFromURI(Context context,
Uri contentUri) {
String uriString = String.valueOf(contentUri);
boolean goForKitKat= uriString.contains("com.android.providers");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && goForKitKat) {
Log.i("KIKAT","YES");
return getPathForV19AndUp(context, contentUri);
} else {
return getPathForPreV19(context, contentUri);
}
}
You can just read the path from sd card and do the following code...it'll Replace the existing photo after rotating it..
Not: Exif doesnt work on most of the devices, it gives the wrong data so it's good to hard code the rotating before saving to any degree you want to,You just have to change the angle value in postRotate to any you want to.
String photopath = tempphoto.getPath().toString();
Bitmap bmp = BitmapFactory.decodeFile(photopath);
Matrix matrix = new Matrix();
matrix.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
FileOutputStream fOut;
try {
fOut = new FileOutputStream(tempphoto);
bmp.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
fOut.flush();
fOut.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I improved upon the answer by Teo Inke. It no longer rotates the image unless it is actually necessary. It is also easier to read, and should run faster.
// Load Image
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
// Rotate Image if Needed
try
{
// Determine Orientation
ExifInterface exif = new ExifInterface(filePath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
// Determine Rotation
int rotation = 0;
if (orientation == 6) rotation = 90;
else if (orientation == 3) rotation = 180;
else if (orientation == 8) rotation = 270;
// Rotate Image if Necessary
if (rotation != 0)
{
// Create Matrix
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
// Rotate Bitmap
Bitmap rotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// Pretend none of this ever happened!
bitmap.recycle();
bitmap = rotated;
rotated = null;
}
}
catch (Exception e)
{
// TODO: Log Error Messages Here
}
// TODO: Use Result Here
xxx.setBitmap(bitmap);
The first thing you need is the real File path If you have it great, if you are using URI then use this method to get the real Path:
public static String getRealPathFromURI(Uri contentURI,Context context) {
String path= contentURI.getPath();
try {
Cursor cursor = context.getContentResolver().query(contentURI, null, null, null, null);
cursor.moveToFirst();
String document_id = cursor.getString(0);
document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
cursor.close();
cursor = context.getContentResolver().query(
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
cursor.moveToFirst();
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
}
catch(Exception e)
{
return path;
}
return path;
}
extract your Bitmap for example:
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);
}
catch (IOException e)
{
Log.e("IOException",e.toString());
}
you can use decodeFile() instead if you wish.
Now that you have the Bitmap and the real Path get the Orientation of the Image:
private static int getExifOrientation(String src) throws IOException {
int orientation = 1;
ExifInterface exif = new ExifInterface(src);
String orientationString=exif.getAttribute(ExifInterface.TAG_ORIENTATION);
try {
orientation = Integer.parseInt(orientationString);
}
catch(NumberFormatException e){}
return orientation;
}
and finally rotate it to the right position like so:
public static Bitmap rotateBitmap(String src, Bitmap bitmap) {
try {
int orientation = getExifOrientation(src);
if (orientation == 1) {
return bitmap;
}
Matrix matrix = new Matrix();
switch (orientation) {
case 2:
matrix.setScale(-1, 1);
break;
case 3:
matrix.setRotate(180);
break;
case 4:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case 5:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case 6:
matrix.setRotate(90);
break;
case 7:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case 8:
matrix.setRotate(-90);
break;
default:
return bitmap;
}
try {
Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return oriented;
} catch (OutOfMemoryError e) {
e.printStackTrace();
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
That's it , you now have the bitmap rotated to the right position.
cheers.
This works, but probably not the best way to do it, but it might help someone.
String imagepath = someUri.getAbsolutePath();
imageview = (ImageView)findViewById(R.id.imageview);
imageview.setImageBitmap(setImage(imagepath, 120, 120));
public Bitmap setImage(String path, final int targetWidth, final int targetHeight) {
Bitmap bitmap = null;
// Get exif orientation
try {
ExifInterface exif = new ExifInterface(path);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
if (orientation == 6) {
orientation_val = 90;
}
else if (orientation == 3) {
orientation_val = 180;
}
else if (orientation == 8) {
orientation_val = 270;
}
}
catch (Exception e) {
}
try {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Adjust extents
int sourceWidth, sourceHeight;
if (orientation_val == 90 || orientation_val == 270) {
sourceWidth = options.outHeight;
sourceHeight = options.outWidth;
} else {
sourceWidth = options.outWidth;
sourceHeight = options.outHeight;
}
// Calculate the maximum required scaling ratio if required and load the bitmap
if (sourceWidth > targetWidth || sourceHeight > targetHeight) {
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
options.inJustDecodeBounds = false;
options.inSampleSize = (int)maxRatio;
bitmap = BitmapFactory.decodeFile(path, options);
} else {
bitmap = BitmapFactory.decodeFile(path);
}
// Rotate the bitmap if required
if (orientation_val > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation_val);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
// Re-scale the bitmap if necessary
sourceWidth = bitmap.getWidth();
sourceHeight = bitmap.getHeight();
if (sourceWidth != targetWidth || sourceHeight != targetHeight) {
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
sourceWidth = (int)((float)sourceWidth / maxRatio);
sourceHeight = (int)((float)sourceHeight / maxRatio);
bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
}
} catch (Exception e) {
}
return bitmap;
}
The methods below scales AND rotates the bitmap according to the orientation:
public Bitmap scaleAndRotateImage(String path, int orientation, final int targetWidth, final int targetHeight)
{
Bitmap bitmap = null;
try
{
// Check the dimensions of the Image
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Adjust the Width and Height
int sourceWidth, sourceHeight;
if (orientation == 90 || orientation == 270)
{
sourceWidth = options.outHeight;
sourceHeight = options.outWidth;
}
else
{
sourceWidth = options.outWidth;
sourceHeight = options.outHeight;
}
// Calculate the maximum required scaling ratio if required and load the bitmap
if (sourceWidth > targetWidth || sourceHeight > targetHeight)
{
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
options.inJustDecodeBounds = false;
options.inSampleSize = (int)maxRatio;
bitmap = BitmapFactory.decodeFile(path, options);
}
else
{
bitmap = BitmapFactory.decodeFile(path);
}
// We need to rotate the bitmap (if required)
int orientationInDegrees = exifToDegrees(orientation);
if (orientation > 0)
{
Matrix matrix = new Matrix();
if (orientation != 0f)
{
matrix.preRotate(orientationInDegrees);
};
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
// Re-scale the bitmap if necessary
sourceWidth = bitmap.getWidth();
sourceHeight = bitmap.getHeight();
if (sourceWidth != targetWidth || sourceHeight != targetHeight)
{
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
sourceWidth = (int)((float)sourceWidth / maxRatio);
sourceHeight = (int)((float)sourceHeight / maxRatio);
bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
}
}
catch (Exception e)
{
Logger.d("Could not rotate the image");
Logger.d(e.getMessage());
}
return bitmap;
}
Example:
public void getPictureFromDevice(Uri Uri,ImageView imageView)
{
try
{
ExifInterface exifInterface = new ExifInterface(Uri.getPath());
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Bitmap bitmap = scaleAndRotateImage(Uri.getPath(), orientation, imageView.getWidth(), imageView.getHeight());
imageView.setImageBitmap(bitmap);
}
catch (OutOfMemoryError outOfMemoryError)
{
Logger.d(outOfMemoryError.getLocalizedMessage());
Logger.d("Failed to load image from filePath (out of memory)");
Logger.d(Uri.toString());
}
catch (Exception e)
{
Logger.d("Failed to load image from filePath");
Logger.d(Uri.toString());
}
}
maybe this will help (rotate 90 degree)(this worked for me)
private Bitmap rotateBitmap(Bitmap image){
int width=image.getHeight();
int height=image.getWidth();
Bitmap srcBitmap=Bitmap.createBitmap(width, height, image.getConfig());
for (int y=width-1;y>=0;y--)
for(int x=0;x<height;x++)
srcBitmap.setPixel(width-y-1, x,image.getPixel(x, y));
return srcBitmap;
}
The cursor should be closed after opening it.
Here is an example.
public static int getOrientation(Context context, Uri selectedImage)
{
int orientation = -1;
Cursor cursor = context.getContentResolver().query(selectedImage,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() != 1)
return orientation;
cursor.moveToFirst();
orientation = cursor.getInt(0);
cursor.close(); // ADD THIS LINE
return orientation;
}
I have melted @Timmmm answer and @Manuel. If you do this solution, you will not get a Run Out Of Memory Exception.
This method retrieves the image orientation:
private static final int ROTATION_DEGREES = 90;
// This means 512 px
private static final Integer MAX_IMAGE_DIMENSION = 512;
public static int getOrientation(Uri photoUri) throws IOException {
ExifInterface exif = new ExifInterface(photoUri.getPath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
orientation = ROTATION_DEGREES;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
orientation = ROTATION_DEGREES * 2;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
orientation = ROTATION_DEGREES * 3;
break;
default:
// Default case, image is not rotated
orientation = 0;
}
return orientation;
}
Therefore, you would use this method to resize image before load it on memory. In that way, you will not get a Memory Exception.
public static Bitmap getCorrectlyOrientedImage(Context context, Uri photoUri) throws IOException {
InputStream is = context.getContentResolver().openInputStream(photoUri);
BitmapFactory.Options dbo = new BitmapFactory.Options();
dbo.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, dbo);
is.close();
int rotatedWidth, rotatedHeight;
int orientation = getOrientation(photoUri);
if (orientation == 90 || orientation == 270) {
rotatedWidth = dbo.outHeight;
rotatedHeight = dbo.outWidth;
} else {
rotatedWidth = dbo.outWidth;
rotatedHeight = dbo.outHeight;
}
Bitmap srcBitmap;
is = context.getContentResolver().openInputStream(photoUri);
if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
float maxRatio = Math.max(widthRatio, heightRatio);
// Create the bitmap from file
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) maxRatio;
srcBitmap = BitmapFactory.decodeStream(is, null, options);
} else {
srcBitmap = BitmapFactory.decodeStream(is);
}
is.close();
// if the orientation is not 0, we have to do a rotation.
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
srcBitmap.getHeight(), matrix, true);
}
return srcBitmap;
}
This works perfectly for me. I hope this helps somebody else
All the previous answers use the android.media.ExifInterface, which is outdated.
This class has known issues on some versions of Android. It is recommended to use the AndroidX ExifInterface Library since it offers a superset of the functionality of this class and is more easily updateable.
In newer Android versions (AndroidX), this is how you do it:
fun loadBitmapFromFile(file: File): Bitmap {
val bitmap = BitmapFactory.decodeFile(file.path)
val exif = ExifInterface(file)
val rotation = exif.rotationDegrees
if (rotation == 0) return bitmap
val matrix = Matrix().apply { postRotate(rotation.toFloat()) }
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
You also have to add implementation 'androidx.exifinterface:exifinterface:1.3.6'
to your build.gradle dependencies.
Make sure you import androidx.exifinterface.media.ExifInterface
and not the outdated android.media.ExifInterface
.
Improving on the solution above by Timmmm to add some extra scaling at the end to ensure that the image fits within the bounds:
public static Bitmap loadBitmap(String path, int orientation, final int targetWidth, final int targetHeight) {
Bitmap bitmap = null;
try {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Adjust extents
int sourceWidth, sourceHeight;
if (orientation == 90 || orientation == 270) {
sourceWidth = options.outHeight;
sourceHeight = options.outWidth;
} else {
sourceWidth = options.outWidth;
sourceHeight = options.outHeight;
}
// Calculate the maximum required scaling ratio if required and load the bitmap
if (sourceWidth > targetWidth || sourceHeight > targetHeight) {
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
options.inJustDecodeBounds = false;
options.inSampleSize = (int)maxRatio;
bitmap = BitmapFactory.decodeFile(path, options);
} else {
bitmap = BitmapFactory.decodeFile(path);
}
// Rotate the bitmap if required
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
// Re-scale the bitmap if necessary
sourceWidth = bitmap.getWidth();
sourceHeight = bitmap.getHeight();
if (sourceWidth != targetWidth || sourceHeight != targetHeight) {
float widthRatio = (float)sourceWidth / (float)targetWidth;
float heightRatio = (float)sourceHeight / (float)targetHeight;
float maxRatio = Math.max(widthRatio, heightRatio);
sourceWidth = (int)((float)sourceWidth / maxRatio);
sourceHeight = (int)((float)sourceHeight / maxRatio);
bitmap = Bitmap.createScaledBitmap(bitmap, sourceWidth, sourceHeight, true);
}
} catch (Exception e) {
}
return bitmap;
}
Use the following code to rotate an image correctly:
private Bitmap rotateImage(Bitmap bitmap, String filePath)
{
Bitmap resultBitmap = bitmap;
try
{
ExifInterface exifInterface = new ExifInterface(filePath);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
Matrix matrix = new Matrix();
if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
{
matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_90);
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
{
matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_180);
}
else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
{
matrix.postRotate(ExifInterface.ORIENTATION_ROTATE_270);
}
// Rotate the bitmap
resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
catch (Exception exception)
{
Logger.d("Could not rotate the image");
}
return resultBitmap;
}
I solved the problem with the following workaround. Note that I am also scaling the image, which was necessary to avoid OutOfMemoryExceptions.
Beware that this solution will not work properly with portrait images or opside-down images (thank you Timmmm for noting). Timmmm's solution above might be the better choice if that is required and it looks more elegant, too: https://mcmap.net/q/50207/-android-bitmaps-loaded-from-gallery-are-rotated-in-imageview
File path = // ... location of your bitmap file
int w = 512; int h = 384; // size that does not lead to OutOfMemoryException on Nexus One
Bitmap b = BitmapFactory.decodeFile(path);
// Hack to determine whether the image is rotated
boolean rotated = b.getWidth() > b.getHeight();
Bitmap resultBmp = null;
// If not rotated, just scale it
if (!rotated) {
resultBmp = Bitmap.createScaledBitmap(b, w, h, true);
b.recycle();
b = null;
// If rotated, scale it by switching width and height and then rotated it
} else {
Bitmap scaledBmp = Bitmap.createScaledBitmap(b, h, w, true);
b.recycle();
b = null;
Matrix mat = new Matrix();
mat.postRotate(90);
resultBmp = Bitmap.createBitmap(scaledBmp, 0, 0, h, w, mat, true);
// Release image resources
scaledBmp.recycle();
scaledBmp = null;
}
// resultBmp now contains the scaled and rotated image
Cheers
© 2022 - 2024 — McMap. All rights reserved.