14

I want to save bitmap without transparent area.

Bitmap has large transparent pixel.

So i want to remove that

How can i do this?

I cant add picture so explain with symbols.

I dont want to crop function. I hope use filter

┌────────────────────────┐

│ transparent area

│ ┌────────┐

│ crop this
└────────┘
└────────────────────────┘

user1066874
  • 161
  • 2
  • 7

4 Answers4

28

To find the non-transparent area of your bitmap, iterate across the bitmap in x and y and find the min and max of the non-transparent region. Then crop the bitmap to those co-ordinates.

Bitmap CropBitmapTransparency(Bitmap sourceBitmap)
{
    int minX = sourceBitmap.getWidth();
    int minY = sourceBitmap.getHeight();
    int maxX = -1;
    int maxY = -1;
    for(int y = 0; y < sourceBitmap.getHeight(); y++)
    {
        for(int x = 0; x < sourceBitmap.getWidth(); x++)
        {
            int alpha = (sourceBitmap.getPixel(x, y) >> 24) & 255;
            if(alpha > 0)   // pixel is not 100% transparent
            {
                if(x < minX)
                    minX = x;
                if(x > maxX)
                    maxX = x;
                if(y < minY)
                    minY = y;
                if(y > maxY)
                    maxY = y;
            }
        }
    }
    if((maxX < minX) || (maxY < minY))
        return null; // Bitmap is entirely transparent

    // crop bitmap to non-transparent area and return:
    return Bitmap.createBitmap(sourceBitmap, minX, minY, (maxX - minX) + 1, (maxY - minY) + 1);
}
samgak
  • 23,944
  • 4
  • 60
  • 82
  • superb work dear ! just finding about this from last couple of days ! thank you again ! – Mayur R. Amipara Aug 04 '15 at 04:16
  • This worked really great. But it took a while to execute. Is it the fastest way to do this job? – nitech Oct 19 '15 at 08:59
  • @nitech if you call getPixels() or copyPixelsToBuffer() on the bitmap then you can iterate across the pixel data without having to call getPixel() once for each pixel. That should speed it up, but it will complicate things as you will have to take into account the stride and do the pixel indexing yourself. Doing it in native code would probably be faster still but seems needlessly complicated. – samgak Oct 19 '15 at 09:46
  • Thank you. I'll do a bit more research on speed. – nitech Oct 19 '15 at 10:14
  • @nitech Changing the algorithm would also speed it up. As it is it does a lot of unnecessary checks. For example you could scan down from the top and then up from the bottom to find the min and max y, then in the remaining region scan across each row, but in each row only check the region less than the current min x and greater than the current max x to work out the min and max x. Performance might be affected by reading the memory out of order however. – samgak Oct 19 '15 at 10:43
  • very good answer,,,,, but this process is so slow when i use images to recyclerview.......... can anyone have any code of this which is not slow – Ahamadullah Saikat Mar 13 '17 at 19:36
  • very much slow process.. make it fast – Ahamadullah Saikat Mar 13 '17 at 20:00
  • Perfect solution..! :) – Krupa Kakkad Nov 16 '18 at 11:57
8

Crop transparent border with this github.

public static Bitmap crop(Bitmap bitmap) {

    int height = bitmap.getHeight();
    int width = bitmap.getWidth();
    int[] empty = new int[width];
    int[] buffer = new int[width];
    Arrays.fill(empty, 0);
    int top = 0;
    int left = 0;
    int bottom = height;
    int right = width;

    for (int y = 0; y < height; y++) {
        bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
        if (!Arrays.equals(empty, buffer)) {
            top = y;
            break;
        }
    }

    for (int y = height - 1; y > top; y--) {
        bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
        if (!Arrays.equals(empty, buffer)) {
            bottom = y;
            break;
        }
    }

    empty = new int[height];
    buffer = new int[height];
    Arrays.fill(empty, 0);

    for (int x = 0; x < width; x++) {
        bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
        if (!Arrays.equals(empty, buffer)) {
            left = x;
            break;
        }
    }

    for (int x = width - 1; x > left; x--) {
        bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
        if (!Arrays.equals(empty, buffer)) {
            right = x;
            break;
        }
    }

    return Bitmap.createBitmap(bitmap, left, top, right - left + 1, bottom - top + 1);
}
Nabin Bhandari
  • 15,949
  • 6
  • 45
  • 59
Álvaro Menezes
  • 1,658
  • 1
  • 13
  • 14
  • Could you maybe add the code snippet here, so that other people don't have to go to the github repo? – Martin Gottweis Sep 12 '16 at 15:05
  • Done ;) @MartinGottweis.This code not read all pixels, just slice vertical and horizontal matrix, so it was faster in my tests. – Álvaro Menezes Sep 12 '16 at 17:32
  • 4
    crashes on this line ................................ bitmap.getPixels(buffer, 0, 1, x, top + 1, 1, bufferSize); /////////////////////// java.lang.IllegalArgumentException: y + height must be <= bitmap.height() – Ahamadullah Saikat Mar 13 '17 at 19:47
8

I took @Alvaro Menezes's answer and improved it as a Kotlin extension function. I tweaked it a bit, changed some variable names for better readability and it adds more fixes to the issue mentioned by @Ahamadullah Saikat that throws an IllegalArgumentException

Note that reading pixels by line improve a lot the performances against reading this independently as the accepted answer suggest.

/**
 * Trims a bitmap borders of a given color.
 *
 */
fun Bitmap.trim(@ColorInt color: Int = Color.TRANSPARENT): Bitmap {

    var top = height
    var bottom = 0
    var right = width
    var left = 0

    var colored = IntArray(width, { color })
    var buffer = IntArray(width)

    for (y in bottom until top) {
        getPixels(buffer, 0, width, 0, y, width, 1)
        if (!Arrays.equals(colored, buffer)) {
            bottom = y
            break
        }
    }

    for (y in top - 1 downTo bottom) {
        getPixels(buffer, 0, width, 0, y, width, 1)
        if (!Arrays.equals(colored, buffer)) {
            top = y
            break
        }
    }

    val heightRemaining = top - bottom
    colored = IntArray(heightRemaining, { color })
    buffer = IntArray(heightRemaining)

    for (x in left until right) {
        getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
        if (!Arrays.equals(colored, buffer)) {
            left = x
            break
        }
    }

    for (x in right - 1 downTo left) {
        getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
        if (!Arrays.equals(colored, buffer)) {
            right = x
            break
        }
    }
    return Bitmap.createBitmap(this, left, bottom, right - left, top - bottom)
}
crgarridos
  • 8,758
  • 3
  • 49
  • 61
0

Following the official doc:

The new bitmap may be the same object as source, or a copy may have been made.

You should take into account when you execute .recycle() with the source bitmap.

Azametzin
  • 5,223
  • 12
  • 28
  • 46
Sergio A
  • 71
  • 2
  • 3