I have a created bitmaps. Sizes are not specific. Sometimes 120x60 , 129x800 , 851x784. Its not have a specific value... I want to make these bitmaps resizing to 512x512 always but without changing original images aspect ratio. And without cropping. New image must have canvas 512x512 and original image must be center without any cropping.
I was resizing my bitmaps with this function but it makes images really bad because image fitting X and Y . I don't want image to fit x and y on same time fits one of it and keeps its aspect ratio.
public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
}
What I have;
What I want;
Ok, so you're really close. I can't test this right now, but basically what needs to be changed is
1) You need to apply the same scale to both X and Y, so you need to pick the smaller one (try the bigger one if that doesn't work).
matrix.postScale(Math.min(scaleWidth, scaleHeight), Math.min(scaleWidth, scaleHeight));
2) The result will be a bitmap where at least one side is 512px large, the other one will be smaller. So you need to add the padding to fit that side to 512px (equally left and right/top and bottom for centering). In order to do so, you need to create an new bitmap of the desired size:
Bitmap outputimage = Bitmap.createBitmap(512,512, Bitmap.Config.ARGB_8888);
3) and lastly depending on what side of the resizedBitmap is 512px you need to draw resizedBitmap to the correct position in outputImage
Canvas can = new Canvas(outputimage);
can.drawBitmap(resizedBitmap, (512 - resizedBitmap.getWidth()) / 2, (512 - resizedBitmap.getHeight()) / 2, null);
Note here, that 512 - resizedBitmap.getWidth() results in 0 and therefor no padding at the side with correct size.
4) Now return outputImage
Here's a simplification in Kotlin that does both the scale and the translation with the matrix, skipping the intermediate bitmap.
Note that it also sets the background color to white for new pixels, which I needed for my image pipeline. Feel free to remove that if you don't need it.
fun resizedBitmapWithPadding(bitmap: Bitmap, newWidth: Int, newHeight: Int) : Bitmap {
val scale = min(newWidth.toFloat() / bitmap.width, newHeight.toFloat() / bitmap.height)
val scaledWidth = scale * bitmap.width
val scaledHeight = scale * bitmap.height
val matrix = Matrix()
matrix.postScale(scale, scale)
matrix.postTranslate(
(newWidth - scaledWidth) / 2f,
(newHeight - scaledHeight) / 2f
)
val outputBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.config)
outputBitmap.eraseColor(Color.WHITE)
Canvas(outputBitmap).drawBitmap(
bitmap,
matrix,
null
)
return outputBitmap
}
Related
I draw a bitmap on a canvas like this:
float multiplier = (float) canvas.getWidth() / (float) background.getWidth();
Matrix bgMatrix = new Matrix();
bgMatrix.postScale(multiplier, multiplier);
canvas.drawBitmap(background, bgMatrix, paint);
multiplier is used to scale the image to fit the canvas.
Next I need to draw another bitmap on top of it, aligned to the center horizontally and a little bit above the center vertically. I calculate the center point of the second bitmap like this:
int centerX = (int) (canvas.getWidth() - overlay.getWidth() * multiplier) / 2;
int centerY = (int) (canvas.getHeight() - overlay.getHeight() * multiplier) / 2;
where overlay is the overlaying bitmap.
Everything so far is fine. Next part is where it gets tricky. I need to subtract 39 pixels (measured from the original image, "real pixels") from the vertical center point to get the right coordinates. However, as I scale the images and Android handles widths and heights relative to the screens resolution and the screen size, I don't know how I can make the 39 pixels look the same size on all devices.
Currently I draw the overlay image in this way
Matrix matrix = new Matrix();
matrix.setRotate(rotation, overlay.getWidth() / 2, overlay.getHeight() / 2);
matrix.postScale(multiplier, multiplier);
matrix.postTranslate(centerX, centerY);
canvas.drawBitmap(overlay, matrix, paint);
How do I make the 39 pixel reduction look the same on all devices?
Trying to resize a bitmap and set to a specific part of an imageview. The imageview is square and I wish to have the bitmap in the bottom right corner. Width to be 10% of imageview and height to be 30%.
int w = imageview.getWidth();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.vertical_bar_green);
imageview.setImageBitmap(Bitmap.createScaledBitmap(bm, w/10, w*30/100, false));
imageview.setScaleType(ScaleType.FIT_END);
The result is the bitmap is the full height of the imageview and the width is much larger.
How can I set specific points to place the bitmap?
From the documentation for END (the matrix used by FIT_END).
Compute a scale that will maintain the original src aspect ratio, but
will also ensure that src fits entirely inside dst. At least one axis
(X or Y) will fit exactly. END aligns the result to the right and
bottom edges of dst.
You will probable want to use a custom matrix for this, probably built with setRectToRect().
For example:
Matrix matrix = new Matrix();
RectF from = new RectF(0, 0, bm.getIntrinsicWidth(), bm.getIntrinsicHeight());
RectF to = new RectF(view.getWidth() * 0.9, view.getHeight() * 0.7, view.getWidth(), view.getHeight());
matrix.setRectToRect(from, to, Matrix.ScaleToFit.FILL);
view.setScaleType(ScaleType.MATRIX);
view.setImageMatrix(matrix);
(I'm not sure if you wanted to keep the original proportions or not, if you want it then use FIT_END for setRectToRect()).
Well, all this thing tortures me for long weeks, I set an Image 227 pixels high in scales it to 170 pixels even if I want it to be wrap_content whenever I do.
Ok. Here I take My Image which is 1950 pixels long (I put here a part of it so you can understand how it should look like).
First, I want to scale it back to 227 pixels high because that's how it was designed and how it should be
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),R.drawable.ver_bottom_panel_tiled_long);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 200; //this should be parent's whdth later
int newHeight = 227;
// calculate the scale
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
BitmapDrawable dmpDrwbl=new BitmapDrawable(resizedBitmap);
verbottompanelprayer.setBackgroundDrawable(dmpDrwbl);
so... it's not a cropped image at all - no, it's 1950 pixels pressed into 200 pixels.
But I want just cut anything besides this 200 pixels or whatever width I'll set - crop it and not press all this long image into 200 pixels area.
Also, BitmapDrawable(Bitmap bitmap); and imageView.setBackgroundDrawable(drawable); are deprecated - how can I change that?
according to what i see, you create a bitmap of the new size (200x227) , so i'm not sure what you expected. you've even written in the comments that you scale and no word on cropping...
what you can do is :
if the API is at least 10 (gingerbread) , you can use BitmapRegionDecoder , using decodeRegion :
if the API is too old, you need to decode the large bitmap, and then crop it into a new bitmap, using Bitmap.createBitmap
something like this:
final Rect rect =...
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD_MR1)
{
BitmapRegionDecoder decoder=BitmapRegionDecoder.newInstance(imageFilePath, true);
croppedBitmap= decoder.decodeRegion(rect, null);
decoder.recycle();
}
else
{
Bitmap bitmapOriginal=BitmapFactory.decodeFile(imageFilePath, null);
croppedBitmap=Bitmap.createBitmap(bitmapOriginal,rect.left,rect.top,rect.width(),rect.height());
}
I am creating this Android game with Java. However, I load the bitmaps and then resize them to fit screens and such (dpi isn't really exact). BUT my thought is also to load the bitmaps in 16b (mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_4444) for devices with a small amount of ram. But when I resize the bitmaps they seem to go back to 32b (Bitmap.Config.ARGB_8888).
This is how I declare the options:
mBitmapOptions = new BitmapFactory.Options();
mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_4444;
This is how I load the bitmaps:
mBitmaps.add(getResizedBitmap(BitmapFactory.decodeResource(mResources, imagePath, mBitmapOptions)));
And this is the getResizeBitmap method:
public Bitmap getResizedBitmap(Bitmap bm)
{
//Original size
int width = bm.getWidth();
int height = bm.getHeight();
//New size (percent)
float newWidth = 1 * mScaleWidth;
float newHeight = 1 * mScaleHeight;
//Create the matrix
Matrix matrix = new Matrix();
matrix.postScale(newWidth, newHeight);
//Recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
//Recycle the old Bitmap
bm.recycle();
return resizedBitmap;
}
Any ideas why the new Bitmap ignores the options?
Why don't you use the createScaledBitmap method? It should preserve the options. In your case, you are creating a completely new Bitmap and it probably applies a default config.
EDIT: Another option would be to use your code and add a call to the copy method like this:
Bitmap smallerBitmap = resizedBitmap.copy (Bitmap.Config.ARGB_4444, false);
resizedBitmap.recycle ();
However, I don't think this will have a nice performance...
How could I resize an image and still keep it's aspect ratio?
This is the method that I use :
private static BufferedImage resizeImage(BufferedImage originalImage,
int type) {
BufferedImage resizedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT,
type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(originalImage, 0, 0, IMG_WIDTH, IMG_HEIGHT, null);
g.dispose();
return resizedImage;
}
The type variable :
BufferedImage original = ImageIO.read(new File(imagePath));
int type = original.getType() == 0 ? BufferedImage.TYPE_INT_ARGB
: original.getType();
The problem is that some images are correctly resized but others lose their aspect ratio because of the IMG_WIDTH and IMG_HEIGHT.
Is there a way to get the original image dimensions and then apply some kind of proportion resize to maintain the aspect ratio of the resized image?
Why don't you use originalImage.getWidth() and originalImage.getHeight()? Then you can easily calculate aspect ratio. Don't forget that int/int = int, so you need to do
double ratio = 1.0 * originalImage.getWidth() / originalImage.getHeight();
or
double ratio = (double) originalImage.getWidth() / originalImage.getHeight();
Regarding the additional math, you can calculate
int height = (int) IMG_WIDTH/ratio;
int width = (int) IMG_HEIGHT*ratio;
Then see which one fits your needs better and resize to (IMG_WIDTH, height) or (width, IMG_HEIGHT)
To get the image size, see getWidth()/getHeight(). The rest is just some relatively simple math.
Presuming the IMG_WIDTH & IMG_HEIGHT represent the largest size desired:
Find which is going to hit the limit first.
Calculate the ratio between the natural size and that maximum size.
Multiply the other image dimension by the same ratio.