3

我正在尝试重构这个不安全的代码,以按照MSDNSystem.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 walk 工作。不幸的是,我现在收到以下错误:

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

    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);
    }

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

4

3 回答 3

3
  1. LockBits来源。
  2. Marshal.Copy缓冲区BitmapData的源。byte[]
  3. UnlockBits来源。
  4. LockBits目的地。
  5. Marshal.Copy缓冲区BitmapData的目的地。byte[]
  6. 循环并将该通道从源复制byte[]到目标byte[](注意,对索引使用算术而不是指针)
  7. Marshal.Copybyte[]回到目的地BitmapData
  8. UnlockBits目的地。

不过,我不确定重点是什么。使用的代码与使用关键字Marshal.Copy的代码一样危险,并且应该需要类似的代码安全权限。unsafe

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

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

于 2012-06-07T15:15:28.927 回答
0

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

您还可以使用 ColorMatrix 来执行代码中的通道交换。

于 2012-06-07T15:34:20.163 回答
0

Unfortunately, a ColorMatrix won't work if you want to combine channels from two separate images. You would need an additive (or bitwise or) blending method, and the only blending provided by GDI+ is Over and Copy. It also looks to me like any methods that would allow you to access the bits directly, including LockBits, are locked down.

I think the only option is to use GetPixel and SetPixel on each pixel, something like this:

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));
于 2012-06-08T00:03:00.947 回答