首页 文章

缩放位图保持宽高比

提问于
浏览
41

我想将 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%填充 .

另外,我知道 ImageViewGravity.CENTER_CROP 功能,它也使用整数缩放,所以当它不应该时,它会切断图像的宽度 .

11 回答

  • 22

    那这个呢:

    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

    这将尊重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;
        }
    }
    
  • 2

    在这里,我有一个经过测试的解决方案,我在一个位图文件中创建一个缩放的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像素,但在调整大小时需要保持纵横比 . 如果您需要宽度或高度的其他值,只需替换值“4096” .

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

  • 4

    这是我的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;
        }
    
  • 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;
    }
    
  • 13

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

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

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

    /**
     * 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 .

    我认为这是必须的,卢比 .

  • 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;
    }
    
  • 1
    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;
    }
    
  • 77

    This is an awesome library from ArthurHub如果您不想重新发明轮子,则以编程方式和交互方式处理图像裁剪 .

    但是如果你喜欢像我这样的非膨胀版本..,这里显示的内部函数是一个相当复杂的执行Image Scaling与几个标准选项

    /**
     * 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;
    }
    
  • 5

    我的解决方案是这样,它保持纵横比,并且只需要一个尺寸,例如,如果你有一个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;
            }
        }
    

相关问题