Scale bitmap around center pivot - android

I need to make an icon on Home screen from image. I use the standart code for this:
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
final int scaleFactor = (int) Math.max(photoW/app_icon_size, photoH/app_icon_size);
// Decode the image file into a Bitmap sized to fill needed sizes
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
return bitmap;
But because of using integer rather than float for inSampleSize i have to downscale bitmap a little more to fit it exactly to sizes i need:
final float aspectRatio = (float) bitmap.getWidth()/bitmap.getHeight();
final int finalW = (aspectRatio < 1 ? (int) (app_icon_size*aspectRatio) : app_icon_size);
final int finalH = (aspectRatio > 1 ? (int) (app_icon_size/aspectRatio) : app_icon_size);
bitmap = Bitmap.createScaledBitmap(bitmap, finalW, finalH, false);
Everything is nice exept it is scaled from its 0,0 point. So i tried to use createBitmap with Matrix instead of createScaledBitmap:
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale((float) finalW/bitmap.getWidth(), (float) finalH/bitmap.getHeight(), (float) bitmap.getWidth()/2, (float) bitmap.getHeight()/2);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), scaleMatrix, false);
But nothing changed, it still scales from 0,0. This is banal question i guess, but most solutions on Stack and other resources concern Canvas and View but i need to scale Bitmap itslef.

Related

How to resize image without loosing image content

I want to resize an image to a smaller image like thumbnail. I have used following options
Bitmap imageBitmap = Bitmap.createScaledBitmap(mBitmap, 200, 700, false);
and
ThumbnailUtils.extractThumbnail()
Both of these options cut parts of images. So they don't look good. I just want to reduce image dimensions and don't want to loose image content.
you have to try like this
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth)
{
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);
return resizedBitmap;
}
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(filepath,bmOptions);
//Part 1 :withcompression Sacle image 200 pixels x 700 pixels this size you can customize as per your requirement
Bitmap withCompressed = Bitmap.createScaledBitmap(bitmap,200,700,true);
If you want to resize an image without losing quality and images are from drawable/mipmap then you should try pngquant.
https://pngquant.org/

How to set width and height auto when rotated image?

When i capture photo in portrait mode, it is landscaped and I use this function to rotate image
private Bitmap rotateImage(String mCurrentPhotoPath, String rotationAngle){
//Set hardCode size imageView 300dp
int targetW = CommonUtils.ConvertDpTPpx(mContext, 300);
int targetH = CommonUtils.ConvertDpTPpx(mContext, 300);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
Matrix matrix = new Matrix();
matrix.setRotate(Float.parseFloat(rotationAngle), (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
return Bitmap.createBitmap(bitmap, 0, 0, photoW/2, photoH/2, matrix, true);
}
When I build on device on S4, it runs right, but when it run on Sony Docomo (SO-04E) and LG LTE2, after capture photo and app crash. A bug is x + width (image) < bitmap.width. And then i edit photoW/4 and photoH/4 so Sony and LG run right but S4 image is cropped. So, at the return line, how i should fix the width and height to run right on all device.

createBitmap() returns a bitmap without drawn

I want to scale an existing bitmap. So I use Bitmap.createBitmap(Bitmap b, int x, int y, int width, int height, Matrix m, boolean filter) to create a scaled bitmap. If the scale ratio of the Matrix object is less than 1, this method works well. However, if the ratio is equal to or greater than 1, the method returns a bitmap with the width and height i desire but without an image (is transparent). I wonder why and how to solve this.
Bitmap imageBitmap;
imageBitmap = weiboView.getDrawingCache();
Log.d("cosmo", "bitmap generated: "+imageBitmap.getWidth()+" * "+imageBitmap.getHeight());
//Init and configure and load the image view
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
imageView.setScaleType(ScaleType.FIT_CENTER);
containerLayout.addView(imageView);
//create a scaled bitmap to assign to the image view
Bitmap scaledImageBitmap = MyBitmapUtils.getBitmapScaledToFitWidth(getWindowManager().getDefaultDisplay().getWidth(), imageBitmap);
Log.d("cosmo", "scaled: "+scaledImageBitmap.getWidth()+" * "+scaledImageBitmap.getHeight());
//Here if I set imageBitmap as the image of imageView it works well
imageView.setImageBitmap(scaledImageBitmap);
Here is MyBitmapUtils.getBitmapScaledToFitWidth:
public static Bitmap getBitmapScaledToFitWidth(int targetWidth, Bitmap bitmap) {
float ratio = (float)targetWidth/(float)bitmap.getWidth();
Log.d("cosmo", "ratio is "+ratio);
//ratio = 0.5f;
Matrix matrix = new Matrix();
matrix.postScale(ratio, ratio);
Bitmap target = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,false);
return target;
}
I have known how did this happen. It's because that the height of the Image is greater than 2048, and in android's system creating a bitmap bigger than 2048*2048 will cause OOM (My logcat didn't report this error, oddly)
Why don't you just use createScaledBitmap?
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, true);
You can supply the targetWidth and targetHeight directly and it should be more reliable than scaling the bitmap yourself with a Matrix.
If you just supply the targetWidth to your method then you have to calculate the targetHeight which would look something like this:
float ratio = (float)targetWidth/(float)bitmap.getWidth();
int targetHeight = (int)((float)bitmap.getHeight * ratio);
And if you put that in your method it would look like this:
public static Bitmap getBitmapScaledToFitWidth(double targetWidth, Bitmap bitmap) {
double bitmapWidth = bitmap.getWidth();
double bitmapHeight = bitmap.getHeight();
double widthRatio = targetWidth / bitmapWidth;
double targetHeight = widthRatio * bitmapHeight;
Bitmap target = Bitmap.createScaledBitmap(bitmap, (int)targetWidth, (int)targetHeight, true);
return target;
}
Also don't forget to recycle() not needed bitmaps as often and as soon as possible. That can prevent you from running into memory problems, for example if you don't need the not scaled bitmap anymore after scaling it you can recycle it directly in your helper method:
public static Bitmap getBitmapScaledToFitWidth(double targetWidth, Bitmap bitmap) {
double bitmapWidth = bitmap.getWidth();
double bitmapHeight = bitmap.getHeight();
double widthRatio = targetWidth / bitmapWidth;
double targetHeight = widthRatio * bitmapHeight;
Bitmap target = Bitmap.createScaledBitmap(bitmap, (int)targetWidth, (int)targetHeight, true);
// Recycle old bitmap to free up memory.
bitmap.recycle();
return target;
}

Image scaling approach

I want to show 4 images in 2 x 2 grid format on the screen. Images are sourced from google image search and images are square of 200 X 200
This is my approach to scale them. RelativeLayout with 4 nested RelativeLayout and each layout has imageView in it. and this is how I get screen width to scale images. Setting internal layoutparams height and width to screenWidth/2 and then scaling images.
this is what I am doing to get the image height and width for particular screen. e.g if screen width is 550 then my image size would be 275 x 275.
public static int getOptionWidth(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.widthPixels;
}
optionWidth = (getOptionWidth(context) / 2)
This is for unscaled bitmap
public static Bitmap resourceDecoder(byte[] imgBytes, int destWidth, int destHeight) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length, options);
options.inJustDecodeBounds = false;
float srcAspect = (float) srcWidth / (float) srcHeight;
float dstAspect = (float) dstWidth / (float) dstHeight;
if (srcAspect > dstAspect) {
options.inSampleSize = srcHeight / dstHeight;
} else {
options.inSampleSize = srcWidth / dstWidth;
}
Bitmap unscaledBitmap = BitmapFactory.decodeByteArray(imgBytes, 0, imgBytes.length, options);
return unscaledBitmap;
}
This will be my destination width and height because I need images in square. I have implemented basic method to get source rectangle (getSrcRect) and get destination rectangle (getDstRect)
Rect srcRect = getSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight);
Rect dstRect = getDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight);
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
This is working fine and results are coming as expected (tested on hdpi, xhdpi and mdpi). But now I am confused as I am no using dxtopx or pxTodX conversion. Am I missing something? though results are as expected I am little worried about the approach. I don't know should I use pxToDx or vice-versa. If I do how does it affect my result and how should I use these.
You don't have to use PX to DP conversion for this, because you already set all of your size variables relative to screen width by using getOptionWidth(context).

Set wallpaper in Android

I have image 640*480 in sd card and i want to set it as home screen wallpaper. How Can I do this with small quality loss? With my code Wallpaper has a not good clarity. That's my code:
private void setWallpaper(String filePath) throws IOException{
if(filePath!=null){
// set options for decoding
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
options.inJustDecodeBounds = true;
//get bitmap from filepath
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
//get sizes of bitmap
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//get sizes of screen and scale level
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int newWidth = display.getWidth()*2;
int newHeight = display.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
//set matrix of the scaling
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//get new bitmap for wallpaper
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
//set bitmap as wallpaper
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
wallpaperManager.clear();
wallpaperManager.setBitmap(resizedBitmap);
}
}
Any time you scale upwards you're going to suffer quality loss. What size screen are you trying to display this image one? Keep in mind the Android wallpaper dimensions are the height of the screen by 2 times the width of the screen (e.g. 800x480 screen needs a 800x960 image).
My recommendation would be to have a larger image and scale downwards. The quality loss from downsampling is far less noticeable than from upscaling. You could, if space is not an issue, include multiple sizes of the image for differing screen resolutions.

Categories

Resources