How to remove blank space around an image in Android?
You can trim transparent space of around the image using single property of ImageView view.
You can use android:adjustViewBounds="true"
property into XML layout file to trim transparent space of respective image.
I had difficulties to find best practices or even advices to solve my problem. Based on this anwer by JannGabriel, who crops the image right and bottom by reducing image size, i managed to make a step further and also remove the top and left blank spaces, and to generally improve elaboration time. The result is good, and i am currently using it in my project. I'm fairly new to Android programming and any advice on this method is welcome.
public static Bitmap TrimBitmap(Bitmap bmp) {
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
//TRIM WIDTH - LEFT
int startWidth = 0;
for(int x = 0; x < imgWidth; x++) {
if (startWidth == 0) {
for (int y = 0; y < imgHeight; y++) {
if (bmp.getPixel(x, y) != Color.TRANSPARENT) {
startWidth = x;
break;
}
}
} else break;
}
//TRIM WIDTH - RIGHT
int endWidth = 0;
for(int x = imgWidth - 1; x >= 0; x--) {
if (endWidth == 0) {
for (int y = 0; y < imgHeight; y++) {
if (bmp.getPixel(x, y) != Color.TRANSPARENT) {
endWidth = x;
break;
}
}
} else break;
}
//TRIM HEIGHT - TOP
int startHeight = 0;
for(int y = 0; y < imgHeight; y++) {
if (startHeight == 0) {
for (int x = 0; x < imgWidth; x++) {
if (bmp.getPixel(x, y) != Color.TRANSPARENT) {
startHeight = y;
break;
}
}
} else break;
}
//TRIM HEIGHT - BOTTOM
int endHeight = 0;
for(int y = imgHeight - 1; y >= 0; y--) {
if (endHeight == 0 ) {
for (int x = 0; x < imgWidth; x++) {
if (bmp.getPixel(x, y) != Color.TRANSPARENT) {
endHeight = y;
break;
}
}
} else break;
}
return Bitmap.createBitmap(
bmp,
startWidth,
startHeight,
endWidth - startWidth,
endHeight - startHeight
);
}
Explanation: For each side of the image, a FOR loop is run to check if pixels does not contains transparent color, returning the first non-transparent pixel useful coordinate. This is done elaborating coordinates using as a base the opposite dimension than the dimension to trim: to find y, scan x for every y.
To check where the Vertical-Top blank space ends, it runs the following steps:
- Starting is from the top row (y=0)
- Checks all the columns of the row (x from 0 to imageWidth)
- If a non-transparent pixel is found, break the loop and save the y coordinate. Otherwise continue.
- At the ending of the columns, go to the next row (y+1) and start checking columns agains. Break if a non-transparent pixel has already been found.
Similiar methods are used for the other dimensions, only changing the direction of the scan.
Once obtained the 4 coordinates for the first useful pixels of the image, the Bitmap.createBitmap
method is invoked, with the original bitmap as a base image, and the useful pixels coordinates as Top-Left and Bottom-Right limits for the resize.
Note 1: It is useful to note that the coordinates 0, 0 equals to Top-Left.
Note 2: The ending width and height in Bitmap.createBitmap are reduced by the new starting relative coordinate, otherwise the new image will have the boundaries wrongly pushed bottom-right. Figure it like this: you have an image 100x100px, so with ending coordinates 100,100. Changing the starting coordinates to 50,50 will bring the ending coordinates of your elaboration rectangle to 150,150 (100 original coordinate + 50 of modified starting point), pushing it outside the original image boundaries. To avoid this, the new ending coordinate is reduced by the new starting coordinate (100 + 50 new starting coord - 50 new starting coord adjustment)
Note 3: in the original answer, a check for all the pixels in a given direction is run using the same dimension of the coordinate to find, returning the most advanced useful pixel. Checking the opposite dimension and stopping at the first useful pixel increased performances.
Kotlin implementation for answer @Manzotin with fix small bugs.
fun Bitmap.trimBorders(color: Int): Bitmap {
var startX = 0
loop@ for (x in 0 until width) {
for (y in 0 until height) {
if (getPixel(x, y) != color) {
startX = x
break@loop
}
}
}
var startY = 0
loop@ for (y in 0 until height) {
for (x in 0 until width) {
if (getPixel(x, y) != color) {
startY = y
break@loop
}
}
}
var endX = width - 1
loop@ for (x in endX downTo 0) {
for (y in 0 until height) {
if (getPixel(x, y) != color) {
endX = x
break@loop
}
}
}
var endY = height - 1
loop@ for (y in endY downTo 0) {
for (x in 0 until width) {
if (getPixel(x, y) != color) {
endY = y
break@loop
}
}
}
val newWidth = endX - startX + 1
val newHeight = endY - startY + 1
return Bitmap.createBitmap(this, startX, startY, newWidth, newHeight)
}