The images captured by the recent cameras often exceed 2 MB. Using such images frequently results in OutOfMemoryError. Also, due to landscape/portrait orientation, the images often are displayed as rotated. To deal with these issues we need to compress the image and give proper rotation before loading it to memory. The following method compresses image (similar to whatsapp), while maintaining its aspect ratio and also preventing significant loss to its quality.
public
String compressImage(String imageUri) {
String filePath = getRealPathFromURI(imageUri);
Bitmap scaledBitmap =
null
;
BitmapFactory.Options options =
new
BitmapFactory.Options();
// by setting this field as true, the actual bitmap pixels are not loaded in the memory. Just the bounds are loaded. If
// you try the use the bitmap here, you will get null.
options.inJustDecodeBounds =
true
;
Bitmap bmp = BitmapFactory.decodeFile(filePath, options);
int
actualHeight = options.outHeight;
int
actualWidth = options.outWidth;
// max Height and width values of the compressed image is taken as 816x612
float
maxHeight =
816
.0f;
float
maxWidth =
612
.0f;
float
imgRatio = actualWidth / actualHeight;
float
maxRatio = maxWidth / maxHeight;
// width and height values are set maintaining the aspect ratio of the image
if
(actualHeight > maxHeight || actualWidth > maxWidth) {
if
(imgRatio < maxRatio) { imgRatio = maxHeight / actualHeight; actualWidth = (
int
) (imgRatio * actualWidth); actualHeight = (
int
) maxHeight; }
else
if
(imgRatio > maxRatio) {
imgRatio = maxWidth / actualWidth;
actualHeight = (
int
) (imgRatio * actualHeight);
actualWidth = (
int
) maxWidth;
}
else
{
actualHeight = (
int
) maxHeight;
actualWidth = (
int
) maxWidth;
}
}
// setting inSampleSize value allows to load a scaled down version of the original image
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
// inJustDecodeBounds set to false to load the actual bitmap
options.inJustDecodeBounds =
false
;
// this options allow android to claim the bitmap memory if it runs low on memory
options.inPurgeable =
true
;
options.inInputShareable =
true
;
options.inTempStorage =
new
byte
[
16
*
1024
];
try
{
// load the bitmap from its path
bmp = BitmapFactory.decodeFile(filePath, options);
}
catch
(OutOfMemoryError exception) {
exception.printStackTrace();
}
try
{
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight,Bitmap.Config.ARGB_8888);
}
catch
(OutOfMemoryError exception) {
exception.printStackTrace();
}
float
ratioX = actualWidth / (
float
) options.outWidth;
float
ratioY = actualHeight / (
float
) options.outHeight;
float
middleX = actualWidth /
2
.0f;
float
middleY = actualHeight /
2
.0f;
Matrix scaleMatrix =
new
Matrix();
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
Canvas canvas =
new
Canvas(scaledBitmap);
canvas.setMatrix(scaleMatrix);
canvas.drawBitmap(bmp, middleX - bmp.getWidth() /
2
, middleY - bmp.getHeight() /
2
,
new
Paint(Paint.FILTER_BITMAP_FLAG));
// check the rotation of the image and display it properly
ExifInterface exif;
try
{
exif =
new
ExifInterface(filePath);
int
orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
0
);
Log.d(
"EXIF"
,
"Exif: "
+ orientation);
Matrix matrix =
new
Matrix();
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);
}
scaledBitmap = Bitmap.createBitmap(scaledBitmap,
0
,
0
,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,
true
);
}
catch
(IOException e) {
e.printStackTrace();
}
FileOutputStream out =
null
;
String filename = getFilename();
try
{
out =
new
FileOutputStream(filename);
// write the compressed bitmap at the destination specified by filename.
scaledBitmap.compress(Bitmap.CompressFormat.JPEG,
80
, out);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
return
filename;
}
The method getFilename():: It creates a folder in the SDCard used to store the images.
public
String getFilename() {
File file =
new
File(Environment.getExternalStorageDirectory().getPath(),
"MyFolder/Images"
);
if
(!file.exists()) {
file.mkdirs();
}
String uriSting = (file.getAbsolutePath() +
"/"
+ System.currentTimeMillis() +
".jpg"
);
return
uriSting;
}
The method getRealPathFromURI(imageUri):: Gives the actual filepath of the image from its contentUri:
private
String getRealPathFromURI(String contentURI) {
Uri contentUri = Uri.parse(contentURI);
Cursor cursor = getContentResolver().query(contentUri,
null
,
null
,
null
,
null
);
if
(cursor ==
null
) {
return
contentUri.getPath();
}
else
{
cursor.moveToFirst();
int
index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return
cursor.getString(index);
}
}
The method calculateInSampleSize:: calculates a proper value for inSampleSize based on the actual and required dimensions:
public
int
calculateInSampleSize(BitmapFactory.Options options,
int
reqWidth,
int
reqHeight) {
final
int
height = options.outHeight;
final
int
width = options.outWidth;
int
inSampleSize =
1
;
if
(height > reqHeight || width > reqWidth) {
final
int
heightRatio = Math.round((
float
) height/ (
float
) reqHeight);
final
int
widthRatio = Math.round((
float
) width / (
float
) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; }
final
float
totalPixels = width * height;
final
float
totalReqPixelsCap = reqWidth * reqHeight *
2
;
while
(totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
return
inSampleSize;
}
Comments
Post a Comment