I am following the tutorial here to take picture with other app AND with customized SurfaceView.
When take picture with SurfaceView, the picture is taken successfully (I quit my app and saw the result image file does exist in file manager, and the image content is correct.), but the picture cannot show correctly in my app. The ImageView shows nothing.
My code is like this:
public void onPictureTaken(byte[] data, Camera camera) {
try {
File file = Utils.getOutputMediaFile(Utils.MediaFileType.Image);
FileOutputStream os = new FileOutputStream(file);
os.write(data);
os.flush();
os.close();
final Uri uri = Uri.fromFile(file);
showImage(uri);
} catch (FileNotFoundException e) {
Log.d(TAG, "onPictureTaken, e=" + e);
} catch (IOException e) {
Log.d(TAG, "onPictureTaken, e=" + e);
}
camera.startPreview();
}
private void showImage(Uri imageFileUri) {
int w = mContentContainer.getWidth();
int h = mContentContainer.getHeight();
Bitmap bmp = Utils.loadBitmapFromFile(imageFileUri.getPath(), w, h);
mImageView.setImageBitmap(bmp);
mStatusTextView.setText("take photo: succcess");
}
public static Bitmap loadBitmapFromFile(String filename, int maxWidth, int maxHeight) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, opt);
Log.d(TAG, "loadBitmapFromFile, w=" + opt.outWidth + ", h=" + opt.outHeight);
int widthRatio = (int) Math.ceil(opt.outWidth / maxWidth);
int heightRatio = (int) Math.ceil(opt.outHeight / maxHeight);
if (widthRatio > 1 || heightRatio > 1) {
if (widthRatio > heightRatio) {
opt.inSampleSize = widthRatio;
} else {
opt.inSampleSize = heightRatio;
}
}
opt.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(filename, opt);
Log.d(TAG, "loadBitmapFromFile, bmp=" + bmp);
return bmp;
}
From log, I saw the width and height is correctly loaded from file, and bmp is not null, but the ImageView is just empty.
Strange is, if my app firstly take a photo and show the photo with showImage() (the ImageView shows photo correctly), then after that, take phone with SurfaceView and show with showImage(), the photo shows correctly. But if directly take phone with SurfaceView and showImage(), the ImageView is empty.
Any comments about why the ImageView is empty? Thanks.
Try (see the comments):
private void showImage(Uri imageFileUri) {
int w = mContentContainer.getWidth();
int h = mContentContainer.getHeight();
Bitmap bmp = Utils.loadBitmapFromFile(imageFileUri.getPath(), w, h);
mImageView.requestLayout(); //try to request the layout first
mImageView.setImageBitmap(bmp);
//if its still not working try to call invalidate() method here
mStatusTextView.setText("take photo: succcess");
}
Related
I capture photo with default camera by calling
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, ApplicationData.CAMERA_REQUEST);
Then I save the photo to sdcard and retrieve it and set it to ImageView
OutputStream output;
Bitmap photo = (Bitmap) data.getExtras().get("data");
output = new FileOutputStream(file);
photo.compress(Bitmap.CompressFormat.PNG, 100, output);
output.flush();
output.close();
mThumbnaiImagelLayout.setVisibility(View.VISIBLE);
mImageThumbNail.setImageBitmap(photo);
What I want in ImageView is exactly the same like thumbnail image of the device.For example:
But it become like this
So my question is:
1/How can I resize captured image?
2/Is any other way I can set image to ImageView without bitmap? Because when I set ImageView with Bitmap, it look terrible, like this (the image's size is 512x512)
Please help me to solve this problem.Thank you!
Try this
android:scaleType="fitCenter"
If you use Bundle extras = data.getExtras(); in your onActivityResult then it will return thumbnail image not actual image.
Here is code I have used for Capturing and Saving Camera Image then display it to imageview.
Here is method for opening capturing camera image activity.
private static final int CAMERA_PHOTO = 111;
private Uri imageToUploadUri;
private void captureCameraImage() {
Intent chooserIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File f = new File(Environment.getExternalStorageDirectory(), "POST_IMAGE.jpg");
chooserIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
imageToUploadUri = Uri.fromFile(f);
startActivityForResult(chooserIntent, CAMERA_PHOTO);
}
then your onActivityResult() method should be like this.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAMERA_PHOTO && resultCode == Activity.RESULT_OK) {
if(imageToUploadUri != null){
Uri selectedImage = imageToUploadUri;
getContentResolver().notifyChange(selectedImage, null);
Bitmap reducedSizeBitmap = getBitmap(imageToUploadUri.getPath());
if(reducedSizeBitmap != null){
imageview.setImageBitmap(reducedSizeBitmap);
}else{
Toast.makeText(this,"Error while capturing Image",Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(this,"Error while capturing Image",Toast.LENGTH_LONG).show();
}
}
}
Here is getBitmap() method used in onActivityResult().
private Bitmap getBitmap(String path) {
Uri uri = Uri.fromFile(new File(path));
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = getContentResolver().openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d("", "scale = " + scale + ", orig-width: " + o.outWidth + ", orig-height: " + o.outHeight);
Bitmap b = null;
in = getContentResolver().openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
b = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = b.getHeight();
int width = b.getWidth();
Log.d("", "1th scale operation dimenions - width: " + width + ", height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x,
(int) y, true);
b.recycle();
b = scaledBitmap;
System.gc();
} else {
b = BitmapFactory.decodeStream(in);
}
in.close();
Log.d("", "bitmap size - width: " + b.getWidth() + ", height: " +
b.getHeight());
return b;
} catch (IOException e) {
Log.e("", e.getMessage(), e);
return null;
}
}
I'm trying to take an image captured from the camera and place a photoframe or overlay over it. The photoframe is basically a png image placed in an ImageView within a FrameLayout that also contains the SurfaceView. The problem is that I must scale either or both of the resulting bitmaps of the view containing the photo image and the photo frame image in order for the overlay to be placed in exactly the correct position over the captured photo image. But since they have different aspect ratios, I'm at a loss to figure out how to do this without either the photo or the overlay from getting distorted. Here is my code that handles the bitmap manipulations. In addition, I sometimes get OutofMemory exceptions due to the huge size of the bitmaps. I tried to use MappedByteBufferbut couldn't get that to work right either... sigh. Anyway, any suggestions on what I'm doing wrong or code samples that show a better way to accomplish this are greatly appreciated!
private void saveTempPhoto(byte[] data) {
// Need to flip back the photo frame on front facing camera.
if (this.isCameraFront)
this.flipPhotoFrame();
findViewById(R.id.close_button).setVisibility(View.INVISIBLE);
findViewById(R.id.flash_button).setVisibility(View.INVISIBLE);
findViewById(R.id.focus_button).setVisibility(View.INVISIBLE);
findViewById(R.id.take_photo).setVisibility(View.INVISIBLE);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inTempStorage = new byte[32 * 1024];
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = true;
Bitmap bitmapPhoto = null;
Bitmap bitmapPhotoFrame = null;
Bitmap bitmapCanvas = null;
View view = findViewById(R.id.top_photo_frame); //view containing the photoframe
try {
int photoFrameWidth = view.getWidth();
int photoFrameHeight = view.getHeight();
BitmapFactory.decodeByteArray(data, 0, data.length, options);
if (orientation == 90 || orientation == 270) {
//Swap height and width
int temp = options.outWidth;
options.outWidth = options.outHeight;
options.outHeight = temp;
}
// Calculate the best sample size to use based on the ratio of the captured image to the photoframe.
// This is done to prevent OutofMemoryExceptions from occurring, as the bitmap allocations can use up a lot of heap space.
float ratioWidth = (float)options.outWidth / (float)photoFrameWidth;
float ratioHeight = (float)options.outHeight / (float)photoFrameHeight;
float ratio = Math.min(ratioWidth, ratioHeight);
if (ratioWidth > 1 || ratioHeight > 1) {
double power = Math.log(ratio) / Math.log(2);
options.inSampleSize = (int) Math.pow(2, Math.round(power));
}
options.inJustDecodeBounds = false;
Bitmap bitmapPhotoPreRotate = BitmapFactory.decodeByteArray(data, 0, data.length, options);
int postRotation = isCameraFront ? -orientation : orientation;
if (orientation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(postRotation);
bitmapPhoto = Bitmap.createBitmap(bitmapPhotoPreRotate, 0, 0, bitmapPhotoPreRotate.getWidth(), bitmapPhotoPreRotate.getHeight(), matrix, true);
bitmapPhotoPreRotate.recycle();
}
else
bitmapPhoto = bitmapPhotoPreRotate;
Log.d("PhotoFrameActivity", String.format("Photo bitmap has width %d and height %d", bitmapPhoto.getWidth(), bitmapPhoto.getHeight()));
Log.d("PhotoFrameActivity", String.format("PhotoFrame bitmap has width %d and height %d", view.getWidth(), view.getHeight()));
int photoWidth = bitmapPhoto.getWidth();
int photoHeight = bitmapPhoto.getHeight();
Bitmap.Config photoConfig = bitmapPhoto.getConfig();
bitmapCanvas = Bitmap.createBitmap(photoWidth,
photoHeight, photoConfig);
if (bitmapCanvas != null) {
Canvas canvas = new Canvas(bitmapCanvas);
canvas.drawBitmap(bitmapPhoto, new Matrix(), null);
bitmapPhoto.recycle();
bitmapPhoto = null;
System.gc(); //Try to force GC here to free up some memory
bitmapPhotoFrame = Bitmap.createScaledBitmap(
this.loadBitmapFromView(view),
photoWidth,
photoHeight,
true);
canvas.drawBitmap(bitmapPhotoFrame, 0, 0, null);
bitmapPhotoFrame.recycle();
Log.d("PhotoFrameActivity", String.format("Combined bitmap has width %d and height %d", bitmapCanvas.getWidth(), bitmapCanvas.getHeight()));
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmapCanvas.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] jpegWithPhotoFrame = stream.toByteArray();
try {
createPhotoFile();
FileOutputStream fos = new FileOutputStream(photoFile);
fos.write(jpegWithPhotoFrame);
fos.close();
Log.d("PhotoFrameActivity", String.format("Image file saved to %s", photoFile.getPath()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (bitmapCanvas != null)
bitmapCanvas.recycle();
if (bitmapPhoto != null)
bitmapPhoto.recycle();
if (bitmapPhotoFrame != null)
bitmapPhotoFrame.recycle();
}
}
catch (OutOfMemoryError e) {
// Put up out of memory alert
AlertDialog dialogError = new AlertDialog.Builder(this).create();
dialogError.setButton(DialogInterface.BUTTON_POSITIVE,"OK",
new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
finish();
}
}
);
dialogError.setMessage("Out of memory!");
dialogError.show();
}
catch (Exception e) {
e.printStackTrace();
}
}
I am Getting error on my code : Out of Memory Error
Below is my code :
public class ViewFullImage extends Activity {
//Bitmap bitmap;
private Bitmap bitmap;
private ImageView iv;
private String ImgFile_Name;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.view_draw_picture);
Log.v("","===================== viewFullImage.java================");
try{
String path = "mfc/cam_img/";
int s_id=getIntent().getIntExtra("s_id", -1);
int intentKey=getIntent().getIntExtra("iv", -1);
iv = (ImageView)findViewById(R.id.iv_display);
File Dir= Environment.getExternalStorageDirectory();
File imageDirectory = new File(Dir,path);
File file = new File(imageDirectory, "img_"+s_id+"_"+intentKey+".jpg");
ImgFile_Name = file.getAbsolutePath();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 1;
BitmapFactory.decodeFile(file.getAbsolutePath(),options);
int h = options.outHeight;
int w = options.outWidth;
Log.v("","This is h : "+h);
Log.v("","This is w : "+w);
bitmap = BitmapFactory.decodeFile(ImgFile_Name,options);
iv = (ImageView)findViewById(R.id.image);
if(h<w)
{
iv.setImageBitmap(rotateBitmap(bitmap));
}
else{
iv.setImageBitmap(bitmap);
}
}catch (Exception e) {
// TODO: handle exception
Log.v("","Exception : "+e);
}
}
Bitmap rotateBitmap(Bitmap bitmap)
{
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap bitmap1 = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(),
matrix, true);
bitmap.recycle();
bitmap=null;
return bitmap1;
}
}
In this class i am trying to displaying image from sdcard. I am calling this Activity from other activities Like :
Intent intent =new Intent(cxt,ViewFullImage.class);
intent.putExtra("iv", 8);
intent.putExtra("s_id", s_id);
startActivity(intent);
Please somebody tell where i m doing mistack.....
You aren't doing anything wrong, you can't just allocate an image (or even a large string) in Android without knowing if it's going to fit in memory.
The problem happens in Android 4 devices because there is more 4.0 devices out there with only 16MB heaps. The memory heap is the memory where you will open your image in.
To solve this issue you should scale your image if required (depending both on the heap size and image size). Below is the code for how to decode with scaling:
public static Bitmap decodeSampleImage(File f, int width, int height) {
try {
System.gc(); // First of all free some memory
// 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 requiredWidth = width;
final int requiredHeight = height;
// Find the scale value (as a power of 2)
int sampleScaleSize = 1;
while (o.outWidth / sampleScaleSize / 2 >= requiredWidth && o.outHeight / sampleScaleSize / 2 >= requiredHeight)
sampleScaleSize *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = sampleScaleSize;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (Exception e) {
Log.d(TAG, e.getMessage()); // We don't want the application to just throw an exception
}
return null;
}
The other thing is, if you don't free some memory (System.gc()) and you click/switch between many pictures you may run out of memory anyway. If you run out of memory you don't want to blow the app, instead manage the case where the Bitmap is null. You can improve the Exception's catch cases for that as well.
Hope it helps.
My Problem
I take a picture with my android device. I then decode that picture from file.
Bitmap photo = BitmapFactory.decodeFile(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX);
if (photo == null && data != null)
photo = (Bitmap) data.getExtras().get("data");
else if (data == null && photo == null)
Log.e("CCPhotoManager","Can't find image from file or from intent data.");
I then check that picture and see whether it needs to be rotated to the correct orientation.
try {
ExifInterface exif = new ExifInterface(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX);
int rotation = CCDataUtils.exifToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL));
Log.v("CCPhotoManager", "Rotation:"+rotation);
if (rotation > 0) {
photo = this.convertSavedImageToCorrectOrientation(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX, photo, rotation);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
If it does need rotating I call this method.
public Bitmap convertSavedImageToCorrectOrientation(String filePath,Bitmap photo,int rotation) {
Log.d("CCPhotoManager", "Changing Orientation of photo located at: "+filePath+" Rotating by:"+rotation);
int width = photo.getWidth();
int height = photo.getHeight();
Matrix matrix = new Matrix();
matrix.preRotate(rotation);
Bitmap adjusted = Bitmap.createBitmap(photo, 0, 0, width, height, matrix, true);
try {
FileOutputStream out = new FileOutputStream(filePath);
adjusted.compress(Bitmap.CompressFormat.JPEG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
return adjusted;
}
I am getting Out of Memory complaints if the convertSavedImageToCorrectOrientation is called on the line Bitmap adjusted = Bitmap.createBitmap(photo,0,0,width,height,matrix,true);
This is only the case on the Samsung Galaxy S3. It works fine on the Samsung Galaxy Ace, HTC Hero and the Sony Xperia U.
Here is the error.
10-17 14:33:33.950: E/AndroidRuntime(12556): java.lang.OutOfMemoryError
10-17 14:33:33.950: E/AndroidRuntime(12556): at android.graphics.Bitmap.nativeCreate(Native Method)
10-17 14:33:33.950: E/AndroidRuntime(12556): at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
10-17 14:33:33.950: E/AndroidRuntime(12556): at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
It's a massive amount of memory too.
10-17 14:33:33.945: E/dalvikvm-heap(12556): Out of memory on a 31961104-byte allocation.
I think its something to do with the amount of Bitmaps around but I'm not sure how to stop this error from happening.
I know you can call .recycle(); on them but it doesn't seem to work.
My Question
How do I correctly handle my Bitmaps so I don't have this OOM problem?
Thanks in advance
For out of memory issue
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
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_SIZE=70;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
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;
}
I had the exact same issue, doing the exact same thing on the exact same device! Unfortunately in my case the image needed to be submitted to a webservice, using the full size, original. What worked for me was turning on largeHeap in the application element of the manifest.
This will not solve the issue permanently - its possible that a device will come along with an even larger camera and the images will not fit in memory even with largeHeap enabled. To catch this extreme edge case I also put a try catch around the code that rotates the image, and just displayed a nice error to the user.
A fuller solution would be to write your own jpeg manipulation code that can rotate a jpeg using a stream based approach so that the image never needs to be loaded into memory.
Here is a more complete example of how to resize/rotate, taken in part from the Android Developers guide (change REQ_WIDTH and REQ_HEIGHT):
private static final int REQ_WIDTH = 450;
private static final int REQ_HEIGHT = 450;
/**
* Resize, crop, rotate and Inserts the picture on the layout.
*
* #param mImageView to insert the bitmap.
* #param imageURI from wich to obtain the bitmap.
*
*/
private void setPic(ImageView mImageView, String imageURI) {
// Get the original bitmap dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageURI, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, REQ_HEIGHT, REQ_WIDTH);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imageURI, options);
//need rotation?
float rotation = rotationForImage(getActivity(), Uri.fromFile(new File(imageURI)));
if (rotation != 0) {
//rotate
Matrix matrix = new Matrix();
matrix.preRotate(rotation);
mImageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, REQ_HEIGHT, REQ_WIDTH, matrix, true));
} else {
//use the original
mImageView.setImageBitmap(BitmapFactory.decodeFile(imageURI, options));
}
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
public static float rotationForImage(Context context, Uri uri) {
try {
if (uri.getScheme().equals("content")) {
String[] projection = { Images.ImageColumns.ORIENTATION };
Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
if (c.moveToFirst()) {
return c.getInt(0);
}
} else if (uri.getScheme().equals("file")) {
ExifInterface exif = new ExifInterface(uri.getPath());
int rotation = (int) exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL));
return rotation;
}
return 0;
} catch (IOException e) {
Log.e(TAG, "Error checking exif", e);
return 0;
}
}
private static float exifOrientationToDegrees(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;
}
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:
Load in the image
Check if it needs to be rotated
Loads a scaled-down version for display in an ImageView
Rotates the small image if necessary
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
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,...
}