使用安全代码将通道从一个图像复制到另一个图像

时间:2012-06-07 15:09:21

标签: c# gdi+

我正在尝试重构这个不安全的代码,根据MSDN上的这个示例,使用System.Runtime.InteropServices.Marshal.Copy将单个ARGB频道从一个图像复制到另一个图像,但我完全迷失了。

有没有人可以告诉我如何解决这个问题?

public enum ChannelARGB
{
    Blue = 0,
    Green = 1,
    Red = 2,
    Alpha = 3
}

public static void transferOneARGBChannelFromOneBitmapToAnother(
    Bitmap source,
    Bitmap dest,
    ChannelARGB sourceChannel,
    ChannelARGB destChannel )
{
    if ( source.Size!=dest.Size )
        throw new ArgumentException();
    Rectangle r = new Rectangle( Point.Empty, source.Size );
    BitmapData bdSrc = source.LockBits( r, 
                                        ImageLockMode.ReadOnly, 
                                        PixelFormat.Format32bppArgb );
    BitmapData bdDst = dest.LockBits( r, 
                                      ImageLockMode.ReadWrite,
                                      PixelFormat.Format32bppArgb );
    unsafe
    {
        byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer();
        byte* bpDst = (byte*)bdDst.Scan0.ToPointer();
        bpSrc += (int)sourceChannel;
        bpDst += (int)destChannel;
        for ( int i = r.Height * r.Width; i > 0; i-- )
        {
            *bpDst = *bpSrc;
            bpSrc += 4;
            bpDst += 4;
        }
    }
    source.UnlockBits( bdSrc );
    dest.UnlockBits( bdDst );
}

修改

为了尝试通过@Ben Voigt,我到目前为止已经提出了这个问题。不幸的是,我现在收到以下错误:

  

尝试读取或写入受保护的内存。这通常是一个   表明其他内存已损坏。

    private static void TransferOneArgbChannelFromOneBitmapToAnother(
                                         Bitmap source,
                                         Bitmap destination, 
                                         ChannelARGB sourceChannel, 
                                         ChannelARGB destinationChannel)
    {
        if (source.Size != destination.Size)
        {
            throw new ArgumentException();
        }

        Rectangle rectangle = new Rectangle(Point.Empty, source.Size);

        // Lockbits the source.
        BitmapData bitmapDataSource = source.LockBits(rectangle,
                                                  ImageLockMode.ReadWrite,
                                                  PixelFormat.Format32bppArgb);

        // Declare an array to hold the bytes of the bitmap.
        int bytes = bitmapDataSource.Stride * bitmapDataSource.Height;

        // Allocate a buffer for the source image
        byte[] sourceRgbValues = new byte[bytes];

        // Get the address of the first line.
        IntPtr ptrSource = bitmapDataSource.Scan0;

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptrSource, 
                                                    sourceRgbValues, 
                                                    0, 
                                                    bytes);

        // Unlockbits the source.
        source.UnlockBits(bitmapDataSource);

        // Lockbits the destination.
        BitmapData bitmapDataDestination = destination.LockBits(rectangle,
                                                  ImageLockMode.ReadWrite,
                                                  PixelFormat.Format32bppArgb);

        // Allocate a buffer for image
        byte[] destinationRgbValues = new byte[bytes];

        IntPtr ptrDestination = bitmapDataDestination.Scan0;

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptrDestination,
                                                    destinationRgbValues, 
                                                    0, 
                                                    bytes);

        ptrSource += (int)sourceChannel;
        ptrDestination += (int)destinationChannel;

        for (int i = rectangle.Height * rectangle.Width; i > 0; i--)
        {
            destinationRgbValues[i] = sourceRgbValues[i];
            ptrSource += 4;
            ptrDestination += 4;
        }

        // Copy the RGB values back to the bitmap
        // ******This is where I am getting the exception*******.
        System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues, 
                                                    0, 
                                                    ptrDestination, 
                                                    bytes);

        // Unlock bits the destination.
        destination.UnlockBits(bitmapDataDestination);
    }

谁能看到我做错了什么?老实说,这有点过头了。我想我应该买些书。

3 个答案:

答案 0 :(得分:3)

  1. LockBits来源。
  2. Marshal.Copy来源BitmapDatabyte[]缓冲区。
  3. UnlockBits来源。
  4. LockBits目的地。
  5. Marshal.Copy目的地BitmapDatabyte[]缓冲区。
  6. 循环并将该频道从源byte[]复制到目标byte[](注意,对索引而不是指针使用算术)
  7. Marshal.Copy目的地byte[]返回BitmapData
  8. UnlockBits目的地。
  9. 但是,我不确定这是什么意思。使用Marshal.Copy的代码与使用unsafe关键字的代码一样危险,并且应该要求类似的代码安全权限。

    一种可能更有效的方法是使用ImageAttributes.SetColorMatrix从目标图像中删除所需的频道,从源图像中删除所有其他频道,然后混合。请参阅ColorMatrix

    的示例

    或者使用DirectX(或OpenGL)和只传输一个通道的着色器。

答案 1 :(得分:0)

您可以使用NugetCodeplex中基于LINQ的简单图像处理框架,编写一个简单的查询来交换频道。

您也可以使用ColorMatrix执行频道互换,例如this代码。

答案 2 :(得分:0)

不幸的是,如果要组合来自两个单独图像的通道,ColorMatrix将无法工作。您需要一种加法(或按位或)混合方法,GDI +提供的唯一混合是Over和Copy。它也让我觉得任何允许你直接访问位的方法,包括LockBits都被锁定。

我认为唯一的选择是在每个像素上使用GetPixel和SetPixel,如下所示:

Color dstColor = bpDst.GetPixel(x, y);
Color srcColor = bpSrc.GetPixel(x, y);
int srcValue = (srcColor.ToArgb() >> (sourceChannel * 8)) & 0xff;
int dstArgb = (dstColor.ToArgb() & ~(0xff << (destChannel * 8))) | (srcValue << (destChannel * 8));
bpDst.SetPixel(x, y, Color.FromArgb(dstArgb));