I would like to scale a Bitmap
to a runtime dependant width and height, where the aspect ratio is maintained and the Bitmap
fills the entire width and centers the image vertically, either cropping the excess or filling in the gap with 0 alpha pixels.
I'm currently redrawing the bitmap myself by creating a Bitmap
of all 0 alpha pixels and drawing the image Bitmap
on top of it, scaling to the exact specified width and maintaining the aspect ratio, however, it ends up losing/screwing up the pixel data.
Here is how I'm doing it:
Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
float originalWidth = originalImage.getWidth(), originalHeight = originalImage.getHeight();
Canvas canvas = new Canvas(background);
float scale = width/originalWidth;
float xTranslation = 0.0f, yTranslation = (height - originalHeight * scale)/2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
canvas.drawBitmap(originalImage, transformation, null);
return background;
Is there a library out there or some better code that can do this better? I would like the image to look as crisp as possible, but I knew that my function wouldn't provide a great result.
I know I could have the image stay fine by using integer scaling, instead of float scaling, but I need the width to be 100% filled.
Also, I know about an ImageView
's Gravity.CENTER_CROP
capability, however, that also uses integer scaling, so it cuts off the width of the image when it shouldn't.
However, make sure you're setting the image to the ImageView using android:src="..." rather than android:background="..." . src= makes it scale the image maintaining aspect ratio, but background= makes it scale and distort the image to make it fit exactly to the size of the ImageView.
The larger dimension of the target bitmap size is going to match either maxWidth or maxHeight , the second dimension will be scaled proportionally. A new bitmap is created using createScaledBitmap with the correct targetWidth and targetHeight .
The aspect ratio is the proportional relationship between an image's width and height. Different cameras shoot in different aspect ratios, so whether you're using a digital camera, a 35mm film camera, or an iPhone, they can all vary.
This will respect maxWidth and maxHeight, which means the resulting bitmap will never have dimensions larger then those:
private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
if (maxHeight > 0 && maxWidth > 0) {
int width = image.getWidth();
int height = image.getHeight();
float ratioBitmap = (float) width / (float) height;
float ratioMax = (float) maxWidth / (float) maxHeight;
int finalWidth = maxWidth;
int finalHeight = maxHeight;
if (ratioMax > ratioBitmap) {
finalWidth = (int) ((float)maxHeight * ratioBitmap);
} else {
finalHeight = (int) ((float)maxWidth / ratioBitmap);
}
image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
return image;
} else {
return image;
}
}
What about this:
Bitmap background = Bitmap.createBitmap((int)width, (int)height, Config.ARGB_8888);
float originalWidth = originalImage.getWidth();
float originalHeight = originalImage.getHeight();
Canvas canvas = new Canvas(background);
float scale = width / originalWidth;
float xTranslation = 0.0f;
float yTranslation = (height - originalHeight * scale) / 2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(originalImage, transformation, paint);
return background;
I added a paint
to filter the scaled bitmap.
here is a method from my Utils class, that does the job:
public static Bitmap scaleBitmapAndKeepRation(Bitmap targetBmp,int reqHeightInPixels,int reqWidthInPixels)
{
Matrix matrix = new Matrix();
matrix .setRectToRect(new RectF(0, 0, targetBmp.getWidth(), targetBmp.getHeight()), new RectF(0, 0, reqWidthInPixels, reqHeightInPixels), Matrix.ScaleToFit.CENTER);
Bitmap scaledBitmap = Bitmap.createBitmap(targetBmp, 0, 0, targetBmp.getWidth(), targetBmp.getHeight(), matrix, true);
return scaledBitmap;
}
Here I have a tested solution where I create a scaled Bitmap out of a bitmap file:
int scaleSize =1024;
public Bitmap resizeImageForImageView(Bitmap bitmap) {
Bitmap resizedBitmap = null;
int originalWidth = bitmap.getWidth();
int originalHeight = bitmap.getHeight();
int newWidth = -1;
int newHeight = -1;
float multFactor = -1.0F;
if(originalHeight > originalWidth) {
newHeight = scaleSize ;
multFactor = (float) originalWidth/(float) originalHeight;
newWidth = (int) (newHeight*multFactor);
} else if(originalWidth > originalHeight) {
newWidth = scaleSize ;
multFactor = (float) originalHeight/ (float)originalWidth;
newHeight = (int) (newWidth*multFactor);
} else if(originalHeight == originalWidth) {
newHeight = scaleSize ;
newWidth = scaleSize ;
}
resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
return resizedBitmap;
}
Notice that I need scaled Bitmaps which have a maximum size of 4096x4096 Pixels but the aspect ratio needs to be kept while resizing. If you need other values for width or height just replace the values "4096".
This is just an addition to the answer of Coen but the problem in his code is the line where he calculates the ratio. Dividing two Integers gives an Integer and if the result is < 1 it will be rounded to 0. So this throws the "divide by zero" exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With