Guillaume201
Guillaume201

Reputation: 33

C# Copy bitmap's pixels in the alpha channel on another bitmap

I have two bitmaps, one containing a "normal" picture and another one containing a grayscale image.

My goal is simply to place the grayscale image in the alpha channel of first bitmap in order to use the grayscale image like a mask.

I have seen some solutions to do this by using SetPixel but it's pretty slow and creates some weird artifacts on the final image due to errors in the memory allocation.

So maybe a way to do this will be by browsing all the pixels form the original bitmap and assign-it the corresponding pixel from the grayscale bitmap in the alpha channel. Maybe with a LockBits pixel array?

So my question is how to do that? I have especially difficulties to find how to assign a pixel in the alpha channel and how to get the corresponding pixel in the grayscale bitmap.

Upvotes: 1

Views: 3686

Answers (1)

TaW
TaW

Reputation: 54453

This is not using LockBits yet, but it'll be good enough to see if it'll get you want you want:

public Bitmap getGrayOverlay(Bitmap bmpColor, Bitmap bmpGray)
{
    Size s1 = bmpColor.Size;
    Size s2 = bmpGray.Size;
    if (s1 != s2) return null;

    Bitmap bmpResult= new Bitmap(s1.Width, s1.Height);

    for (int y = 0; y < s1.Height; y++)
        for (int x = 0; x < s1.Width; x++)
        {
            Color c1 = bmpColor.GetPixel(x, y);
            Color c2 = bmpGray.GetPixel(x, y);
            bmpResult.SetPixel(x, y, Color.FromArgb((int)(255 * c2.GetBrightness()), c1 ) );
        }
    return bmpResult;
}

Dispose of all three Bitmaps when you're done with them!

Here is the LockBit version, a little longer, but basically the same. And around 10 times faster.. I assumes that the input files are either 24bppRGB or 32bppARGB or else they return null..:

public Bitmap getGrayOverlayLBA(Bitmap bmp1, Bitmap bmp2)
{
    Size s1 = bmp1.Size;
    Size s2 = bmp2.Size;
    if (s1 != s2) return null;

    PixelFormat fmt1 = bmp1.PixelFormat;
    PixelFormat fmt2 = bmp2.PixelFormat;

    PixelFormat fmt = new PixelFormat();
    fmt = PixelFormat.Format32bppArgb;
    Bitmap bmp3 = new Bitmap(s1.Width, s1.Height, fmt);

    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);

    BitmapData bmp1Data = bmp1.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
    BitmapData bmp2Data = bmp2.LockBits(rect, ImageLockMode.ReadOnly, fmt2);
    BitmapData bmp3Data = bmp3.LockBits(rect, ImageLockMode.ReadWrite, fmt);

    byte bpp1 = 4;
    byte bpp2 = 4;
    byte bpp3 = 4;

    if (fmt1 == PixelFormat.Format24bppRgb) bpp1 = 3;
    else if (fmt1 == PixelFormat.Format32bppArgb) bpp1 = 4; else return null;
    if (fmt2 == PixelFormat.Format24bppRgb) bpp2 = 3;
    else if (fmt2 == PixelFormat.Format32bppArgb) bpp2 = 4; else return null;

    int size1 = bmp1Data.Stride * bmp1Data.Height;
    int size2 = bmp2Data.Stride * bmp2Data.Height;
    int size3 = bmp3Data.Stride * bmp3Data.Height;
    byte[] data1 = new byte[size1];
    byte[] data2 = new byte[size2];
    byte[] data3 = new byte[size3];
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
    System.Runtime.InteropServices.Marshal.Copy(bmp2Data.Scan0, data2, 0, size2);
    System.Runtime.InteropServices.Marshal.Copy(bmp3Data.Scan0, data3, 0, size3);

    for (int y = 0; y < s1.Height; y++)
    {
        for (int x = 0; x < s1.Width; x++)
        {
            int index1 = y * bmp1Data.Stride + x * bpp1;
            int index2 = y * bmp2Data.Stride + x * bpp2;
            int index3 = y * bmp3Data.Stride + x * bpp3;
            Color c1, c2;

            if (bpp1 == 4)
                c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2], data1[index1 + 1], data1[index1 + 0]);
            else c1 = Color.FromArgb(255, data1[index1 + 2], data1[index1 + 1], data1[index1 + 0]);
            if (bpp2 == 4)
                c2 = Color.FromArgb(data2[index2 + 3], data2[index2 + 2], data2[index2 + 1], data2[index2 + 0]);
            else c2 = Color.FromArgb(255, data2[index2 + 2], data2[index2 + 1], data2[index2 + 0]);

            byte A = (byte)(255 * c2.GetBrightness());
            data3[index3 + 0] = c1.B;
            data3[index3 + 1] = c1.G;
            data3[index3 + 2] = c1.R;
            data3[index3 + 3] = A;
        }
    }

    System.Runtime.InteropServices.Marshal.Copy(data3, 0, bmp3Data.Scan0, data3.Length);
    bmp1.UnlockBits(bmp1Data);
    bmp2.UnlockBits(bmp2Data);
    bmp3.UnlockBits(bmp3Data);
    return bmp3;
}

Edit: I amended the code to allow for 24bpp images as sources.

Upvotes: 3

Related Questions