LuizZak/FastBitmap

fast.CopyRegion() doesn't maintain image transparency

Closed this issue · 4 comments

When using fast.CopyRegion() to draw a bitmap onto another bitmap, the image is drawn as if the background of the image being drawn onto another bitmap is not transparent.

CopyRegion performs a SourceCopy-style operation across the bitmaps which ignores transparency completely. To perform a blended bitmap copy operation you'd have to copy pixel-by-pixel calculating the final color yourself.

Here's a snippet of code (that copies entire bitmaps instead of regions, but could be adapted to work like CopyRegion) that I use in another project, along with the handy color-blending function it uses:

int width = target.Width;
int height = target.Height;

using (FastBitmap fastTarget = target.FastLock(), fastForeBitmap = foreBitmap.FastLock())
{
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            fastTarget.SetPixel(x, y, FlattenColor(fastTarget.GetPixelUInt(x, y), fastForeBitmap.GetPixelUInt(x, y)));
        }
    }
}

Here's the FlattenColor method the code above references:

/// <summary>
/// Flattens two colors using a GDI+ like color blending mode
/// </summary>
/// <param name="backColor">The back color to blend</param>
/// <param name="foreColor">The fore color to blend</param>
/// <returns>The two colors, blended with a GDI+ like color bleding mode</returns>
[Pure]
public static uint FlattenColor(uint backColor, uint foreColor)
{
    // Based off an answer by an anonymous user on StackOverlow http://stackoverflow.com/questions/1718825/blend-formula-for-gdi/2223241#2223241
    byte foreA = (byte)((foreColor >> 24) & 0xFF);

    if (foreA == 0)
        return backColor;
    if (foreA == 255)
        return foreColor;

    byte foreR = (byte)((foreColor >> 16) & 0xFF);
    byte foreG = (byte)((foreColor >> 8) & 0xFF);
    byte foreB = (byte)((foreColor) & 0xFF);

    byte backA = (byte)((backColor >> 24) & 0xFF);
    byte backR = (byte)((backColor >> 16) & 0xFF);
    byte backG = (byte)((backColor >> 8) & 0xFF);
    byte backB = (byte)((backColor) & 0xFF);

    float backAlphaFloat = backA;
    float foreAlphaFloat = foreA;

    float foreAlphaNormalized = foreAlphaFloat / 255;
    float backColorMultiplier = backAlphaFloat * (1 - foreAlphaNormalized);

    float alpha = backAlphaFloat + foreAlphaFloat - backAlphaFloat * foreAlphaNormalized;

    uint finalA = (uint)Math.Min(255, alpha);
    uint finalR = (uint)(Math.Min(255, (foreR * foreAlphaFloat + backR * backColorMultiplier) / alpha));
    uint finalG = (uint)(Math.Min(255, (foreG * foreAlphaFloat + backG * backColorMultiplier) / alpha));
    uint finalB = (uint)(Math.Min(255, (foreB * foreAlphaFloat + backB * backColorMultiplier) / alpha));

    return finalA << 24 | finalR << 16 | finalG << 8 | finalB;
}

I decided to not include this functionality straight into FastBitmap to not pin the library on a specific color blending function that I otherwise couldn't tell was the best one.

Hope this helps, lemme know if you need anything else!

Hey, good to know it worked! I'll be closing down the issue then.

Cheers!