Reputation:
I need to change the white colors in an image to transparent. I tried this.. it pulls the raster grabs the pixels line by line and fills a second image with abgr. Where a =0 if the rgb values equal white or 255 if the rgb values dont.and writes them to a second image.
package color;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class ColorToAlpha {
public static void main(String[] args){
try {
BufferedImage i=ImageIO.read(ColorToAlpha.class.getResource("brown.jpg"));
JLabel jl=new JLabel();
jl.setIcon(new ImageIcon(rasterToAlpha(i,Color.white)));
JOptionPane.showConfirmDialog(null, jl);
} catch (IOException e) {}
}
public static BufferedImage rasterToAlpha(BufferedImage r,Color c){
BufferedImage bi=new BufferedImage(r.getWidth(),r.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);
WritableRaster wr2=bi.getRaster();
WritableRaster wr=r.getRaster();
for(int i=0;i<r.getHeight();i++){
int[] xx=new int[4*r.getWidth()];//array for the abgr
int[] x=new int[3*r.getWidth()];//array for the bgr
wr.getPixels(0, i, r.getWidth(),1,x);//get them line by line
for(int j=0,k = 0;j<x.length;j+=3,k+=4){
if(c.equals(new Color( x[j+2], x[j+1],x[j]))){//flip bgr to rgb and check
xx[k]=0;xx[k+1]=0;xx[k+2]=0;xx[k+3]=0;//if its the same make it transparent
}
else{xx[k]=255;xx[k+1]=x[j];xx[k+2]=x[j+1];xx[k+3]=x[j+2];}//make it opaque
}
wr2.setPixels(0, i, r.getWidth(),1,xx);//write the line
}
return bi;}
}
but it comes out very funny.What am I doing wrong please help. this is what it ends as.. EDIT it definitly is better but it is red not black.
Upvotes: 1
Views: 1825
Reputation: 34628
Although the BufferedImage
type is BGR for the source and ABGR for the target image, the integers you are getting from getPixels
are actually in the common RGB order. And the ABGR image expects the array to be in RGBA order.
This is not too well-documented, but the colors are mapped to the integers according to the color model of the image (which is RGB), not according to the raw buffer ordering.
The upshot of all this is that when you set the k
element to 255, it's actually the red value, so you get a "reddened" image. And you actually get the alpha value from what was the original image's blue value by putting the j+2
value of the source array into the k+3
value of the target array.
It also means that you are not supposed to reverse the order of the values to create a Color
object for comparison to your color.
So here is a corrected loop for you (note that I renamed the variables to make a little more sense. You really shouldn't be using names like r
,x
,xx
,c
and so on, it's unreadable. And formatting is also important!):
public static BufferedImage rasterToAlpha(BufferedImage sourceImage, Color origColor) {
BufferedImage targetImage = new BufferedImage(sourceImage.getWidth(),
sourceImage.getHeight(),
BufferedImage.TYPE_4BYTE_ABGR);
WritableRaster targetRaster = targetImage.getRaster();
WritableRaster sourceRaster = sourceImage.getRaster();
for (int row = 0; row < sourceImage.getHeight(); row++) {
int[] rgba = new int[4 * sourceImage.getWidth()];
int[] rgb = new int[3 * sourceImage.getWidth()];
// Get the next row of pixels
sourceRaster.getPixels(0, row, sourceImage.getWidth(), 1, rgb);
for (int i = 0, j = 0; i < rgb.length; i += 3, j += 4) {
if (origColor.equals(new Color(rgb[i], rgb[i + 1], rgb[i + 2]))) {
// If it's the same make it transparent
rgba[j] = 0;
rgba[j + 1] = 0;
rgba[j + 2] = 0;
rgba[j + 3] = 0;
} else {
rgba[j] = rgb[i];
rgba[j + 1] = rgb[i + 1];
rgba[j + 2] = rgb[i + 2];
// Make it opaque
rgba[j + 3] = 255;
}
}
// Write the line
targetRaster.setPixels(0, row, sourceImage.getWidth(), 1, rgba);
}
return targetImage;
}
Upvotes: 3