Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: rotate image without loading it to memory

Tags:

I am wondering is it possible to rotate an image stored on sdcard without loading it's to memory.

The reason is why I am looking for that is famous OutOfMemoryError. I know I can avoid it by downsampling large image but in fact I don't want to reduce size of that image, I want to have original image but rotated on 90 degrees.

Any suggestions about that are warmly appreciated :)

like image 390
endryha Avatar asked Aug 20 '12 20:08

endryha


People also ask

How do I rotate and save a picture in gallery?

Open a picture from the gallery and then press the menu button. This menu is only available when previewing a photo by itself. Now, select More from this menu. Editing choices will appear in the new pop-up menu, such as Details, Set as, Crop, Rotate Left, and Rotate Right.

Does rotating photos reduce quality?

Rotation by arbitrary angle always requires re-compression and also makes the picture softer, even if you use a non-compressed format (such as BMP) - the new pixels do not have exactly corresponding pixels on the non-rotated image and therefore have to be calculated based on weighted average of surrounding pixels.


2 Answers

enter image description here

For the 90-degrees rotations I really embrace RenderScript which is exactly designed to deal with bitmaps and is unexpectedly even faster than the default Bitmap.createBitmap(). The in-process bitmap isn't stored on the Java heap, therefore not pushing you into OutOfMemoryError.

After you set up the RenderScript support in your project with few lines, here is the RenderScript algorithm to use:

1) Create app\src\main\rs\rotator.rs RenderScript file with the following content.

#pragma version(1) #pragma rs java_package_name(ua.kulku.rs)  rs_allocation inImage; int inWidth; int inHeight;  uchar4 __attribute__ ((kernel)) rotate_90_clockwise (uchar4 in, uint32_t x, uint32_t y) {     uint32_t inX  = inWidth - 1 - y;     uint32_t inY = x;     const uchar4 *out = rsGetElementAt(inImage, inX, inY);     return *out; }  uchar4 __attribute__ ((kernel)) rotate_270_clockwise (uchar4 in, uint32_t x, uint32_t y) {     uint32_t inX = y;     uint32_t inY = inHeight - 1 - x;      const uchar4 *out = rsGetElementAt(inImage, inX, inY);     return *out; } 

Pay attention to ua.kulku.rs, that's some package name you choose for the auto-generate RS Java interface.

2) Reference it in your Java code:

import ua.kulku.rs.ScriptC_rotator;      public Bitmap rotate(Bitmap bitmap) {         RenderScript rs = RenderScript.create(mContext);         ScriptC_rotator script = new ScriptC_rotator(rs);         script.set_inWidth(bitmap.getWidth());         script.set_inHeight(bitmap.getHeight());         Allocation sourceAllocation = Allocation.createFromBitmap(rs, bitmap,                 Allocation.MipmapControl.MIPMAP_NONE,                 Allocation.USAGE_SCRIPT);         bitmap.recycle();         script.set_inImage(sourceAllocation);          int targetHeight = bitmap.getWidth();         int targetWidth = bitmap.getHeight();         Bitmap.Config config = bitmap.getConfig();         Bitmap target = Bitmap.createBitmap(targetWidth, targetHeight, config);         final Allocation targetAllocation = Allocation.createFromBitmap(rs, target,                 Allocation.MipmapControl.MIPMAP_NONE,                 Allocation.USAGE_SCRIPT);         script.forEach_rotate_90_clockwise(targetAllocation, targetAllocation);         targetAllocation.copyTo(target);         rs.destroy();         return target;     } 

enter image description here

For 180-degrees rotations, NDK solution outperformed RenderScript, as for me, due to making use of the sequential array item access, as the 180-degree rotation is actually the reversion of the image's pixel array. The NDK algorithm I've used in these comparisons is from https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperations . The in-process bitmap is also not stored on the Java heap, preventing OutOfMemoryError.

The stats bars indicate what I got in milliseconds on my Samsung S4 (Android 5.0) for the 13 MP photo.

like image 62
riwnodennyk Avatar answered Sep 18 '22 02:09

riwnodennyk


you should decode decode the images using Bitmap. you should follow Loading Large Image presented by google on how to do it.. it helped me alot, you'll notice the large difference in the RAM usage..

UPDATE if all you want is just rotate the image you can use this code

Matrix matrix = new Matrix(); matrix.setRotate(90); result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false); 

if you just need to set the image orientation (for example the photo orientation when it's was taken) you can use

ExifInterface exif = new ExifInterface(filePath); 

with the attribute ExifInterface.TAG_ORIENTATION I hope this helps you

like image 27
ColdFire Avatar answered Sep 18 '22 02:09

ColdFire