Search code examples
javaalpha-transparency

I tried to implement transparency but something went wrong


I wanted to add "transparency" to my Game Engine, which I have no earlier knowledge of. I didn't find any straight answer on how to implement it so I ended up doing some research about something called Alpha Blending which is one way of showing opacity if I have understood it right.

I Googled around and tried to find a source that showed how to implement this when you have a pixel array. I have no clue how but I found nothing except a tutorial on Youtube. They didn't explain why they did like they did and due to that I still have no clue how I can implement it or how it works. I tried to follow the tutorial but the code they used didn't work at all so I changed it a bit (which clearly didn't work).

This code below is my setPixel() function that sets a pixel at a specified location. From the start of the function it just checks if it needs to place a pixel at all. This function is used to draw every individual pixel from the pixel data. The pixel data of the screen is stored in the variable pixels. The image data is stored in value. Value is however just an integer, while pixels is an array..

    public void setPixel(int x, int y, int value, Color invis) {
    
    int alpha = (value>>24);
    
    if(invis != null && value == invis.getRGB() || alpha == 0x00) {
        
        return;
    }
    
    if(!isOutSideScreen(x,y)) {
        
        
        
        if(alpha == 255) {
            pixels[x + y * pWidth] = value;
        }
        else {
            int pixelColor =  value;
             
            
            int newRed = ((pixelColor >> 16) & 0xff) + (int)((((pixelColor >> 16) & 0xff) - ((pixels[x + y * pWidth] >> 16) & 0xff)) * (alpha/255f));
            int newGreen = ((pixelColor >> 8) & 0xff) + (int)((((pixelColor >> 8) & 0xff) - ((pixels[x + y * pWidth] >> 8) & 0xff)) * (alpha/255f));
            int newBlue = (pixelColor & 0xff) + (int)(((pixelColor & 0xff) - (pixels[x + y * pWidth] & 0xff)) * (alpha/255f));
            
            
            
            pixels[x+y * pWidth] = ((255 << 24) | (newRed << 16) | (newGreen << 8) | newBlue);
        }
        
    }
    
}

What I dont understand about this code is all the bitwise code and why you calculate the colors like that.

                int newRed = ((pixelColor >> 16) & 0xff) + (int)((((pixelColor >> 16) & 0xff) - ((pixels[x + y * pWidth] >> 16) & 0xff)) * (alpha/255f));
            int newGreen = ((pixelColor >> 8) & 0xff) + (int)((((pixelColor >> 8) & 0xff) - ((pixels[x + y * pWidth] >> 8) & 0xff)) * (alpha/255f));
            int newBlue = (pixelColor & 0xff) + (int)(((pixelColor & 0xff) - (pixels[x + y * pWidth] & 0xff)) * (alpha/255f));
            
            
            
            pixels[x+y * pWidth] = ((255 << 24) | (newRed << 16) | (newGreen << 8) | newBlue);

The code acts like this: GIF

If someone can explain why this code doesn't work and how is actually works I would be forever grateful!

Thanks in advance and sorry for my ignorance!

Edit after Joni's answer

This is the code I now use:

            int pixelColor =  pixels[x+y * pWidth];
             
            
            int newRed  = (int)((1 - (alpha / 255f)) * ((pixelColor>>16) & 0xff) + (alpha / 255f) * ((value >> 16) & 0xff));
            int newGreen  = (int)((1 - (alpha / 255f)) * ((pixelColor>>8) & 0xff) + (alpha / 255f) * ((value >> 8) & 0xff));
            int newBlue  = (int)((1 - (alpha / 255f)) * (pixelColor & 0xff) + (alpha / 255f) * (value & 0xff));
            
            pixels[x+y * pWidth] = ((255 << 24) | (newRed << 16) | (newGreen << 8) | newBlue);

I used the formula: outColor = (1 - alpha) * backgroundColor + alpha * newColor


Solution

  • The formula for alpha blending "newColor" on top of "backgroundColor" is:

    outColor = (1 - alpha) * backgroundColor + alpha * newColor
    

    To see why it works, try different values of alpha. With alpha=0, you get background color. With alpha=1, you get newColor.

    The formula you've programmed is

    outColor = backgroundColor + alpha * (backgroundColor - newColor)
    

    Alpha = 1 gives you outColor = 2*backgroundColor-newColor which is incorrect. To fix it you need to swap pixelColor and pixels[x+y*pWidth] around - for example for blue channel:

    int newBlue = (pixelColor & 0xff) + (int)(((pixels[x + y * pWidth] & 0xff) - (pixelColor & 0xff)) * (alpha/255f));
    

    What I dont understand about this code is all the bitwise code and why you calculate the colors like that.

    This code assumes a color model that packs four 8-bit integers into one int. The most significant 8 bits make up the alpha component, followed by 8bits for the red color component, green color, and blue color each. The way you extract a component out of an int is with bit-wise operators. For example, color&0xff is the lowest 8 bits, therefore it's the blue component. (color>>8)&0xff gives you the second lowest 8 bits, which is the green component.