I have a large bitmap (say 3888x2592) in a file. Now, I want to resize that bitmap to 800x533 and save it to another file.
I normally would scale the bitmap by calling
Above code made a little cleaner. InputStreams have finally close wrapping to ensure they get closed as well:
*Note
Input: InputStream is, int w, int h
Output: Bitmap
try
{
final int inWidth;
final int inHeight;
final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");
{
final FileOutputStream tempOut = new FileOutputStream(tempFile);
StreamUtil.copyTo(is, tempOut);
tempOut.close();
}
{
final InputStream in = new FileInputStream(tempFile);
final BitmapFactory.Options options = new BitmapFactory.Options();
try {
// decode image size (decode metadata only, not the whole image)
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
}
finally {
in.close();
}
// save width and height
inWidth = options.outWidth;
inHeight = options.outHeight;
}
final Bitmap roughBitmap;
{
// decode full image pre-resized
final InputStream in = new FileInputStream(tempFile);
try {
final BitmapFactory.Options options = new BitmapFactory.Options();
// calc rought re-size (this is no exact resize)
options.inSampleSize = Math.max(inWidth/w, inHeight/h);
// decode full image
roughBitmap = BitmapFactory.decodeStream(in, null, options);
}
finally {
in.close();
}
tempFile.delete();
}
float[] values = new float[9];
{
// calc exact destination size
Matrix m = new Matrix();
RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
RectF outRect = new RectF(0, 0, w, h);
m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
m.getValues(values);
}
// resize bitmap
final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);
return resizedBitmap;
}
catch (IOException e) {
logger.error("Error:" , e);
throw new ResourceException("could not create bitmap");
}
I don't know if my solution is best practice, but I achieved loading a bitmap with my desired scaling by using the inDensity
and inTargetDensity
options. inDensity
is 0
initially when not loading a drawable resource, so this approach is for loading non resource images.
The variables imageUri
, maxImageSideLength
and context
are parameters of my method. I posted only the method implementation without the wrapping AsyncTask for clarity.
ContentResolver resolver = context.getContentResolver();
InputStream is;
try {
is = resolver.openInputStream(imageUri);
} catch (FileNotFoundException e) {
Log.e(TAG, "Image not found.", e);
return null;
}
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, opts);
// scale the image
float maxSideLength = maxImageSideLength;
float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
// do not upscale!
if (scaleFactor < 1) {
opts.inDensity = 10000;
opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
}
opts.inJustDecodeBounds = false;
try {
is.close();
} catch (IOException e) {
// ignore
}
try {
is = resolver.openInputStream(imageUri);
} catch (FileNotFoundException e) {
Log.e(TAG, "Image not found.", e);
return null;
}
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
try {
is.close();
} catch (IOException e) {
// ignore
}
return bitmap;
When i have large bitmaps and i want to decode them resized i use the following
BitmapFactory.Options options = new BitmapFactory.Options();
InputStream is = null;
is = new FileInputStream(path_to_file);
BitmapFactory.decodeStream(is,null,options);
is.close();
is = new FileInputStream(path_to_file);
// here w and h are the desired width and height
options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
// bitmap is the resized bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
This worked for me. The function gets a path to a file on the sd card and returns a Bitmap in the maximum displayable size. The code is from Ofir with some changes like image file on sd instead a Ressource and the witdth and heigth are get from the Display Object.
private Bitmap makeBitmap(String path) {
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
//resource = getResources();
// Decode image size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int scale = 1;
while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight);
Bitmap pic = null;
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
options = new BitmapFactory.Options();
options.inSampleSize = scale;
pic = BitmapFactory.decodeFile(path, options);
// resize to desired dimensions
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.y;
int height = size.x;
//int height = imageView.getHeight();
//int width = imageView.getWidth();
Log.d("TAG", "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(pic, (int) x, (int) y, true);
pic.recycle();
pic = scaledBitmap;
System.gc();
} else {
pic = BitmapFactory.decodeFile(path);
}
Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
return pic;
} catch (Exception e) {
Log.e("TAG", e.getMessage(),e);
return null;
}
}
Justin answer translated to code (works perfect for me):
private Bitmap getBitmap(String path) {
Uri uri = getImageUri(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = mContentResolver.openInputStream(uri);
// Decode image size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
in.close();
int scale = 1;
while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ",
orig-height: " + options.outHeight);
Bitmap resultBitmap = null;
in = mContentResolver.openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
options = new BitmapFactory.Options();
options.inSampleSize = scale;
resultBitmap = BitmapFactory.decodeStream(in, null, options);
// resize to desired dimensions
int height = resultBitmap.getHeight();
int width = resultBitmap.getWidth();
Log.d(TAG, "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(resultBitmap, (int) x,
(int) y, true);
resultBitmap.recycle();
resultBitmap = scaledBitmap;
System.gc();
} else {
resultBitmap = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " +
resultBitmap.getHeight());
return resultBitmap;
} catch (IOException e) {
Log.e(TAG, e.getMessage(),e);
return null;
}
After reading these answers and android documentation here's the code to resize bitmap without loading it into memory:
public Bitmap getResizedBitmap(int targetW, int targetH, String imagePath) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
//inJustDecodeBounds = true <-- will not load the bitmap into memory
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
return(bitmap);
}