Coldsteel48
Coldsteel48

Reputation: 3512

Pixels Overlay With transparency

I have 2 pixels in B8G8R8A8 (32) format. Both pixels (top and bottom) has transparency (Alpha channel < 255 )

What is the way (formula) to overlay top pixel on the bottom one ? (without using 3rd parties).

I tried to do something like this

struct FColor
{
public:
    // Variables.
#if PLATFORM_LITTLE_ENDIAN
    #ifdef _MSC_VER
        // Win32 x86
        union { struct{ uint8 B,G,R,A; }; uint32 AlignmentDummy; };
    #else
        // Linux x86, etc
        uint8 B GCC_ALIGN(4);
        uint8 G,R,A;
    #endif
#else // PLATFORM_LITTLE_ENDIAN
    union { struct{ uint8 A,R,G,B; }; uint32 AlignmentDummy; };
#endif
//...
};

FORCEINLINE FColor AlphaBlendColors(FColor pixel1, FColor pixel2)
{
    FColor blendedColor;
    //Calculate new Alpha:
    uint8 newAlpha = 0;
    newAlpha = pixel1.A + pixel2.A * (255 - pixel1.A);

    //get FColor as uint32
    uint32 colora = pixel1.DWColor();
    uint32 colorb = pixel2.DWColor();

    uint32 rb1 = ((0x100 - newAlpha) * (colora & 0xFF00FF)) >> 8;
    uint32 rb2 = (newAlpha * (colorb & 0xFF00FF)) >> 8;
    uint32 g1 = ((0x100 - newAlpha) * (colora & 0x00FF00)) >> 8;
    uint32 g2 = (newAlpha * (colorb & 0x00FF00)) >> 8;
    blendedColor = FColor(((rb1 | rb2) & 0xFF00FF) + ((g1 | g2) & 0x00FF00));


    blendedColor.A = newAlpha;
    return blendedColor;
}

But the result is far not what I want :-)

I looked for some Alpha blending formulas (I did never understand how would I calculate a new alpha of the overlay) -> perhaps I was going in a wrong direction ?

Edit:

Changing the newAlpha to newAlpha = FMath::Min(pixel1.A + pixel2.A, 255); Actually gives a much better result, but is it right to calculate it like this ? Am I missing something here?

Working Example Based On Accepted Answer)

   FORCEINLINE FColor AlphaBlendColors(FColor BottomPixel, FColor TopPixel)
{
    FColor blendedColor;
    //Calculate new Alpha:
    float normA1 = 0.003921568627451f * (TopPixel.A);
    float normA2 = 0.003921568627451f * (BottomPixel.A);

    uint8 newAlpha = (uint8)((normA1 + normA2 * (1.0f - normA1)) * 255.0f);

    if (newAlpha == 0)
    {
        return FColor(0,0,0,0);
    }

    //Going By Straight Alpha formula
    float dstCoef = normA2 * (1.0f - normA1);
    float multiplier = 255.0f / float(newAlpha);

    blendedColor.R = (uint8)((TopPixel.R * normA1 + BottomPixel.R * dstCoef) * multiplier);
    blendedColor.G = (uint8)((TopPixel.G * normA1 + BottomPixel.G * dstCoef) * multiplier);
    blendedColor.B = (uint8)((TopPixel.B * normA1 + BottomPixel.B * dstCoef) * multiplier);

    blendedColor.A = newAlpha;

    return blendedColor;
}

Upvotes: 1

Views: 660

Answers (1)

Aziuth
Aziuth

Reputation: 3902

Start by assuming that there is a third pixel below that happens to be opaque.

For the further notations, I will assume that alpha values are in [0,1].

Given: three pixels with the first one being on top, colors c_1, c_2, c_3, alpha values a_1, a_2, a_3 = 1

Then the resulting alpha value is obviously 1 and the color is

(a_1)*c_1 + (1-a_1)(*a_2)*c_2 + (1-a_1)*(1-a_2)*c_3

Now, we want to find some values c_k, a_k so that the formula above equates

(a_k)*c_k + (1-a_k)*c_3

We can solve this in two steps:

(1-a_k) = (1-a_1)*(1-a_2)
->
a_k = 1-(1-a_1)*(1-a_2)

and

(a_k)*c_k = (a_1)*c_1 + (1-a_1)(*a_2)*c_2
->
c_k = [(a_1)*c_1 + (1-a_1)(*a_2)*c_2] / a_k

Use those formulas (with a different range for your alpha values) and you get your desired color.

(Don't forget to catch a_k = 0)

edit: Explanation of the third pixel:

When you use your two pixels in any way, that is doing something that results it in being used to display something, they will be put over some other existing color that is opaque. For example, this might be the background color, but it could also be some color that is the result of applying many more transparent pixels on some background color.

What I now do to combine your two colors is to find a color that behaves just like those two colors. That is, putting it on top of some opaque color should result in the same as putting the original two colors on top of it. This is what I demand of the new color, resulting in the formula I use.

The formula is nothing than the result of applying two colors in succession on the third one.

Upvotes: 1

Related Questions