Scale Bitmap maintaining aspect ratio and fitting both width and height

5.7k Views Asked by At

I want to scale a bitmap maintaining the aspect ratio, but fitting the required dimensions. This answer scales the bitmap and maintains the aspect ratio, but leaves some blank space unless the image is a perfect square. I need to fill both width and height, just like the FIT_XY ScaleType property of an ImageView.

3

There are 3 best solutions below

6
On BEST ANSWER

Based on Streets of Boston's answer, I made this method that scales and returns any Bitmap to a desired width and height, fitting both dimensions (no blank space!). It automatically adapts to more horizontal or more vertical images.

public Bitmap resizeBitmapFitXY(int width, int height, Bitmap bitmap){
    Bitmap background = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    float originalWidth = bitmap.getWidth(), originalHeight = bitmap.getHeight();
    Canvas canvas = new Canvas(background);
    float scale, xTranslation = 0.0f, yTranslation = 0.0f;
    if (originalWidth > originalHeight) {
        scale = height/originalHeight;
        xTranslation = (width - originalWidth * scale)/2.0f;
    }
    else {
        scale = width / originalWidth;
        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(bitmap, transformation, paint);
    return background;
}
0
On

This will scale the image to fit into sqaure without any solid color padding in the rest of area and no cropping. If Image is not sqaure then there will be transparent area and aspect ratio of image will as original image. I have tested it with potrait and lanscape images

import android.graphics.*;

 public static Bitmap scalePreserveRatio(Bitmap imageToScale, int destinationWidth,
        int destinationHeight) {
        if (destinationHeight > 0 && destinationWidth > 0 && imageToScale != null) {
            int width = imageToScale.getWidth();
            int height = imageToScale.getHeight();

            //Calculate the max changing amount and decide which dimension to use
            float widthRatio = (float) destinationWidth / (float) width;
            float heightRatio = (float) destinationHeight / (float) height;

            //Use the ratio that will fit the image into the desired sizes
            int finalWidth = (int)Math.floor(width * widthRatio);
            int finalHeight = (int)Math.floor(height * widthRatio);
            if (finalWidth > destinationWidth || finalHeight > destinationHeight) {
                finalWidth = (int)Math.floor(width * heightRatio);
                finalHeight = (int)Math.floor(height * heightRatio);
            }

            //Scale given bitmap to fit into the desired area
            imageToScale = Bitmap.createScaledBitmap(imageToScale, finalWidth, finalHeight, true);

            //Created a bitmap with desired sizes
            Bitmap scaledImage = Bitmap.createBitmap(destinationWidth, destinationHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(scaledImage);

            //Draw background color
            Paint paint = new Paint();
            paint.setColor(Color.TRANSPARENT);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);

            //Calculate the ratios and decide which part will have empty areas (width or height)
            float ratioBitmap = (float)finalWidth / (float)finalHeight;
            float destinationRatio = (float) destinationWidth / (float) destinationHeight;
            float left = ratioBitmap >= destinationRatio ? 0 : (float)(destinationWidth - finalWidth) / 2;
            float top = ratioBitmap < destinationRatio ? 0: (float)(destinationHeight - finalHeight) / 2;
            canvas.drawBitmap(imageToScale, left, top, null);

            return scaledImage;
        } else {
            return imageToScale;
        }
    }

from https://stackoverflow.com/a/32810187/9308731

0
On

Answer is in Kotlin, based on Pablo's answer and fixed the issue of getting cropped off:

        myBitmap?.let {
        val originalWidth = it.width.toFloat()
        val originalHeight = it.height.toFloat()
        var scale = 0.0f
        var xTranslation = 0.0f
        var yTranslation = 0.0f
        val widthRatio = viewWidth / originalWidth
        val heightRatio = viewHeight / originalHeight

        if (widthRatio > heightRatio) {
            scale = viewHeight / originalHeight
            xTranslation = (viewWidth - originalWidth * scale) / 2.0f
        } else {
            scale = viewWidth / originalWidth
            yTranslation = (viewHeight - originalHeight * scale) / 2.0f
        }

        val matrix = Matrix()
        matrix.postTranslate(xTranslation, yTranslation)
        matrix.preScale(scale, scale)
        canvas.drawBitmap(it, matrix, myBitmapPainter)
    }