用固定数组动态制作这个不安全的结构的方法? (或另类?)

时间:2013-08-14 01:41:44

标签: c# pointers unsafe

我现在有这个;

    private const int PixelSizeBGR = 3;

    [StructLayout(LayoutKind.Explicit)]
    private unsafe struct BGR5
    {
        [FieldOffset(0)]
        private fixed byte bgr[PixelSizeBGR * 5];

        public BGR5(byte b, byte g, byte r)
        {
            fixed (byte* v = bgr) 
            {
                int num = 0;
                do
                {
                    v[num++] = b; v[num++] = g; v[num++] = r;
                } while (num < (PixelSizeBGR * 5));
            }
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    private unsafe struct BGR3
    {
        [FieldOffset(0)]
        private fixed byte bgr[PixelSizeBGR * 3];

        public BGR3(byte b, byte g, byte r)
        {
            fixed (byte* v = bgr)
            {
                int num = 0;
                do
                {
                    v[num++] = b; v[num++] = g; v[num++] = r;
                } while (num < (PixelSizeBGR * 3));
            }
        }
    }

你可以看到我认为的模式。

有没有办法让它变得动态,因为我可能会发现我需要更多这些?

还是有其他选择吗?

实际例子;

之前,24000像素乘24000像素位图,2151毫秒

            byte* row = (byte*)bmd.Scan0;

            /*** stuff ***/

            Offset1 = ((CurrentPos / GridX) * FullRow) + ((CurrentPos % GridX) * FullSquare);
            for (k = PixelSize; k <= w; k += PixelSize)
            {
                Offset1 += PixelSize;
                for (l = Stride; l <= h; l += Stride)
                {
                    row[l + Offset1] = 0; //b
                    row[l + Offset1 + 1] = 255; //g
                    row[l + Offset1 + 2] = 255; //r
                }
            }
            /*** more stuff ***/

AFTER,24000像素乘24000像素位图,944毫秒

            byte* row = (byte*)bmd.Scan0;

            /*** stuff ***/

            np5.Set(0, 255, 255);
            Offset1 = ((CurrentPos / GridX) * FullRow) + ((CurrentPos % GridX) * FullSquare) + PixelSizeBGR;
            h = Stride;
            do
            {
                *((BGR5*)(row + h + Offset1)) = np5;
                h += Stride;
            } while (h < FullRow);

            /*** more stuff ***/

AFTER的速度提高了50%以上

2 个答案:

答案 0 :(得分:1)

如果没有讨论是否应该,或者有更好的方法可以做到这一点,我将尝试回答OP希望创建可用于变量的运行时大小的结构长度内存块副本。简而言之,对数据执行高性能字节级操作比C#更适合C ++,但它似乎在技术上仍然可行。

为了实现这一点,我们可以使用编组来动态创建可变大小的非托管内存来保存我们的临时BGRn数据,然后使用P / Invoke来执行块内存复制。

public unsafe class BGRn
{
    IntPtr bgrPtr;
    int totalSize;
    const int pixelSizeBGR = 3;

    public BGRn(byte b, byte g, byte r, int size)
    {
        totalSize = pixelSizeBGR * size;
        bgrPtr = Marshal.AllocHGlobal(totalSize);

        byte* v = (byte*)bgrPtr;

        int num = 0;

        do
        {
            v[num++] = b;
            v[num++] = g;
            v[num++] = r;
        } while (num < (totalSize));
    } 

    public void CopyTo(IntPtr buffer)
    {
        CopyMemory(buffer, bgrPtr, (uint) totalSize);
    }

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

}

然后您可以保留其他代码,除了替换:

BGR5 np5 = new BGR5(0, 255, 255);
// ...
*((BGR5*)(row + h + Offset1)) = np5;

使用:

BGRn np5 = new BGRn(0, 255, 255, 5);
// ...
np5.CopyTo((IntPtr)(row + h + Offset1));

当然,我遗漏了应该使用此类的Marshal.FreeHGlobal,但这取决于用户决定如何以及何时释放内存(可能是通过实现IDisposableusing声明。

我实际上没有测试过这段代码是如何执行的,或者它是否有效,但它确实可以编译。

答案 1 :(得分:0)

使用absract基类可以简化任何宽度位图的工作,尽管你仍然需要为每个位图宽度编写一个单独的类。你不需要结构 - 你可以直接写入位图的内存。

using System.Drawing.Imaging;

namespace TestProject2
{
public abstract class BGRBase
{
    //init to your bitmap's BitmapData, obtained by calling Bitmap.LockBits
    protected readonly BitmapData data;

    public abstract void SetRow(int rowIndex, byte b, byte g, byte r);
}

public class BGR3 : BGRBase
{
    //use constructor to ensure that the bitmap's width is compatible

    public unsafe override void SetRow(int rowIndex, byte b, byte g, byte r)
    {
        byte* row = (byte*)data.Scan0 + rowIndex * data.Stride;
        row[0] = row[3] = row[6] = b;
        //etc
    }
}

public class BGR5 : BGRBase
{
    public override unsafe void SetRow(int rowIndex, byte b, byte g, byte r)
    {
        //code as adove
    }
}
}

或者,使用委托来封装适当的方法并在循环中调用它

public static void Set5(byte* p, byte b, byte g, byte r)

public static void Set3(byte* p, byte b, byte g, byte r)

//usage
public void Draw(Rectangle rect, byte b, byte g, byte r)
{
    Action<byte*, byte, byte, byte> setRow = null;
    switch(rect.Width)
    {
        case 3: setRow = Set3; break;
        case 5: setRow = Set5; break;
        //etc
    }
    byte* p=(byte*)bmd.Scan0 + bmd.Stride * rect.Y + 3 * rect.X;
    while(p < endAddress)
    {
        setRow(p, b, g, r);
        p+=bmd.Stride;  
    }
}