Is there anyway to resize/scale bitmap without creating a new bitmap? Let say i download image that height or width is larger than 2048px. Before i can display it, i have to resize it because ImageView does not support bitmaps larger than 2048px. If i use Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) it gives me a new bitmap. Now we have two bitmaps, original and scaled. Thats when my application runs out of memory.
Below is the code which i use right now.
// Async task to download the image
private ImageView mImage;
private ProgressBar progress;
private Button button;
#Override
protected void onPostExecute(Bitmap result){
progress.setVisibility(View.GONE);
if(result != null){
if(result.getHeight() > 2048 || result.getWidth() > 2048){
float scaledvalues[] = scale(result.getWidth(), result.getHeight());
image = Bitmap.createScaledBitmap(result, (int)scaledvalues[0], (int)scaledvalues[1], false);
mImage.setBitmap(image);
}
else{
image = result;
mImage.setBitmap(result);
}
button.setEnabled(true);
}
}
//I use this method to calculate new width and height
public float[] scale(int width, int height){
float scaledheight = -1f;
float scaledwidth = -1f;
float scaledheightpros = -1f;
float scaledwidthpros = -1f;
float finalheight = -1f;
float finalwidth = -1f;
if(height > 2048){
scaledheight = height - 2048f;
float s = scaledheight*100f;
scaledheightpros = s / 100f;
}
if(width > 2048){
scaledwidth = width - 2048f;
float z = scaledwidth * 100f;
scaledwidthpros = z / width;
}
if(scaledheightpros > scaledwidthpros){
float a = height/100f;
float b = width/100f;
finalheight = height - (a * scaledheightpros);
finalwidth = width - (b * scaledheightpros);
}
else{
float a = height/100f;
float b = width/100f;
finalheight = height - (a * scaledwidthpros);
finalwidth = width - (b * scaledwidthpros);
}
Log.i(TAG, "startingheight: " + height + " finalheight: " + finalheight + "%: " + scaledheightpros);
Log.i(TAG, "startingwidth: " + width + " finalwidth: " + finalwidth + "%: " + scaledwidthpros);
float array[] = {finalwidth, finalheight};
return array;
}
use sampling if you facing out of memory issue.
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
At the end for setting image in Imageview, use this
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.id.myimage,
Screen_width, screen_height));
Must read this
Related
I am downsampling image with following code
private static Bitmap downsample(String path) {
final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inTargetDensity = 1;
bitmapOptions.inSampleSize = 1;
bitmapOptions.inDensity = 1;
bitmapOptions.inTargetDensity = 1;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
int reqWidth = 800, reqHeight = 800;
final int newWidth = imageWidth / bitmapOptions.inSampleSize, newHeight =
imageHeight / bitmapOptions.inSampleSize;
if (newWidth > reqWidth || newHeight > reqHeight) {
if (newWidth * reqHeight > newHeight * reqWidth) {
// prefer width, as the width ratio is larger
bitmapOptions.inTargetDensity = reqWidth;
bitmapOptions.inDensity = newWidth;
} else {
// prefer height
bitmapOptions.inTargetDensity = reqHeight;
bitmapOptions.inDensity = newHeight;
}
}
final Bitmap scaledBitmap = BitmapFactory.decodeFile(path, bitmapOptions);
scaledBitmap.setDensity(Bitmap.DENSITY_NONE);
return scaledBitmap;
}
and load with glide
Glide.with(getActivity())
.load(MainActivity.mProfile.getImg())
.override(avatarSize, avatarSize)
.centerCrop()
.error(R.drawable.placeholder_photo)
.into(imAvatar);
However resulting bitmap i rotated 90 degree if original height bigger then width.
I am developing an android application in which i want to set a Banner image on top of the screen which will come from server, i need to resize it dynamically according to the device size programmatically.
try this,
int iWidth=bmp.getWidth();
int iHeight=bmp.getHeight();
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
int dWidth=dm.widthPixels;
int dHeight=dm.heightPixels;
float sWidth=((float) dWidth)/iWidth;
float sHeight=((float) dHeight)/iHeight;
if(sWidth>sHeight) sWidth=sHeight;
else sHeight=sWidth;
Matrix matrix=new Matrix();
matrix.postScale(sWidth,sHeight);
newImage=Bitmap.createBitmap(bmp, 0, 0, iWidth, iHeight, matrix, true);
imageview.setImageBitmap(newImage);
You could check out the scale attributes of the ImageView in xml first
And resize using this
public static Bitmap getResizedBitmap(Bitmap image)
{
return Bitmap.createScaledBitmap(image, x, y, true);
}
Assuming you want to set an ImageView declared in your layout (for example with properties android:layout_width="match_parent" android:layout_height="wrap_content") you can adapt the following code:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // just compute size, don't create bitmap
BitmapFactory.decodeStream(stream, null, options); // stream is the InputStream representing the image you want to display
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
imageViewWidth = imageView.getWidth(); // imageView is the View container for the image you want to display
imageViewHeight = imageView.getHeight();
// Compute the sample size
double sampleSize = computePowerOfTwoInSampleSize(options, imageViewWidth, imageViewHeight);
if(sampleSize<1) {
options.inSampleSize = 1;
}
else {
options.inSampleSize = (int) sampleSize;
}
options.inJustDecodeBounds = false; // compute size and create bitmap
bitmap = BitmapFactory.decodeStream(stream, null, options);
imageView.setImageBitmap(bitmap);
try {
stream.close();
}
catch (IOException e) {
e.printStackTrace();
}
where the method computePowerOfTwoInSampleSize is something like this (take into consideration that resizing to power of 2 is more efficient compared to other values):
private double computePowerOfTwoInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
int height = options.outHeight;
int width = options.outWidth;
double inSampleSize = 1;
// compute the up-sample coefficient (power of 2)
if(height < reqHeight && width < reqWidth) {
while(height < reqHeight && width < reqWidth) {
height = height*2;
width = width*2;
if(height < reqHeight && width < reqWidth) {
inSampleSize = inSampleSize/2;
}
}
}
// compute the down-sample coefficient
else {
while(height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width (power of 2)
height = Math.round((float)height/2);
width = Math.round((float)width/2);
inSampleSize = inSampleSize*2;
}
}
return inSampleSize;
}
I am having a strange behaviour when trying to decode a photo of the size 2448x2448 pixels. In code, I am calculating that a inSampleSize of 6 should be applied (based on the required size of the resulting bitmap) and when I call BitmapFactory.decodeStream with those options I am expecting a bitmap like this:
full_photo_width = 2448
full_photo_height = 2448
inSampleSize = 6
expected_width = (2448 / 6) = 408
expected_height (2448 / 6) = 408
actual_width = 612
actual_height = 612
Here is the code:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
int photo_width = options.outWidth;
int photo_height = options.outHeight;
float rotation = rotationForImage(this, uri);
if (rotation != 0f) {
// Assume the photo is portrait oriented
matrix.preRotate(rotation);
float photo_ratio = (float) ((float)photo_width / (float)photo_height);
frame_height = (int) (frame_width / photo_ratio);
} else {
// Assume the photo is landscape oriented
float photo_ratio = (float) ((float)photo_height / (float)photo_width);
frame_height = (int) (frame_width * photo_ratio);
}
int sampleSize = calculateInSampleSize(options, frame_width, frame_height);
if ((sampleSize % 2) != 0) {
sampleSize++;
}
options.inSampleSize = sampleSize;
options.inJustDecodeBounds = false;
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And the calculateInSampleSize function:
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// We round the value to the highest, always.
if ((height / inSampleSize) > reqHeight || (width / inSampleSize > reqWidth)) {
inSampleSize++;
}
}
return inSampleSize;
}
The code works for all the photos for all the photos and decodeStream is returning a bitmap with the correct size (depending on the calculated inSampleSize) in all the cases, except with a particular photo. Am I missing something here?
Thanks!
Please refer to official API documentation: inSampleSize.
Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.
All I have used to get the image as the background on canvas
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.help_4, 0, 0, null);
And I get a large image. So can anyone pls. provide me a solution to get the Image set according to the Device size. As I'm new to coding a detailed explanation would be appreciated :)
Thank You
You're almost there. You just need to configure the BitmapFactory.Options variable by setting the output width and height (based on the screen size):
BitmapFactory.Options options = new BitmapFactory.Options();
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
options.outHeight = size.x;
options.outWidth = size.y;
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.help_4, options));
Determine the device width and height and scale it:
int deviceWidth = getWindowManager().getDefaultDisplay()
.getWidth();
int deviceHeight = getWindowManager().getDefaultDisplay()
.getHeight();
So in complete you can use the following ready-to-use snippet taken from here:
public Bitmap scaleToActualAspectRatio(Bitmap bitmap) {
if (bitmap != null) {
boolean flag = true;
int deviceWidth = getWindowManager().getDefaultDisplay()
.getWidth();
int deviceHeight = getWindowManager().getDefaultDisplay()
.getHeight();
int bitmapHeight = bitmap.getHeight();
int bitmapWidth = bitmap.getWidth();
if (bitmapWidth > deviceWidth) {
flag = false;
// scale According to WIDTH
int scaledWidth = deviceWidth;
int scaledHeight = (scaledWidth * bitmapHeight) / bitmapWidth;
try {
if (scaledHeight > deviceHeight)
scaledHeight = deviceHeight;
bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth,
scaledHeight, true);
} catch (Exception e) {
e.printStackTrace();
}
}
if (flag) {
if (bitmapHeight > deviceHeight) {
// scale According to HEIGHT
int scaledHeight = deviceHeight;
int scaledWidth = (scaledHeight * bitmapWidth) / bitmapHeight;
try {
if (scaledWidth > deviceWidth)
scaledWidth = deviceWidth;
bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth,
scaledHeight, true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return bitmap;
}
So finaly use:
myCanvas.drawBitmap(scaleToActualAspectRatio(myBitmap), X, Y, null)
The process would look something like this:
Get screen size
Calculate new image size (keeping aspect ratio)
Decode bitmap to new size.
Get screen size
To get the exact screen size of a device, use DisplayMetrics...
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
The metrics variable will now contain the screen dimensions in pixels. You can get the values by using metrics.widthPixels and metrics.heightPixels.
Calculate new image size (keeping aspect ratio)
To calculate new image size, we can use BitmapFactory.Options and tell it to scale the image down by a factor of x. To calculate that factor (also referred as inSampleSize), use the following method...
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Decode Bitmap to new size
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.help_4, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, metrics.widthPixels, metrics.heightPixels);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), R.drawable.help_4, options);
The inJustDecodeBounds option tells the framework to null out the Bitmap to prevent OOM exceptions, since we are only interested in the dimensions, and not the actual image.
This method will ensure that the Bitmap is scaled efficiently. Read more here: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
I have an image view and I want to set an image on it. The size of the image is 7,707,446 bytes. Whenever I try to set the layout, the app crashes and the error is out of memory error.can anyone suggest me how to solve it.The xml is:-
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/image" >
</RelativeLayout>
</ScrollView>
Bitmap picture=BitmapFactory.decodeFile("/sdcard...");
int width = picture.getWidth();
int height = picture.getWidth();
float aspectRatio = (float) width / (float) height;
int newWidth = 70;
int newHeight = (int) (70 / aspectRatio);
picture= Bitmap.createScaledBitmap(picture, newWidth, newHeight, true);
public static Bitmap decodeImage(String arrayList_image) {
URL aURL;
try {
aURL = new URL(arrayList_image);
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
return bm;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
See Loading Large Bitmaps Effectively.
You could create a function to calculate a downscaling factor:
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;
}
And then use this to load a scaled Bitmap:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Thus if you wanted to load a 100x100 pixel thumbnail into your ImageView, you'd just use:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)
);
Hi i suggest u to try this and display your image according the Device.
Bitmap bmp = BitmapFactory.decodeResource(myContext.getResources(),drawableId);
int w = bmp.getWidth();
int h = bmp.getHeight();
System.out.println("OrgWidth : " + w);
System.out.println("orgHeight : " + h);
int targetWidth = (screenWidth * w)/iPadWidth;
int targetHeight = (screenHeight * h)/iPadHeight;
System.out.println("TargetWidth : " + targetWidth);
System.out.println("TargetHeight : " + targetHeight);
int[] arrWidthHeight = {targetWidth,targetHeight};
// This for Slove the out of Memory problem
int newWidth = 110;
int newHeight = 110;
float scaleWidth = ((float) newWidth) / w;
float scaleHeight = ((float) newHeight) / h;
Matrix bMatrix = new Matrix();
bMatrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bmp, 0, 0, w, h , bMatrix, true);
First fetch the Image from resources and find new height and width of image according to device and after scale this image as purpose for solve the out of memory here.
This for find height and width according to device height and width..
WindowManager winManager = (WindowManager) myContext.getSystemService(Context.WINDOW_SERVICE);
screenWidth = winManager.getDefaultDisplay().getWidth();
screenHeight = winManager.getDefaultDisplay().getHeight();
System.out.println("Screen Width : " + screenWidth);
System.out.println("Screen Height : " + screenHeight);
and use bitmap to get image height and width witch like a ipathwidth and ipathHeight ok now
Happy