6

Basically I'm trying to rotate a Bitmap (from an image) in an Android App. The reason why I want to do this is that a picture taken from the camera (through an intent) is displayed horizontally even if it's captured vertically, and the orientation is kept as metadata on the image. Correct me if in wrong. The problem is, however, that the image will take up a lot of memory when loaded in, if taken on a phone with a reasonably good camera, and I haven't found a way to rotate and save it without the risk of getting OutOfMemoryError. The code below is where i:

  1. Load in the image
  2. Check if it needs to be rotated
  3. Loads a scaled-down version for display in an ImageView
  4. Rotates the small image if necessary
  5. In a seperate thread; load, rotate and save the image, so it doesn't need to in the future

It is important for the application to keep the images in the resolution, but any tricks with encodings are welcome. I have searched the internet for a few days, unable to find anything more than what i already have implemented. There is another thread on the subject here, but there doesn't seem to be any solutions. Hope you can help.

    public Bitmap getBitmap(final Context c) {
    if (bitmap != null)
        return bitmap;

    final int rotate = necessaryRotation(c, file);
    // if(rotate != 0) rotateImageFile(c, rotate);

    try {
        // Get scaled version
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(file, options);
        options.inSampleSize = calcInSampleSize(options, 1024, 1024);
        options.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(file, options);

        // rotate?
        bitmap = rotateImage(c,bitmap,rotate);

        System.out.println("Bitmap loaded from file: size="
                + bitmap.getWidth() + "," + bitmap.getHeight());

        System.gc();
    } catch (Exception e) {
        System.err.println("Unable to load image file: "
                + this.getFilename());
    }

    // if rotation is needed, do it in worker thread for next time
    if(rotate != 0){
        Thread t = new Thread(new Runnable(){

            public void run() {
                // load entire image
                try{
                    File imageFile = new File(getFilename());
                    Bitmap huge = Media.getBitmap(c.getContentResolver(),
                    Uri.fromFile(imageFile));

                    huge = rotateImage(c,huge,rotate);

                    // save bitmap properly
                    FileOutputStream out = new FileOutputStream(imageFile);
                    huge.compress(Bitmap.CompressFormat.PNG, 100, out);

                    out.flush();
                    out.close();
                    huge.recycle();
                    huge = null;
                    out = null;
                    System.gc();

                }catch(IOException e){
                    e.printStackTrace();
                }
            }

        });
        t.start();
    }

    return bitmap;
}

private Bitmap rotateImage(Context c, Bitmap bitmap, int rotate) {
    if (rotate != 0) {
        // rotate
        Matrix m = new Matrix();
        m.postRotate(rotate);
        Bitmap rotImage = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), m, true);
        bitmap.recycle();

        System.out.println("Image (id=" + getId()
                + ") rotated successfully");

        System.gc();

        return rotImage;
    }
    return bitmap;
}

private int necessaryRotation(Context c, String imageFile) {
    int rotate = 0;
    ExifInterface exif;
    try {
        exif = new ExifInterface(imageFile);
        int orientation = exif.getAttributeInt(
                ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_270:
            rotate = 270;
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            rotate = 180;
            break;
        case ExifInterface.ORIENTATION_ROTATE_90:
            rotate = 90;
            break;
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return rotate;
}

private int calcInSampleSize(BitmapFactory.Options options, int reqWidth,
        int reqHeight) {
    int height = options.outHeight;
    int width = options.outWidth;
    int inSampleSize = 1;
    while (height > reqHeight || width > reqWidth) {
        height /= 2;
        width /= 2;
        inSampleSize *= 2;
    }

    return inSampleSize;
}

If there is anything you need to know or have any optimizations i might be able to use to reduce memory usage, please write :) Thanks

nezbo
  • 61
  • 1
  • 4
  • i've created a nice JNI solution that avoids out-of-memory by removing the max heap size limitation barrier . [**here's a link**](http://stackoverflow.com/questions/14398670/android-rotating-a-bitmap-using-jni-ndk#comment20033361_14398670) to my snippet. some notes: - replace in the code every instance of "uint16_t" with "uint32_t" (that's the bug on my code i've asked about) . - input and output bitmap must be with 8888 config (which is ARGB ) - input bitmap will be recycled during the process . - the code rotates the image 90 degress counter clock wise . of course you can change it depending – android developer Jan 18 '13 at 13:17

1 Answers1

0

Try this snippet:

private Bitmap rotateImage(Context c, Bitmap bitmap, int rotate) {
    ....

    // reduce byte per pixel
    bitmap = bitmap.copy(Bitmap.Config.RGB_565, false);

    Bitmap.createBitmap(bitmap,...
}
animuson
  • 53,861
  • 28
  • 137
  • 147
smilkobuta
  • 17
  • 1