2016-01-25 2 views
0

Мне нужно нарисовать изображение из целочисленных значений. Они хранятся в List<int>[]. Список содержит 5081 массивов с 2048 значениями каждый. Каждое значение составляет от 0 до 1000, а один int - цвет для одного пикселя, поэтому он имеет оттенки серого.C# draw bitmap from integer

Я знаю, как это сделать с помощью setpixel, но это слишком медленно.

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)); 
      } 
     } 

Я знаю, что здесь есть похожие вопросы, как рисовать растровые изображения быстрее, но у них уже есть растровое изображение. Мне нужно сделать изображение из моих значений.

+0

@stuartd, хотя связанный вопрос примерно такой же, он специально помечен 'wpf' (и принятый ответ определенно' wpf', поскольку он использует 'ImageSource' и' WriteableBitmap', а не 'Bitmap', как ожидалось по этому вопросу) – Jcl

ответ

2

Если вы не хотите, чтобы сделать это ненадежно, вы могли бы сделать это (предполагает 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); 

Если это не достаточно быстро для вас, вы могли бы сделать все преобразования бит-карты в памяти и скопировать весь массив за один проход. Однако это не будет очень экономичным, и это должно быть «достаточно быстро» для количества данных, которые вы перемещаете.

Update

Просто для удовольствия, в один проход:

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); 

Помимо памяти эффективности, вам необходимо убедиться, что все List<int> в массиве содержат одинаковое число элементов и что Stride растрового изображения совпадает с width*4. Это должно быть для 2048 элементов, но вы никогда не знаете.

+0

Ницца! Это работает достаточно быстро для меня, спасибо! – AwYiss

+0

Я добавил однопроходное преобразование, просто для удовольствия, это действительно можно было бы оптимизировать (не используя альфа и многое другое), но я оставлю это как упражнение! :-) – Jcl

0
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); 
    }