缩放位图保持宽高比

时间:2013-03-15 19:29:54

标签: android bitmap scaling

我想将Bitmap缩放为与运行时相关的宽度和高度,其中保持纵横比,Bitmap填充整个宽度并垂直居中图像,裁剪多余的或用0像素填充间隙。

我目前正在通过创建所有0个像素的Bitmap并在其上方绘制图像Bitmap,缩放到指定的指定宽度并保持纵横比来自行重绘位图,然而,它最终会丢失/搞砸像素数据。

以下是我的表现:

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;

是否有一个库或一些更好的代码可以做得更好?我希望图像看起来尽可能清晰,但我知道我的功能不会提供很好的结果。

我知道通过使用整数缩放而不是浮点缩放可以使图像保持正常,但我需要宽度为100%填充。

另外,我知道ImageView的{​​{1}}功能,它也使用整数缩放,所以当它不应该时,它会切断图像的宽度。

13 个答案:

答案 0 :(得分:80)

这将尊重maxWidth和maxHeight,这意味着生成的位图的尺寸永远不会大于:

 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;
    }
}

答案 1 :(得分:64)

这个怎么样:

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;

我添加了paint来过滤缩放后的位图。

答案 2 :(得分:22)

这里我有一个经过测试的解决方案,我用位图文件创建了一个缩放的Bitmap:

    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;
    }

请注意,我需要缩放的位图,其最大尺寸为4096x4096像素,但在调整大小时需要保持纵横比。如果您需要宽度或高度的其他值,只需替换值#34; 4096"。

这只是Coen答案的补充,但他的代码中的问题是他计算比率的线。除以两个整数得到一个整数,如果结果是< 1它将四舍五入为0.所以这会将"除以零"异常。

答案 3 :(得分:17)

这是我的Utils类的一个方法,它完成了这项工作:

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;
    }

答案 4 :(得分:6)

更简单的解决方案:请注意我们将宽度设置为500像素

 public void scaleImageKeepAspectRatio()
    {
        int imageWidth = scaledGalleryBitmap.getWidth();
        int imageHeight = scaledGalleryBitmap.getHeight();
        int newHeight = (imageHeight * 500)/imageWidth;
        scaledGalleryBitmap = Bitmap.createScaledBitmap(scaledGalleryBitmap, 500, newHeight, false);

    }

答案 5 :(得分:5)

也可以通过自己计算比率来完成,就像这样。

private Bitmap scaleBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    Log.v("Pictures", "Width and height are " + width + "--" + height);

    if (width > height) {
        // landscape
        int ratio = width / maxWidth;
        width = maxWidth;
        height = height / ratio;
    } else if (height > width) {
        // portrait
        int ratio = height / maxHeight;
        height = maxHeight;
        width = width / ratio;
    } else {
        // square
        height = maxHeight;
        width = maxWidth;
    }

    Log.v("Pictures", "after scaling Width and height are " + width + "--" + height);

    bm = Bitmap.createScaledBitmap(bm, width, height, true);
    return bm;
}

答案 6 :(得分:4)

上述答案都没有为我工作,我只是创建了一个方法,将所有尺寸设置为所需尺寸,并将空白区域绘制为黑色。这是我的方法:

/**
 * Scale the image preserving the ratio
 * @param imageToScale Image to be scaled
 * @param destinationWidth Destination width after scaling
 * @param destinationHeight Destination height after scaling
 * @return New scaled bitmap preserving the ratio
 */
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.BLACK);
        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;
    }
}

例如;

假设您的图像为100 x 100但所需尺寸为300x50,则此方法会将图像转换为50 x 50并将其绘制为尺寸为300 x 50的新图像(并且空文件将为是黑色的。

另一个例子:假设您的图像为600 x 1000,所需的尺寸再次为300 x 50,那么您的图像将被转换为30 x 50并绘制成新创建的图像,尺寸为300 x 50

我认为这是必须的,Rs。

答案 7 :(得分:3)

在Gowrav的回答中添加了RESIZE_CROP。

   enum RequestSizeOptions {
    RESIZE_FIT,
    RESIZE_INSIDE,
    RESIZE_EXACT,
    RESIZE_CENTRE_CROP
}
static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
    try {
        if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                options == RequestSizeOptions.RESIZE_INSIDE ||
                options == RequestSizeOptions.RESIZE_EXACT || options == RequestSizeOptions.RESIZE_CENTRE_CROP)) {

            Bitmap resized = null;
            if (options == RequestSizeOptions.RESIZE_EXACT) {
                resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
            } else {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                    resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                }
                if (scale > 1 || options == RequestSizeOptions.RESIZE_CENTRE_CROP) {
                    int smaller_side = (height-width)>0?width:height;
                    int half_smaller_side = smaller_side/2;
                    Rect initialRect = new Rect(0,0,width,height);
                    Rect finalRect = new Rect(initialRect.centerX()-half_smaller_side,initialRect.centerY()-half_smaller_side,
                            initialRect.centerX()+half_smaller_side,initialRect.centerY()+half_smaller_side);
                    bitmap = Bitmap.createBitmap(bitmap,  finalRect.left, finalRect.top, finalRect.width(), finalRect.height(), null, true);
                    //keep in mind we have square as request for cropping, otherwise - it is useless
                    resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
                }

            }
            if (resized != null) {
                if (resized != bitmap) {
                    bitmap.recycle();
                }
                return resized;
            }
        }
    } catch (Exception e) {
        Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
    }
    return bitmap;
}

答案 8 :(得分:2)

public static Bitmap scaleBitmap(Bitmap bitmap, int wantedWidth, int wantedHeight) {
    float originalWidth = bitmap.getWidth();
    float originalHeight = bitmap.getHeight();
    Bitmap output = Bitmap.createBitmap(wantedWidth, wantedHeight, Config.ARGB_8888);
    Canvas canvas = new Canvas(output);
    Matrix m = new Matrix();

    float scalex = wantedWidth/originalWidth;
    float scaley = wantedHeight/originalHeight;
    float xTranslation = 0.0f, yTranslation = (wantedHeight - originalHeight * scaley)/2.0f;

    m.postTranslate(xTranslation, yTranslation);
    m.preScale(scalex, scaley);
    // m.setScale((float) wantedWidth / bitmap.getWidth(), (float) wantedHeight / bitmap.getHeight());
    Paint paint = new Paint();
    paint.setFilterBitmap(true);
    canvas.drawBitmap(bitmap, m, paint);

    return output;
}

答案 9 :(得分:2)

如果您不想重新发明轮子,

This is an awesome library from ArthurHub可以通过编程和交互方式处理图像裁剪。

但如果您更喜欢像我这样的非膨胀版本..,此处显示的内部功能相当复杂,可以使用少量标准选项执行图像缩放

/**
 * Resize the given bitmap to the given width/height by the given option.<br>
 */

enum RequestSizeOptions {
    RESIZE_FIT,
    RESIZE_INSIDE,
    RESIZE_EXACT
}

static Bitmap resizeBitmap(Bitmap bitmap, int reqWidth, int reqHeight, RequestSizeOptions options) {
    try {
        if (reqWidth > 0 && reqHeight > 0 && (options == RequestSizeOptions.RESIZE_FIT ||
                options == RequestSizeOptions.RESIZE_INSIDE ||
                options == RequestSizeOptions.RESIZE_EXACT)) {

            Bitmap resized = null;
            if (options == RequestSizeOptions.RESIZE_EXACT) {
                resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
            } else {
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
                if (scale > 1 || options == RequestSizeOptions.RESIZE_FIT) {
                    resized = Bitmap.createScaledBitmap(bitmap, (int) (width / scale), (int) (height / scale), false);
                }
            }
            if (resized != null) {
                if (resized != bitmap) {
                    bitmap.recycle();
                }
                return resized;
            }
        }
    } catch (Exception e) {
        Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
    }
    return bitmap;
}

答案 10 :(得分:1)

我的解决方案是这样,它保持纵横比,并且只需要一个尺寸,例如,如果你有一个1920 * 1080和一个1080 * 1920的图像,你想要将它调整为1280,第一个将是1280 * 720第二个是720 * 1280

public static Bitmap resizeBitmap(final Bitmap temp, final int size) {
        if (size > 0) {
            int width = temp.getWidth();
            int height = temp.getHeight();
            float ratioBitmap = (float) width / (float) height;
            int finalWidth = size;
            int finalHeight = size;
            if (ratioBitmap < 1) {
                finalWidth = (int) ((float) size * ratioBitmap);
            } else {
                finalHeight = (int) ((float) size / ratioBitmap);
            }
            return Bitmap.createScaledBitmap(temp, finalWidth, finalHeight, true);
        } else {
            return temp;
        }
    }

答案 11 :(得分:0)

重新缩放图像涉及简单的数学运算,请考虑以下代码片段,然后继续进行操作: 1.假设您有720x1280的Imaan图像,并且要适合420宽度,请获取给定数学所需的缩小百分比,

originalWidth = 720;
wP = 720/100;
/*  wP = 7.20 is a percentage value */
  1. 现在从原始宽度中减去所需的宽度,然后将结果乘以wP。您将获得宽度减小的百分比。

difference = originalWidth - 420; dP = difference/wP;

这里dP将为41.66,这意味着您将减小大小41.66%。因此,您必须将高度降低41.66(dP)才能保持该图像的比例或比例。 计算以下高度,

hP = originalHeight / 100;
//here height percentage will be 1280/100 = 12.80
height = originalHeight - ( hp * dP);
// here 1280 - (12.80 * 41.66) = 746.75

这是您的合适比例,您可以在420x747中调整图像/位图的大小。它将返回经过调整大小的图像,而不会丢失比例/比例。

示例

public static Bitmap scaleToFit(Bitmap image, int width, int height, bool isWidthReference) {
    if (isWidthReference) {
        int originalWidth = image.getWidth();
        float wP = width / 100;
        float dP = ( originalWidth - width) / wP;
        int originalHeight = image.getHeight();
        float hP = originalHeight / 100;
        int height = originalHeight - (hP * dP);
        image = Bitmap.createScaledBitmap(image, width, height, true);
    } else {
        int originalHeight = image.getHeight();
        float hP = height / 100;
        float dP = ( originalHeight - height) / hP;
        int originalWidth = image.getWidth();
        float wP = originalWidth / 100;
        int width = originalWidth - (wP * dP);
        image = Bitmap.createScaledBitmap(image, width, height, true);
    }
    return image;
}

这里您只是参考height或width参数缩放图像以适合所需的条件。

答案 12 :(得分:0)

基于joaomgcd回答的Kotlin扩展功能版本

private fun Bitmap.resize(maxWidth: Int, maxHeight: Int): Bitmap {
    return if (maxHeight > 0 && maxWidth > 0) {
        val width = this.width
        val height = this.height
        val ratioBitmap = width.toFloat() / height.toFloat()
        val ratioMax = maxWidth.toFloat() / maxHeight.toFloat()
        var finalWidth = maxWidth
        var finalHeight = maxHeight
        if (ratioMax > ratioBitmap) {
            finalWidth = (maxHeight.toFloat() * ratioBitmap).toInt()
        } else {
            finalHeight = (maxWidth.toFloat() / ratioBitmap).toInt()
        }
        Bitmap.createScaledBitmap(this, finalWidth, finalHeight, true)
    } else this
}