Search code examples
javaalpha

How do I fade a BufferedImage when manipulating it's pixel data?


I am trying to do something I have never done before. My goal is to be able to manipulate the opacity of a BufferedImage. First off, I do not use Graphics. I am developing a simple Game Engine and I only use pixel data from BufferedImages.

What have I tried?

I made my own "Image" class that takes in a BufferedImage.

BufferedImage image = null;
    try {
        image = ImageIO.read(Image.class.getResourceAsStream(resourcePath));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
    width = image.getWidth();
    height = image.getHeight();
    hasAlpha = image.getColorModel().hasAlpha();
    
    pixels = image.getRGB(0, 0, width, height,null,0,width);

After that I proceeded to my rendering code. I have a function called "drawImage()" that takes the image and it's data and then puts it out on a specific spot on the screen depending on the users wishes. I however want the user to choose it's opacity as well.

I started out by taking the pixel data for that pixel and I instead made a Color.

Color c = new Color(image.getPixels()[value2],true);

The "true" statement, what I know of, says that it should "care" about Alpha.

After that I place the pixel by using this code:

pixels[x + y * pWidth] = new Color(c.getRed(),c.getGreen(),c.getBlue(),alphaValue).getRGB();

This doesn't work. It doesnt output any errors and I can change the alphaValue to a number between 0 and infinity. However one issue I found is that it doesnt affect the image at all when I change the alpha from 255 to something like 50. It stays the same. The affect comes at 0-5. Another thing that I noticed is that when I set alphaValue to something like 2 I get an animation instead of the image with lower opacity. Sounds weird, that's why I provided a .GIF of the issue:

Fade issue

I guarantee you that there is no code that changes the alphaValue. During that "animation" it is set to 2 and nothing else.

So something is weird here and I wonder if you guys know the issue?

It might have something to do with the Image class and that I don't set it to BufferedImage.RGBA but I don't know how I can do so when I load an image?

If you need any more information, please leave a comment and I will provide more info!

Thanks in advance!

EDIT I clear the screen every 1/60 of a second by manipulating the backgrounds pixels (also a bufferedImage):

public void clear() {
    for(int i = 0; i < pixels.length; i++) {
        pixels[i] = clearColor.getRGB();
    }
    
    
}

Possible issue

I did some troubleshooting and I found out that it actually somehow increased in opacity each frame as @Joni said. Somehow the opacity or alpha seems to increase each frame but I am clearing the screen between drawings. I will troubleshoot further.

UPDATE

I made so I could move the image around and found this weird behaviour:

Link to Imgur

It seems that the "alpha" isn't reset. I know that the RGB is being reset but something is really off when it comes to the Alpha.

I changed the clear function to this:

public void clear() {
    for(int i = 0; i < pixels.length; i++) {
        pixels[i] = new Color(clearColor.getRed(),clearColor.getGreen(),clearColor.getBlue(),255).getRGB();
    }
    
    
}

And I defined clearColor as:

Color clearColor = new Color(Color.black.getRGB(),true);

That did however not work either. Does anyone have a solution?


Solution

  • Your game engine is drawing the image on top of itself in a loop. After drawing it enough many times the effect is the same as not having used transparency at all. The lower the alpha, the longer it takes though: with 50% alpha you need 7 frames to get 99% opacity, with 5% alpha you need about 90 frames.

    For example, suppose you are drawing a pixel value of 100 on a screen that's intially 0 (black) with 50% opacity. After the first frame, the output pixel value is .5*100 + .5*0 = 50. The second frame is drawn on top of the first frame, so the output pixel value is .5*100 + .5*50 = 75. The third frame, drawn on top of the second frame, will show .5*100 + .5*75 = 87.5.

    To avoid this, you need to fill a solid background color under the image in every frame.