7
public static boolean rotateBitmapByExifAndSave(File targetFile){

  if (targetFile==null || !targetFile.exists() || !targetFile.canRead() || !targetFile.canWrite())
      return false;

    boolean isSucceed = false;
    // detect if photo is need to be rotated
    try {
        final Matrix matrix = new Matrix();

        ExifInterface exifReader = new ExifInterface(targetFile.getAbsolutePath());

        int orientation = exifReader.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
        boolean isRotationNeeded = true;

        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            matrix.postRotate(90);
            break;

        case ExifInterface.ORIENTATION_ROTATE_180:
            matrix.postRotate(180);
            break;

        case ExifInterface.ORIENTATION_ROTATE_270:
            matrix.postRotate(270);
            break;

        default: // ExifInterface.ORIENTATION_NORMAL
            // Do nothing. The original image is fine.
            isRotationNeeded = false;
            isSucceed = true;
            break;
        }

        if (isRotationNeeded){
            BitmapFactory.Options bmfOtions = new BitmapFactory.Options();
            Bitmap bitmap = null;
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(targetFile);
                bitmap = BitmapFactory.decodeStream(fileInputStream,null,bmfOtions);
            } catch (FileNotFoundException e){
                isSucceed = false;
            }
            finally {
                if (fileInputStream != null)
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {}
            }
            if (bitmap!=null){
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                isSucceed = ImageUtils.saveBitmapToFile(bitmap, targetFile, 100);
                bitmap.recycle();
            }
        }

    } catch (IOException e) {
        Log.e("ImageUtils", e);
    } catch (Exception e) {
        // like there is no EXIF support?
        Log.e("ImageUtils", e);
    } catch (Throwable e) {
        // stupid Out of VM's memory
        Log.e("ImageUtils", e.toString());
    }

    return isSucceed; 
}

I use this method to rotate original photos made by device's camera. Nowadays camera could be bigger than 8MPix (Samsung Galaxy S4 has 13 Mega pixel camera). And even with less MPix camera (mine is 5 MP, 2592 x 1944 pixels which in conjunction of ARGB_888 takes 19Mb of RAM according to official docs) I already got OutOfMemory. So the question is how to rotate the photo WITHOUT loss of it's initial resolution and thus quality?

Stan
  • 6,511
  • 8
  • 55
  • 87

2 Answers2

2

Since there was no answer I assume there is no answer or maybe I just had asked the question a bit incorrectly. It looks like the only option here is to increase the app's heap size
UPDATE:
There is also another option - to work with bitmaps via NDK/JNI like here or to use Android Image-Magic lib. The Image Magic lib is pretty cool, to rotate an image all you need is:

ImageInfo imageInfo = new ImageInfo(imageFile.getAbsolutePath());
MagickImage magickImage = new MagickImage(imageInfo);
magickImage.setCompression(100); // to minimize loss
magickImage.rotateImage(90.0f).writeImage(imageInfo);

MagickImage has many other image manipulating options as well. Blur, matte, scale, charcoal and many more. However its libraries size is noticable. Authors made a great job and they covered all possible plaforms: arm64-v8a, armeabi, armeabi-v7a, mips, mips64, x86, x86_64 and final size of all these libs is over 36Mb. So you should think before adding all the libs into one apk, maybe packaging 6 different versions using manifest to filter by chipset/platform is the right way.
UPDATE
Another option is to convert Immutable Bitmap into Mutable (wrap bitmaps into MappedByteBuffer)

Community
  • 1
  • 1
Stan
  • 6,511
  • 8
  • 55
  • 87
  • 1
    Your use case does not seem to justify decode/encode of JPEG in the first place. It is not only the memory issue (as your accepted answer points out, there are quite a few ways to avoid OOM), it also affects performance and image quality. You can perform lossless rotation a JPEG image according to the EXIF flag, as demonstrated [here](http://stackoverflow.com/questions/706665/lossless-jpeg-rotate-90-180-270-degrees-in-java). The Android library can be found on **[GitHub](https://github.com/bkhall/AndroidMediaUtil)** – Alex Cohn Jan 28 '15 at 14:03
  • 1
    I tried the LLJTran/AndroidMediaUtil solution and it fails! I tried to rotate a really big JPEG image (2400x4200). It works slowly and it thows OOM with no resulting JPEG. – Stan Feb 14 '15 at 20:13
  • Btw, AndroidMediaUtil has annoying "feature" - they included a xml file (makernote.xml) in resources and thus they created a pain in the @ss cuz the lib/jar project can't contain any resources (excluding aar format). Even if you import it as a module in AndroidStudio - this won't help. So I had to hardcode info from this xml into String in order to make it work. Also 2400x4200 is not that big since my phone camera shoots 2400x3200. – Stan Feb 16 '15 at 06:30
  • **Re:** xml file, you can add `res` folder in an Android "library" project. Then, you add this jar to your app project via Properties/Android/Reference (see http://stackoverflow.com/a/9393482/192373). This does not make me happier about their bad performance. – Alex Cohn Feb 24 '15 at 19:38
  • 1
    Anyway, Android ImageMagick is a good way to go. Rotating image using this lib requires about 5 lines of code and it works fast... But the lib is not light weight one, especially if making an universal apk (its about 36 megs). – Stan Feb 24 '15 at 20:35
-1

Make a method name decode file:

public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
         try {
             //Decode image size
             BitmapFactory.Options o = new BitmapFactory.Options();
             o.inJustDecodeBounds = true;
             BitmapFactory.decodeStream(new FileInputStream(f),null,o);

             //The new size we want to scale to
             final int REQUIRED_WIDTH=WIDTH;
             final int REQUIRED_HIGHT=HIGHT;
             //Find the correct scale value. It should be the power of 2.
             int scale=1;
             while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
                 scale*=2;

             //Decode with inSampleSize
             BitmapFactory.Options o2 = new BitmapFactory.Options();
             o2.inSampleSize=scale;
             return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
         } catch (FileNotFoundException e) {}
         return null;
     }

then call this method like this (You can call this method in button click listener)

Bitmap bi = decodeFile(new File(path),1280,800);

Where path is the path of image where you save your image.. in my case it is

String path = Environment.getExternalStorageDirectory().toString() + "/nature.jpg";

In case of any problem - ask :) Hope this helps.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
Mit Bhatt
  • 1,605
  • 2
  • 13
  • 24
  • This error occurs because of Out of memory,so you have to set its height and width,pass it in this method – Mit Bhatt Jul 23 '13 at 08:50
  • 2
    Did you read this: So the question is how to rotate the photo WITHOUT loss of it's initial size and thus quality? – Stan Jul 23 '13 at 08:50
  • you can set same image size in this also dude. – Mit Bhatt Jul 23 '13 at 08:52
  • I have done this because I have enough memory thn also this exception occur,,, – Mit Bhatt Jul 23 '13 at 08:53
  • Sure I can set same image size but can't you see it will be useless? Whats the difference between just decode image as is AND decoding image using your code with setting the original image size? This snippet was designed for reduce memory by DOWNSIZING the original image before loading it to memory. Thats why it calculates the scale (downscale ratio). And I'm trying to avoid this. So in my case scale calculation is not needed since it always will be 1. – Stan Jul 23 '13 at 09:23
  • And thus decoded image will take the same amount of memory meaning all your manipulations with BitmapFactory.Options are useless. – Stan Jul 23 '13 at 09:33