162

I have bitmaps which are squares or rectangles. I take the shortest side and do something like this:

int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
    value = bitmap.getHeight();
} else {
    value = bitmap.getWidth();
}

Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);

Then I scale it to a 144 x 144 Bitmap using this:

Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);

Problem is that it crops the top left corner of the original bitmap, Anyone has the code to crop the center of the bitmap?

Ameer Moaaviah
  • 1,530
  • 12
  • 27
Maurice
  • 6,413
  • 13
  • 51
  • 76

10 Answers10

373

enter image description here

This can be achieved with: Bitmap.createBitmap(source, x, y, width, height)

if (srcBmp.getWidth() >= srcBmp.getHeight()){

  dstBmp = Bitmap.createBitmap(
     srcBmp, 
     srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
     0,
     srcBmp.getHeight(), 
     srcBmp.getHeight()
     );

}else{

  dstBmp = Bitmap.createBitmap(
     srcBmp,
     0, 
     srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
     srcBmp.getWidth(),
     srcBmp.getWidth() 
     );
}
Lumis
  • 21,517
  • 8
  • 63
  • 67
  • 1
    Edited the answer so that the actual destination bitmap is a square. – Joram van den Boezem Mar 29 '13 at 19:00
  • 1
    @Lumis: Why did you rollback revision 3? It appeared to be a valid correct. Your current version creates the correct starting point but then includes the remainder of the too long side. For instance given a `100x1000` image you get back a `100x550` image. – Guvante Mar 29 '13 at 19:38
  • Thanks, you are right, the format is Bitmap.createBitmap(source, x, y, width, height) – Lumis Mar 29 '13 at 22:43
  • I'm getting an out of memory error using this code on the line that calls createBitmap...any ideas? It works on some devices but not all, and the OOM error is happening on a galaxy s3 which should have plenty of memory. Works fine on a super old phone running Android 2.2.1. – DiscDev Jul 18 '13 at 20:27
  • @soptdog13, OOM errors are a big topic. You need to do a research on OOM erros and learn everything how to avoid them. There is nothing wrong with this code, it is just a simple function. – Lumis Jul 20 '13 at 18:08
  • 12
    See my answer about using the built-in ThumbnailUtils.extractThumbnail() method. Why reinvent the wheel??? http://stackoverflow.com/a/17733530/1103584 – DiscDev Oct 03 '13 at 16:23
  • remember to recycle the srcBitmap :) – Dori Nov 08 '13 at 10:20
  • You can use my JNI solution for cropping, which avoid having 2 bitmaps at the same time : https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperations . also I would like to ask if there is a way to avoid this all by using a customize drawable that draws only the part of the bitmap you wish to draw. – android developer May 22 '14 at 18:16
  • 1
    this solution is shorter than creating a canvas and then drawing a drawable to it. it also does less processing than the ThumbnailUtils solution (which does sample size calculating to figure out how to scale). – yincrash Jun 18 '14 at 21:34
  • @Lumis hello, if we want to center crop image in 4:6 aspect ratio then how we get that.? – Sagar Maiyad Nov 27 '14 at 06:44
  • Suppose you have "Movie" as a replacement for the bitmap to show, and "Canvas" in order to show it. What should be done in this case? – android developer Mar 29 '16 at 22:25
  • Thank you for such a helpful illustration at the beginning! – Touré Holder Sep 05 '18 at 19:39
  • I want to crop image in different ratios like 9:16, 4:3 and some more, how can achieve this because it will too lengthy code if i use yours code.. – Vivek Thummar Feb 20 '21 at 05:24
344

While most of the above answers provide a way to do this, there is already a built-in way to accomplish this and it's 1 line of code (ThumbnailUtils.extractThumbnail())

int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);

...

//I added this method because people keep asking how 
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
    //use the smallest dimension of the image to crop to
    return Math.min(bitmap.getWidth(), bitmap.getHeight());
}

If you want the bitmap object to be recycled, you can pass options that make it so:

bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);

From: ThumbnailUtils Documentation

public static Bitmap extractThumbnail (Bitmap source, int width, int height)

Added in API level 8 Creates a centered bitmap of the desired size.

Parameters source original bitmap source width targeted width height targeted height

I was getting out of memory errors sometimes when using the accepted answer, and using ThumbnailUtils resolved those issues for me. Plus, this is much cleaner and more reusable.

James Fenn
  • 109
  • 1
  • 14
DiscDev
  • 38,652
  • 20
  • 117
  • 133
  • 8
    +1 I think you would have to improve on this code and instead of using 400px, pass the shortest bmp's size, in order to provide an alternative for the original post above. But thank you for bringing this to our attention, it seems a very useful function. Pity I have not seen it before... – Lumis Oct 10 '13 at 20:50
  • Right, I just hardcoded 400 to give a concrete example...the details are up to the implementor :) – DiscDev Oct 10 '13 at 21:00
  • 1
    +1 good spot - didnt know this existed - simples - crop and resize in one! – Dori Nov 08 '13 at 10:29
  • I want to crop some width more from left and right corner and height is fine for me.so change the following line like this ThumbnailUtils.extractThumbnail(source, newWidth-100, newHeight-50) but not worked.How to achieve this? – Akanksha Rathore Jan 16 '14 at 17:38
  • Is there a way to accomplish that by using a drawable instead of creating a new bitmap ? – android developer May 22 '14 at 12:00
  • @androiddeveloper not that I know of. – DiscDev May 22 '14 at 16:57
  • 4
    @DiscDev - this would have to be literally one of the most useful answers on this entire site. Seriously. It's weird with Android questions - you can often search for two hours before finding the simple obvious answer. Don't know how to thank you enough. Bounty en route! – Fattie May 25 '14 at 19:23
  • 2
    You could even use a little different function, to also recycle the old bitmap : = ThumbnailUtils.extractThumbnail(bitmap, width,height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT) – android developer Jun 16 '14 at 08:30
  • @DiscDev maybe you should mention both ways, so that people won't be surprised to see the older bitmap gets recycled. Also, you can do this using JNI too , by modifying my library a bit: https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperations/ – android developer Jun 16 '14 at 20:20
  • 1
    This is the most powerful answer I've seen so far for android question. I've seen like 20 different solutions how to scale/crop/downscale/resize and so on Bitmap on Android. All answers are different. And this one just takes 1 line and works exactly as expected. Thank you. – Alex Sorokoletov Jul 27 '14 at 17:12
  • According to the question it is not the correct answer, just an another approach. Question is center of the Bitmap whereas your solution scales image into required mentioned. Accepted answer is the best solution. – Murtaza Khursheed Hussain Jan 27 '15 at 13:16
  • @MurtazaHussain your analysis is patently incorrect. The javadoc for extractThumbnail explicitly states that it "Creates a centered bitmap of the desired size." So, whatever dimensions you pass in, it will center-crop the image and give you back something of that size. To appease you, I updated my answer to dynamically calculation the dimensions of the desired image based on the bitmap you passed in. – DiscDev Jan 29 '15 at 19:17
  • @Lumis I updated the answer to dynamically calculate the width/height of the thumbnail to be in line with the accepted answer. This should be the accepted answer now. – DiscDev Jan 29 '15 at 19:18
13

Have you considered doing this from the layout.xml ? You could set for your ImageView the ScaleType to android:scaleType="centerCrop" and set the dimensions of the image in the ImageView inside the layout.xml.

Ovidiu Latcu
  • 71,607
  • 15
  • 76
  • 84
  • I tried this idea with the following OpenGLRenderer error: "Bitmap too large to be uploaded into a texture (2432x4320, max=4096x4096)" So, I am guessing the the 4320 height can't be processed. – GregM Jul 15 '14 at 20:25
  • 1
    Of course this is a correct answer and answers the question just perfect! Optimizing the image quality / size for large images... Well, it's a different question! – ichalos Apr 21 '15 at 16:24
  • @ichalos Perhaps you found what you were looking for, but this does not answer the original question. The original question was about manually rendering on canvas.. actually, I'm not sure how anyone's first attempt to crop a photo would be manually rendering on a canvas, but hey, good that you found a solution here :) – milosmns Mar 24 '19 at 15:56
  • @milosmns Maybe you are right. Maybe this is just how the user tried to approach the problem of manually cropping to the center. It all depends on the exact needs of the original user. – ichalos Mar 26 '19 at 04:55
10

Probably the easiest solution so far:

public static Bitmap cropCenter(Bitmap bmp) {
    int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
    return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}

imports:

import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
Kirill Kulakov
  • 10,035
  • 9
  • 50
  • 67
9

Here a more complete snippet that crops out the center of an [bitmap] of arbitrary dimensions and scales the result to your desired [IMAGE_SIZE]. So you will always get a [croppedBitmap] scaled square of the image center with a fixed size. ideal for thumbnailing and such.

Its a more complete combination of the other solutions.

final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();

float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);

Bitmap croppedBitmap;
if (landscape){
    int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
    int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
    croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
Amit
  • 3,952
  • 7
  • 46
  • 80
mschmoock
  • 20,084
  • 6
  • 33
  • 35
9

You can used following code that can solve your problem.

Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);

Above method do postScalling of image before cropping, so you can get best result with cropped image without getting OOM error.

For more detail you can refer this blog

Hitesh Patel
  • 2,868
  • 2
  • 33
  • 62
  • 1
    E/AndroidRuntime(30010): Caused by: java.lang.IllegalArgumentException: x + width must be <= bitmap.width() and this because of 4 times 100px – Stan Jul 14 '13 at 15:03
3

To correct @willsteel solution:

if (landscape){
                int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
            } else {
                int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
                croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
            }
Yman
  • 934
  • 8
  • 16
  • this is a fix to WillSteel solution. In this case, tempBitmap is just a copy of the original (unaltered) Bitmap, or itself. – Yman Jun 24 '13 at 13:24
3
public Bitmap getResizedBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    int narrowSize = Math.min(width, height);
    int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
    width  = (width  == narrowSize) ? 0 : differ;
    height = (width == 0) ? differ : 0;

    Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
    bm.recycle();
    return resizedBitmap;
}
JHobern
  • 866
  • 1
  • 13
  • 20
Vahe Gharibyan
  • 5,277
  • 4
  • 36
  • 47
1
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    if (w == size && h == size) return bitmap;
    // scale the image so that the shorter side equals to the target;
    // the longer side will be center-cropped.
    float scale = (float) size / Math.min(w,  h);
    Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
    int width = Math.round(scale * bitmap.getWidth());
    int height = Math.round(scale * bitmap.getHeight());
    Canvas canvas = new Canvas(target);
    canvas.translate((size - width) / 2f, (size - height) / 2f);
    canvas.scale(scale, scale);
    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
    canvas.drawBitmap(bitmap, 0, 0, paint);
    if (recycle) bitmap.recycle();
    return target;
}

private static Bitmap.Config getConfig(Bitmap bitmap) {
    Bitmap.Config config = bitmap.getConfig();
    if (config == null) {
        config = Bitmap.Config.ARGB_8888;
    }
    return config;
}
kakopappa
  • 5,023
  • 5
  • 54
  • 73
1
    val sourceWidth = source.width
    val sourceHeight = source.height
    val xScale = newWidth.toFloat() / sourceWidth
    val yScale = newHeight.toFloat() / sourceHeight
    val scale = xScale.coerceAtLeast(yScale)

    val scaledWidth = scale * sourceWidth
    val scaledHeight = scale * sourceHeight
    val left = (newWidth - scaledWidth) / 2
    val top = (newHeight - scaledHeight) / 2
    val targetRect = RectF(
        left, top, left + scaledWidth, top
                + scaledHeight
    )
    val dest = Bitmap.createBitmap(
        newWidth, newHeight,
        source.config
    )
    val mutableDest = dest.copy(source.config, true)
    val canvas = Canvas(mutableDest)
    canvas.drawBitmap(source, null, targetRect, null)
    binding.imgView.setImageBitmap(mutableDest)