Transparency

Does Java allow you to set a certain color transparent after the image has been loaded into memory? I don’t want to go through all my GIFs and make the background transparent. I remember that C# has a feature where you specify a pixel and its color becomes transparent. Does Java have this? Can anyone post some code?

Regards,
Igor

[quote] I don’t want to go through all my GIFs and make the background transparent.
[/quote]
Then write a program to do it for you.

You realy should have the image data in the format expected by the program that will be using it.

As for how to do it, its pretty simple.
Something along these lines should do it :-
(btw I havn’t compiled or run this so dunno if it’ll work :P)


BufferedImage loadedImage = ImageIO.read(url);

IndexColorModel icm = (IndexColorModel)(loadedImage.getColorModel()); //assume image has a IndexColorModel(i.e. a gif)

if(icm.getTransparency()!=Transparency.OPAQUE) return; //image already has transparency.

//now, get the colormap and find a Color matching Color.PINK
//(or whatever color you want to make transparent)

int [] cmap = new int[icm.getMapSize()];
icm.getRGBs(cmap);
int transColor = Color.PINK.getRGB();

int transIndex = -1;
for(int i =0;i< cmap.length;i++)
{
   if(cmap[i]==transColor)
   {
      transIndex = i;
      break;
   }
}

if(transIndex==-1) return; //no pixels are the correct Color

//now we have the index of the color we want as transparent, we need to create a ColorModel.

IndexColorModel newIcm = new IndexColorModel(icm.getPixelSize(), cmap.length, cmap, 0, true, transIndex, icm.getTransferType());

//now we just need to pass this new ColorModel and the raster of the original image into a new BufferedImage

BufferedImage finalImage = new BufferedImage(newIcm,loadedImage.getRaster(),loadedImage.isAlphaPremultiplied(),null);

Thanks Abuse,

Its what I was looking for. One question though… what did you mean when you called “plied()”? Is it a typo? What package do I have to import?

it’s isAlphaPremultiplied()

(remove the space)

btw, did the above code work as intended?

Sorry I took a while. The code compiles but throws an exception of type ClassCastException. It’s thrown at the line:

IndexColorModel icm = (IndexColorModel)(loadedImage.getColorModel());

can anyone tell me why this is so?

hmmmmmm.
I assumed gifs when loaded are created with an IndexColorModel - maybe they arn’t.
Perhaps they are automatically expanded to a full ARGB [or platform equivalent] DirectColorModel image.

try adding :-

System.out.println(loadedImage.getColorModel());

To see what type of ColorModel loaded gifs actually have.

This is what it printed:

DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=1000000

Ah there you go… bitmask transparency, but NOT an indexed color model. Interesting.

urg, thats not good - ImageIO must be expanding the gifs IndexColorModel into a DirectColorModel when it returns it as a BufferedImage.

That isn’t good for what you want to do =(

It means you are going to have to scan over every pixel in the image, check if its Color.PINK, and setting the alpha bit to either 1 or 0 depending on the result.
You can use a subclass of java.awt.image.RGBImageFilter to do the work for you (there is an example that swaps red & blue pixels in the javadoc for the class).

However, that is gonna be a fairly slow process compared to the solution I posted above (as you will be iterating over width*height pixels, rather than colordepth/2 palette entries).
I would definitly recommend not doing this at application load time, instead write an app. to preprocess and fix your gifs.

Looks like a DirectColorModel with 24+alpha bits is used whenever an image is loaded, regardless of the original image’s pixel format.

The easiest (but not necessarily the fastest) way to make some pixels transparent would be to just manually test and set invididual pixels. Something like:


// This image is not accelerated
BufferedImage sprite = ImageIO.read("sprite.gif");

// We want to make the color red transparent
int transColor = 0x01FF0000; // opaque red color

for(int i=0; i < sprite.getWidth(); i++)
{
   for(int j=0; j < sprite.getHeight(); j++)
   {
       if(sprite.getRGB(i, j) == transColor)
          sprite.setRGB(i, j, 0); // make it transparent
   }
}

You can also manipulate the pixels directly by getting the pixel array through the DataBuffer of the image, but when you do that your image will lose any hardware acceleration. However, the process is a lot faster if you manipulate the array directly.

This should not be the case…


import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class Test1 {
    public static void main (String[] args) {
      try {
          BufferedImage bi = ImageIO.read(new File(args[0]));
          System.err.println("color model=" + bi.getColorModel());
      } catch (IOException e) {
          System.err.println("Can't load image due to "+ e);
      }
    }
}

#>java Test1 duke2.gif
color model=IndexColorModel: #pixelBits = 8 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@1f934ad transparency = 2 transIndex = 169 has alpha = true isAlphaPre = false

Thats what I expected it would do :S

ikantor are you sure you are using a gif?

btw on a slightly different note.

Are gifs expanded to a DirectColorModel when they are cached in vram? or is it still rendered as an IndexColorModel (just through hardware instead)

if it is the latter, is it possible to manipulate the images pallete without forcing the image to be recached?
(i.e. can we do pallete cycling?)

Yes, 8-bit images get expanded to the display format, so the trick with palette change won’t fly, sorry…