Reputation: 49
I have to draw an image from integer values. They are stored in List<int>[]
.
The list has 5081 arrays with 2048 values each. Each value is between 0-1000 and one int is the color for one pixel, so it's grayscale.
I know how to do it with setpixel but this is too slow.
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
bmp.SetPixel(x, y, Color.FromArgb(myColor, myColor, myColor));
}
}
I know that there are similar questions here how to draw bitmaps faster but they already have a bitmap. I have to draw the image from my values.
Upvotes: 0
Views: 667
Reputation: 28272
If you don't want to do it unsafely, you could do this (assumes a 32bppArgb
pixelformat):
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
for (var y = 0; y < channelId.Length; y++)
{
var scanLineSize = channelId[y].Count*4;
var rgb = new byte[scanLineSize];
int idx = 0;
// Convert the whole scanline
foreach (var id in channelId[y])
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
// And copy it in one pass
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, scanLineSize);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
If it's not fast enough for you, you could do the whole conversion of the bitmap in-memory and copy the whole array in one pass. This would not be very memory-efficient though, and this should be "fast enough" for the amount of data you are moving.
Just for fun, in one pass:
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int idx = 0;
var rgb = new byte[channelId[0].Count * 4 * channelId.Length];
foreach (var id in channelId.SelectMany(t => t))
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, rgb.Length);
bmp.UnlockBits(bmpData);
Apart from the memory-efficiency, you'll need to make sure all List<int>
in the array contain the same number of elements and that the Stride
of the bitmap is the same as width*4
. It should be for 2048 elements, but you never know.
Upvotes: 2
Reputation: 4833
BitmapData bitmapData = bmp.LockBits(
new Rectangle(0, 0, channelId[0].Count, channelId.Length),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb
);
unsafe{
ColorARGB* startingPosition = (ColorARGB*) bitmapData.Scan0;
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
ColorARGB* position = startingPosition + j + i * channelId[0].Count;
position->A = 255;
position->R = myColor;
position->G = myColor;
position->B = myColor;
}
}
bmp.UnlockBits(bitmapData);
}
Upvotes: 0