1

我现在有这个;

    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 x 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 ***/

之后,24000 x 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% 以上

4

2 回答 2

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声明)。

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

于 2013-08-16T04:24:37.423 回答
0

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

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;  
    }
}
于 2013-08-14T14:53:15.367 回答