I've got a bitmap... and if the bitmap's height is greater than maxHeight, or the width is greater than maxWidth, I'd like to proportionally resize the image so that it fits in maxWidth X maxHeight. Here's what I'm trying:
BitmapDrawable bmp = new BitmapDrawable(getResources(), PHOTO_PATH);
int width = bmp.getIntrinsicWidth();
int height = bmp.getIntrinsicHeight();
float ratio = (float)width/(float)height;
float scaleWidth = width;
float scaleHeight = height;
if((float)mMaxWidth/(float)mMaxHeight > ratio) {
scaleWidth = (float)mMaxHeight * ratio;
}
else {
scaleHeight = (float)mMaxWidth / ratio;
}
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap out = Bitmap.createBitmap(bmp.getBitmap(),
0, 0, width, height, matrix, true);
try {
out.compress(Bitmap.CompressFormat.JPEG, 100,
new FileOutputStream(PHOTO_PATH));
}
catch(FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
I get the following exception:
java.lang.IllegalArgumentException: bitmap size exceeds 32bits
What am I doing wrong here?
Your scaleWidth and scaleHeight should be scale factors (so not very big numbers) but your code seems to pass in the actual width and height you're looking for. So you're ending up massively increasing the size of your bitmap.
I think there are other problems with the code to derive scaleWidth and scaleHeight as well though. For one thing your code always has either scaleWidth = width or scaleHeight = height, and changes only one of them, so you're going to be distorting the aspect ratio of your image as well. If you just want to resize the image then you should just have a single scaleFactor.
Also, why does your if statement check for, effectively, maxRatio > ratio ? Shouldn't you be checking for width > maxWidth or height > maxHeight?
This is because the value of scaleWidth or scaleHeight is too large,scaleWidth or scaleHeight is mean enlarge or shrink 's rate,but not width or height,too large rate lead to bitmap size exceeds 32 bits
matrix.postScale(scaleWidth, scaleHeight);
this is how i did it:
public Bitmap decodeAbtoBm(byte[] b){
Bitmap bm; // prepare object to return
// clear system and runtime of rubbish
System.gc();
Runtime.getRuntime().gc();
//Decode image size only
BitmapFactory.Options oo = new BitmapFactory.Options();
// only decodes size, not the whole image
// See Android documentation for more info.
oo.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(b, 0, b.length ,oo);
//The new size we want to scale to
final int REQUIRED_SIZE=200;
// Important function to resize proportionally.
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(oo.outWidth/scale/2>=REQUIRED_SIZE
&& oo.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2; // Actual scaler
//Decode Options: byte array image with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale; // set scaler
o2.inPurgeable = true; // for effeciency
o2.inInputShareable = true;
// Do actual decoding, this takes up resources and could crash
// your app if you do not do it properly
bm = BitmapFactory.decodeByteArray(b, 0, b.length,o2);
// Just to be safe, clear system and runtime of rubbish again!
System.gc();
Runtime.getRuntime().gc();
return bm; // return Bitmap to the method that called it
}
Related
I want to resize an image to a smaller image like thumbnail. I have used following options
Bitmap imageBitmap = Bitmap.createScaledBitmap(mBitmap, 200, 700, false);
and
ThumbnailUtils.extractThumbnail()
Both of these options cut parts of images. So they don't look good. I just want to reduce image dimensions and don't want to loose image content.
you have to try like this
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth)
{
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(filepath,bmOptions);
//Part 1 :withcompression Sacle image 200 pixels x 700 pixels this size you can customize as per your requirement
Bitmap withCompressed = Bitmap.createScaledBitmap(bitmap,200,700,true);
If you want to resize an image without losing quality and images are from drawable/mipmap then you should try pngquant.
https://pngquant.org/
Is there any method that gets the phones resolution (or dp) and scales bitmaps accordingly? I have all my images in xhdpi folder and at the moment they do not scale the way they should.
I want an efficiant and memory-friendly method that can do the scaling automatically. If not, what is the next best thing? completely new area for me. So any tutorial-link is also appriciated.
this is what I use to load bitmaps atm:
public Bitmap loadBitmap(int resourceID) {
Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap tempBmp = null;
try {
tempBmp = BitmapFactory.decodeResource(getResources(), resourceID,
options);
} catch (OutOfMemoryError e) {
} catch (Error e) {
}
return tempBmp;
}
If you wanna scale bitmap for each phone resolution, you should know phone screen size, scale ratio.
This code will return width (w) & height (h) of screen.
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
float density = dMetrics.density;
int w = Math.round(dMetrics.widthPixels / density);
int h = Math.round(dMetrics.heightPixels / density);
activity is instance of Activity which would you like to get screen size.
You have to remember that: When your device is in landscape orientation, w > h. When it in portrait orientation w < h.
So from width & height you can detect your device is in what orientation.
Example:
From w & h of device and ratio (which you want to scale) you can calculate new bitmap size to scale it.
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
I use the following method to resize images on Android.
public Bitmap resize(Bitmap img, int Width, int Height) {
int width = img.getWidth();
int height = img.getHeight();
int newWidth = (int) Width;
int newHeight = (int) Height;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
//matrix.postRotate(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
return resizedBitmap;
}
It works fine on most Android devices. But on some devices the resized image is not displaying. How might I resolve this?
You may create a scaled bitmap with
Bitmap bitmap = Bitmap.createScaledBitmap(b, width, height, true);
Here, width and height you can provide according to the device's screen size for different devices, by reading screen sizes programmatically. Here is how you can read the screen size of a device programmatically.
Most likely, on "some devices" and depending on the requested width and height, there's not enough memory to create the resized bitmap. Check (in debugger) if the resizedBitmap is null when the image "is not displaying".
I have a Bitmap with a size of 1024x1024.png and I need to stretch it on different device screens, I tried using this:
// given a resource, return a bitmap with a specified maximum height
public static Bitmap maxHeightResourceToBitmap(Context c, int res,
int maxHeight) {
Bitmap bmp = imageResourceToBitmap(c, res, maxHeight);
int width = bmp.getWidth();
int height = bmp.getHeight();
int newHeight = maxHeight;
int newWidth = maxHeight / 2;
// calculate the scale - in this case = 0.4f
float scaleHeight = ((float) newHeight) / height;
float scaleWidth = ((float) newWidth) / width;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap and return it
return Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
}
// given a resource, return a bitmap with a specified maximum height
public static Bitmap scaleWithRatio(Context c, int res,
int max) {
Bitmap bmp = imageResourceToBitmap(c, res, max);
int width = bmp.getWidth();
int height = bmp.getHeight();
// calculate the scale - in this case = 0.4f
float scaleHeight = ((float) max) / height;
float scaleWidth = ((float) max) / width;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap and return it
return Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
In order to stretch a bitmap on screen I would recommend to keep the bitmap as the original in memory (don't make the bitmap itself bigger in any case).
Then, when you show it on screen, usually with an ImageView, you can set the image view ScaleTypeto FIT_XY (see docs for more info). This will stretch the image on screen when drawing it to fill the entire ImageView. Also make sure your ImageView fills the entire screen by setting its LayoutParameters accordingly (fill parent for example).
The only real reason to resize bitmaps in memory is to make them smaller to save memory. This is important because Android devices have a limited heap and if you're bitmaps are too big in memory they will fill up your entire heap and you'll run into OutOfMemory errors. See this tutorial if you're running into memory problems.
I have image 640*480 in sd card and i want to set it as home screen wallpaper. How Can I do this with small quality loss? With my code Wallpaper has a not good clarity. That's my code:
private void setWallpaper(String filePath) throws IOException{
if(filePath!=null){
// set options for decoding
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
options.inJustDecodeBounds = true;
//get bitmap from filepath
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
//get sizes of bitmap
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//get sizes of screen and scale level
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int newWidth = display.getWidth()*2;
int newHeight = display.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
//set matrix of the scaling
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//get new bitmap for wallpaper
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
//set bitmap as wallpaper
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
wallpaperManager.clear();
wallpaperManager.setBitmap(resizedBitmap);
}
}
Any time you scale upwards you're going to suffer quality loss. What size screen are you trying to display this image one? Keep in mind the Android wallpaper dimensions are the height of the screen by 2 times the width of the screen (e.g. 800x480 screen needs a 800x960 image).
My recommendation would be to have a larger image and scale downwards. The quality loss from downsampling is far less noticeable than from upscaling. You could, if space is not an issue, include multiple sizes of the image for differing screen resolutions.