Масштабированный битмап, поддерживающий соотношение сторон
Я хотел бы масштабировать a Bitmap
до зависящей от времени ширины и высоты, где поддерживается соотношение сторон, и Bitmap
заполняет всю ширину и центрирует изображение по вертикали, либо обрезая избыток, либо заполняя пробел с 0 альфа-пикселями.
В настоящее время я перерисовываю растровое изображение, создавая Bitmap
всех 0 альфа-пикселей и рисуя изображение 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
Gravity.CENTER_CROP
, однако также использует целочисленное масштабирование, поэтому он не разрешает ширину изображения, если это не так.
Ответы
Ответ 1
Как насчет этого:
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;
}
}
Ответ 3
Здесь у меня есть проверенное решение, где я создаю масштабированный 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".
Это просто дополнение к ответу Коэна, но проблема в его коде - это линия, где он вычисляет отношение. Разделение двух целых чисел дает целое число, и если результат равен < 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;
}
Ответ 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);
}
Ответ 7
Ни один из вышеперечисленных ответов не был обработан для меня, и я только что создал метод, который устанавливает все измерения в нужные, окрашивая пустую область в черный. Вот мой метод:
/**
* 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.
Ответ 8
Добавлено RESIZE_CROP в ответ 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;
}
Ответ 9
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;
}
Ответ 10
Это потрясающая библиотека от 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;
}
Ответ 11
Мое решение было таким, которое поддерживает соотношение сторон и требует только одного размера, например, если у вас есть 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;
}
}
Ответ 12
Для изменения масштаба изображения используется простая математика, рассмотрите следующий фрагмент и выполните следующие действия:
1. Предположим, у вас есть изображение Imaan с разрешением 720x1280, и вы хотите, чтобы его поместили по ширине 420, получите процент сокращения, требуемый для данной математики,
originalWidth = 720;
wP = 720/100;
/* wP = 7.20 is a percentage value */
- Теперь вычтите необходимую ширину из первоначальной ширины и затем умножьте результат на
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;
}
здесь вы просто масштабируете изображение со ссылкой на параметр высоты или ширины, чтобы соответствовать требуемым критериям.