1

I am trying to make a function that will crop the transparent edges of a BufferedImage only from the right and left side.

So, I found this method of doing this somewhere online, however I don't really understand what is the pixels[] array filled with? I assumed it was the alpha, red, green, blue values for every single pixel (judging by the 4 in (j*width+i)*4 , it picks every fourth element), but the length of this array is 299208 for a 68x67 image (I know it's not a power of 2, just ignore that).

So, how do I accomplish this and I'm curious what's actually stored in this pixel array?

public static BufferedImage trimHorizontally(BufferedImage img) {
    final byte[] pixels = ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
    int width = img.getWidth(), height = img.getHeight();
    int x0, x1;
    int j, i;

    leftLoop:
        for(i = 0; i < width; i++) {

            for(j = 0; j < height; j++) {

                if(pixels[(j*width+i)*4] != 0) {
                    break leftLoop;
                }
            }
        }
    x0 = i;
    rightLoop:
        for(i = width-1; i >= 0; i--) {

            for(j = 0; j < height; j++) {

                if(pixels[(j*width+i)*4] != 0) {
                    break rightLoop;
                }
            }
        }
    x1 = i+1;

    return img.getSubimage(x0, 0, x1-x0, height);


}

What I'm basically doing in the above code is scanning the image from the left and from the right side, checking for the alpha value and marking the distance I need to crop, but it throws an exception because the loops finish without detecting any opaque pixels (and the images I'm cropping are not transparent for sure).

I made a check and found out that the alpha values greater than 0 are actually above the index of 35000 of the pixels[] array, and with the (j*width+i)*4 calculation I can only reach 18000....

River
  • 8,585
  • 14
  • 54
  • 67
Robby
  • 71
  • 7
  • The `pixels[]` array does indeed store what you thought. Not sure why the length is so long. – River Nov 07 '17 at 23:46
  • 1
    Is your image already cropped using the `getSubimage(...)` method? Beware that this method *does not create a copy*, thus the image will share the backing array (incl. size) with the original image. You can get the correct offsets/dimensions from the `Raster`. Also, unconditionally casting the `DataBuffer` to either `DataBufferInt` or `DataBufferByte` is obviously not safe, unless you have full control of the input image. – Harald K Nov 08 '17 at 09:35
  • Yes the image is cropped using getSubimage() before it uses this method... – Robby Nov 10 '17 at 16:27

1 Answers1

1

Quick Fix

You can just make pixels with:

final int[] pixels = ((DataBufferInt)img.getAlphaRaster().getDataBuffer()).getData();

and remove the *4 in both (j*width+i)*4. This just makes your pixels[] only contain alpha values, and not RGB.

However there's a safer (no unsafe DataBuffer casts), more readable solution:

Preferred Version

The changes I made were to stop with the horrible byte array, and simply use the getRGB() method instead. The alpha value can be gotten by creating a Color using the RGB+alpha constructor, and then the getAlpha() method.

Now the following code works for me:

public static BufferedImage trimHorizontally(BufferedImage img) {
    int width = img.getWidth(), height = img.getHeight();
    int x0, x1;
    int j, i;
    int alpha;

    leftLoop:
        for(i = 0; i < width; i++) {

            for(j = 0; j < height; j++) {
                if(new Color(img.getRGB(i, j), true).getAlpha() != 0)  {
                    break leftLoop;
                }
            }
        }
    x0 = i;
    rightLoop:
        for(i = width-1; i >= 0; i--) {

            for(j = 0; j < height; j++) {
                if(new Color(img.getRGB(i, j), true).getAlpha() != 0) {
                    break rightLoop;
                }
            }
        }
    x1 = i+1;

    return img.getSubimage(x0, 0, x1-x0, height);
}

Notes:

img must be of type TYPE_INT_ARGB, or some other type that keeps it's alpha value in the last byte of getRGB()'s data (if you don't know what this means, just try the above code and if it doesn't work covert to ARGB.)

Also make sure you use an image format that supports transparency (not JPG).

River
  • 8,585
  • 14
  • 54
  • 67
  • Yes, it does. However, I've heard somewhere that by using getRGB is inefficient compared to the pixels[] array way of doing it. – Robby Nov 10 '17 at 16:29