AwYiss
AwYiss

Reputation: 49

C# draw bitmap from integer

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

Answers (2)

Jcl
Jcl

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.

Update

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

serhiyb
serhiyb

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

Related Questions