In my app I am giving a functionality to edit an image from the filepath.
Now I want to add a feature to change the image orientation with animation and then save it.
I am able to change the orientation but don't know how to add animation to it.
Here's my code.
ImageView img;
Bitmap bmp;
Bitmap rotatedBMP;
String imageFilePath="";
File imgFile;
public void rotate(){
if(rotatedBMP == null){
if(imgFile != null && imgFile.exists())
bmp = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
}else
bmp = rotatedBMP;
// Getting width & height of the given image.
int w = bmp.getWidth();
int h = bmp.getHeight();
Matrix mtx = new Matrix();
mtx.preRotate(90);
rotatedBMP = Bitmap.createBitmap(bmp, 0, 0, w, h, mtx, true);
/*BitmapDrawable bmd = new BitmapDrawable(rotatedBMP);
img.setImageDrawable(bmd);*/
img.setImageBitmap(rotatedBMP);
}
There's a conflict between your desire to animate rotate a view, and your desire to rotate and save the actual bitmap.
If you want to rotate the image view, the good way to do that is to rotate the canvas in an onDraw method with an animation update step, or to use Android's helper for that like RotateAnimation. Drop us a comment if you want examples.
That's the "good way" because it doesn't create a new bitmap on every animation frame, which would be very expensive.
However, you've got a conflict that you also want to rotate the bitmap itself, rather than just the view, so as to save it. This is fine given it's not on every animation step.
You consequently will have to save the user's chosen transformations you want to apply to the bitmap as late as possible. Then animate the view however is appropriate in response to the user. On the save, you'd concat all the users transformations on writing to the file. Or give up on the idea of animating it.
My implementation idea would be to store a second matrix locally and have each user input both update the canvas transforms and that matrix. You'd then 'get hold' of the bitmap as it were when the save button is pressed and apply it to the updated transformation matrix.
Happy to elaborate as requested.
Some sources
Android: Rotate image in imageview by an angle (mentions issues with creating many bitmaps too)
Rotating a view in Android (gauravjain0102's answer is underrated!)
Related
In my Android app I have a custom ImageView which, based on user interaction, needs to update its image. The ImageView can only have two images, which represent the back and front of a card.
So, when the user clicks a button I need to show the back of the card (set my custom's ImageView image to the back of the card and when the user cliks the button again, set the front image as ImageView drawable).
In order to accomplish this I have two methods in my custom ImageView:
public void showBack() {
setImageResource(R.drawable.card_back);
}
public void showFront() {
setImageResource(R.drawable.card_front);
}
Of course, this works great, but the memory consumption of my app sometimes get near 114 MB, and it increases as the user clicks the button (rotates the card).
Due to that, I think the problem could be produced by the change of image.
The images are small, one is 104.6KB and the other 80.4KB (PNG format), so I don't understand why my app is consuming so much memory (maybe because images get allocated very times and never recycled?, or maybe because of Android's cache system of drawables?)
So, what can I do to solve my problem?
Thank you.
Image dimensions are 335 x 501
So there are a few factors that come into play here. The imageView dimensions (I assume) have been defined in dp . The number of pixels this translates to changes from phone to phone depending on the screen resolution. Hence the same drawable, when attached to imageView in different phones will have to be scaled to match the px required to display it in the imageView.
This scaling requires large amount of memory which causes the OOM exception. Some phones have a large RAM and can complete this scaling without causing any problems, whereas older models may not be able to.
When you load in the imageView, the original image is kept in memory and then rescaled. I suggest getting the pixel dimensions of the imageView and then scaling the image accordingly, to produce the best fit. This will reduce the memory consumption.
This answer and this should help you in getting the dimensions of the imageView.
Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_resource);
private Bitmap ScaleImage(Bitmap bitmap){
//the original dimension of the bitmap
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int requiredWidth=0;
int requiredHeight=0;
//todo: Calculate the required dimensions of the bitmap as per the imageView
ExifInterface exif;
Matrix matrix = new Matrix();
/* In case you need to rotate the bitmap
try {
exif = new ExifInterface(value);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
Log.d("EXIF", "Exif: " + orientation);
if (orientation == 6) {
matrix.postRotate(90);
Log.d("EXIF", "Exif: " + orientation);
} else if (orientation == 3) {
matrix.postRotate(180);
Log.d("EXIF", "Exif: " + orientation);
} else if (orientation == 8) {
matrix.postRotate(270);
Log.d("EXIF", "Exif: " + orientation);}
}catch (IOException e){e.printStackTrace();}
*/
matrix.postScale(requiredWidth/originalWidth, requiredHeight/originalHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,width, height, matrix, true);
return resizedBitmap;
}
And finally
public void showFront() {
Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_resource);
Bitmap scaledBitmap = ScaleImage(icon);
setImageResource(scaledBitmap);
}
Now ideally you would want to shift the scaling of the image to an Async Task so the main thread is not blocked.
Let me know if this works please.
The size of the images don't play much of a role here. The dimensions on the other hand do. So if you are using images with large dimensions and using them in ImageView with smaller resolution, then it will be a problem and you might end up having OOM (Out Of Memory Exception).
Please see:
http://developer.android.com/training/displaying-bitmaps/index.html
From my personal experience when loading images you should always use picasso library. It helps with all the memory problems, lets you scale easily etc.
http://square.github.io/picasso/
Simply put, I have a Bitmap resource that is 1800 x 1800 pix due to the detail I need. I create a canvas from the Bitmap and then draw on it. After drawing is complete, It's attached to an ImageView. It works fine on devices with large Heaps but on small devices, it crashes. The Bitmap needs to be the same size for all devices when added to the canvas because the coordinates that I draw to are precise locations on the Bitmap.
Here is my code
initialBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.VeryLargeBitmap);
mutableBitmap = initialBitmap.copy(Bitmap.Config.RGB_565, true);
canvas = new Canvas(mutableBitmap);
....draw stuff here
canvas.drawLine(x, y, x2, y2, paint);
ImageView.setImageBitmap(mutableBitmap);
ImageView..setAdjustViewBounds(true);
I'm sure there is a better way. I have looked into OpenGL but have not tried it yet. It looks to complex for what I'm trying to accomplish.
BitmapFactory.Options o = new BitmapFactory.Options();
o.inMutable = true;
initialBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.VeryLargeBitmap, o);
Doing this should remove the need to copy the bitmap to an immutable one. When you're done with it (saved to file or ready for a new one) do this:
initialBitmap.recycle();
initialBitmap = null;
To remove any reference to it (NOTE: recycle may not be necessary but I like it "to make sure").
EDIT:
Special note is that creating a Bitmap is CPU intensive so it'd be best to decode it in a thread and start drawing when it's ready. You should never create a Bitmap in an onDraw or draw method.
I have searched and found simple code to rotate an image. I am pulling the image out of an ImageView object into a bitmap, rotating it then putting it back. I realize this is not the most effective method but I don't think it should crash without giving an error message in the CATCH block.
Here is my code. The only value passed in is "r" or "l" depending on which direction I want to rotate. Smaler images (1500x1500 or smaller) work just fine. Things go bad around the 2500x2500 size.
public void rotate(String dir)
{
try
{
float angle = (dir.equals("r") ? 90 : -90);
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Matrix matrix = new Matrix();
matrix.reset();
matrix.postRotate(angle);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
imageView.setImageBitmap(bitmap);
}
catch(Exception e)
{
Utilities.logError(e.toString());
}
}
Any clue as to why it is crashing and why it doesn't thow an exception? I just get a message "Unfortuantly process .... has stopped" and I get kicked back to the welcome screen of my app.
Oh, for kicks I set the angle to ZERO (hard coded) and it didn't crash. I suspect that it is just taking too long to rotate and Android is having a fit. But I am not sure how to confirm that as the problem or how to tell Android to wait a little longer.
Even if I reduce the preview image for the rotation, when I go to save I will have to rotate the full size image at least once and will hit this same issue. Won't I?
I can more or less guarantee without looking at the logs that you're getting an Out Of Memory Exception.
You need to use smaller images, or use a different method to rotate that doesn't use up so much memory (you're allocating 2 2500x2500 bitmaps at the same time here! that's tons!).
Try using a RotateAnimation to get your effect instead.
Hope this helps :)
I have a big Memory Problem:
// in sourceImage is a big JPEG previously loaded
Matrix mat = new Matrix();
mat.postRotate(90);
Bitmap rotatedImage = Bitmap.createBitmap(sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight(), mat, true);
Always i Run this code, my App crashes and says "VM won't let us allocate xxxxxx bytes"
Can you help me?
Edit:
I saw much similar questions here, but i don't know how to recycle the sourceImage before rotatating it... (cause the second instance is to big to hold it at the same time)
Thanks.
You can't create a new rotated bitmap without holding temporary 2 bitmaps in memory.
But you can display the bitmap rotated without creating a new bitmap (apply a transformation).
ImageView does not come with rotation capabilities, so you should write your own extended version of ImageView (RotatedImageView?).
The idea is to override the onDraw method with something like this (not tested).
#Override
public void onDraw(Canvas canvas) {
canvas.rotate((int)(angle * 180 / Math.PI), getWidth() >> 1, getHeight() >> 1);
super.onDraw(canvas);
}
For others like me:
there's an option to have the camera perform the rotation,
https://stackoverflow.com/a/16010289/755804
Hello I am trying to place acouple bitmaps on the screen and rotate them. I can get this to work by doing
canvas.drawBitmap(pic2, rotatePic, null);
rotatePic is matrix with
postRotate(5, pic2.getHieght()/2, pic2.getWidth()/2)
this rotates the pic and puts it at 0, 0 so to place it i tried
Bitmap topPic = Bitmap.createBitmap(pic2, 0, 0, pic2.getWidth(),
pic2.getHeight(), rotatePic, false);
than place with
canvas.drawBitmap(topPic, 200, 100, null);
it places it correctley but it no longer rotates correctley it looks like it is bouncing and spinning I've tried everthing
You can alternatively try rotating the canvas itself, using
canvas.rotate(degress, px, py);
Here's a link to the Android reference page for this:
Canvas.rotate();
Hope this helps!!