Reputation: 20120
I'm currently having this;
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));
}
}
}
you can see the pattern I think.
Is there a way to make it dynamic since i might find out that i need more of these?
or is there any alternative?
practical example;
BEFORE, 24000 pixel by 24000 pixel bitmap, 2151 milliseconds
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 pixel by 24000 pixel bitmap, 944 milliseconds
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 is more than 50% faster
Upvotes: 1
Views: 843
Reputation: 3047
Without going into the question of whether this should be done or if there are better ways of doing this, I will attempt to answer the OP's desire to create runtime sizeable structures that can be used for variable length memory block copies. In short, performing high performance byte-level manipulations of data is more suited for C++ than C#, but it appears it is still technically possible.
To accomplish this, we can use marshalling to dynamically create unmanaged memory of variable size to hold our temporary BGRn
data and then use P/Invoke to perform the block memory copy.
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);
}
You can then leave the other code as is except replace:
BGR5 np5 = new BGR5(0, 255, 255);
// ...
*((BGR5*)(row + h + Offset1)) = np5;
with:
BGRn np5 = new BGRn(0, 255, 255, 5);
// ...
np5.CopyTo((IntPtr)(row + h + Offset1));
Of course, I have left out the Marshal.FreeHGlobal
that should go along with this class, but that is up to the user to decide how and when to free the memory (probably by implementing IDisposable
and a using
statement).
I have not actually tested how this code performs or if it even works, but it does compile.
Upvotes: 1
Reputation: 751
Using an absract base class can ease the work with bitmaps of any width, albeit you will still need to write a separate class for each bitmap width. You don't need structs- you can write direct to bitmap's memory.
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
}
}
}
Or, use a delegate to encapsulate an appropriate method and call it in the loop
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;
}
}
Upvotes: 0