Mapa de bits escalado manteniendo la relación de aspecto

Me gustaría escalar un Bitmap de Bitmap a un ancho y alto dependientes del tiempo de ejecución, donde se mantiene la relación de aspecto y el Bitmap llena todo el ancho y centra la imagen verticalmente, recortando el exceso o completando el espacio con 0 píxeles alfabéticos.

Actualmente, estoy rediseñando el bitmap creando un Bitmap de Bitmap de todos los 0 píxeles alfa y dibujando la imagen Bitmap encima, escalando al ancho especificado y manteniendo la relación de aspecto, sin embargo, termina perdiendo / atornillando el píxel. datos.

Así es como lo estoy haciendo:

 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; 

¿Hay una biblioteca o algún código mejor que pueda hacer esto mejor? Me gustaría que la imagen se vea lo más nítida posible, pero sabía que mi función no proporcionaría un gran resultado.

Sé que podría hacer que la imagen se mantuviera bien utilizando escalas enteras, en lugar de escala flotante, pero necesito que el ancho se llene al 100%.

Además, conozco la capacidad Gravity.CENTER_CROP ImageView , que también usa la escala entera, por lo que corta el ancho de la imagen cuando no debería.

¿Qué tal esto?

 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; 

Agregué una paint para filtrar el bitmap escalado.

Esto respetará maxWidth y maxHeight, lo que significa que el bitmap resultante nunca tendrá dimensiones más grandes que las siguientes:

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

Aquí tengo una solución probada donde creo un bitmap escalado de un archivo de 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; } 

Tenga en cuenta que necesito mapas de bits escalados que tengan un tamaño máximo de 4096×4096 píxeles, pero la relación de aspecto debe mantenerse al cambiar el tamaño. Si necesita otros valores para ancho o alto, simplemente reemplace los valores “4096”.

Esto es solo una adición a la respuesta de Coen, pero el problema en su código es la línea donde calcula la relación. Al dividir dos enteros se obtiene un entero y si el resultado es <1 se redondeará a 0. Por lo tanto, arroja la excepción "dividir por cero".

aquí hay un método de mi clase de Utils, que hace el trabajo:

 public static Bitmap scaleBitmapAndKeepRation(Bitmap TargetBmp,int reqHeightInPixels,int reqWidthInPixels) { Matrix m = new Matrix(); m.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(), m, true); return scaledBitmap; } 

solución más simple: tenga en cuenta que establecemos el ancho en 500 píxeles

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

También se puede hacer calculando la proporción usted mismo, así.

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

Ninguna de las respuestas anteriores se trabajó para mí y acabo de crear un método que establece todas las dimensiones en las deseadas pintando el área vacía en negro. Aquí está mi método:

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

Por ejemplo;

Digamos que tienes una imagen de 100 x 100 pero el tamaño deseado es 300x50, luego este método convertirá tu imagen a 50 x 50 y la pintará en una nueva imagen que tiene unas dimensiones de 300 x 50 (y los archivos vacíos serán negros) .

Otro ejemplo: digamos que tiene una imagen como 600 x 1000 y los tamaños deseados son 300 x 50 nuevamente, luego su imagen se convertirá en 30 x 50 y se pintará en una imagen recién creada que tiene tamaños como 300 x 50.

Creo que esto es lo que debe ser, Rs.

Se agregó RESIZE_CROP a la respuesta de Gowrav.

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

Mi solución fue esta, que mantiene la relación de aspecto, y requiere solo un tamaño, por ejemplo, si tiene una imagen de 1920 * 1080 y una de 1080 * 1920 y desea cambiar el tamaño a 1280, la primera será 1280 * 720 y la segunda será 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; } } 

Esta es una impresionante biblioteca de ArthurHub para manejar los cultivos de imágenes de forma programática e interactiva si no desea reinventar la rueda.

Pero si prefiere una versión no hinchada como yo …, la función interna que se muestra aquí es bastante sofisticada para realizar la Escala de imagen con pocas opciones estándar.

 /** * Resize the given bitmap to the given width/height by the given option.
*/ 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; }