Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
663 views
in Technique[技术] by (71.8m points)

image - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...