1
votes

I have what is essentially a 2-dimensional array of colors with each indices representing the pixel and the value being the color of that pixel. For simplicity, let's imagine a 3x3 pixel array that basically has 1-pixel row of red, a 1-pixel row of green and a 1-pixel row of blue:

Color[,] colorArray = new Color[3,3];

for (int i = 0; i < 3; ++i) 
{
    colorArray[0,i] = Colors.Red;
    colorArray[1,i] = Colors.Green;
    colorArray[2,i] = Colors.Blue;
}

I have a XAML file with an Image (again, for simplicity, let's assume that the image is also 3x3 pixels). How do I go about writing the above pixel data into something that can be showed in that image file?

Edit: I've tried using both WriteableBitmaps and BitmapSources but can't seem to get them working. For example, the following code (where I'm just trying to paint the whole screen yellow) results in a white image (borrowed from here).

BitmapSource Example

uint[] pixelData = new uint[width * height];
for (int y = 0; y < height; ++y)
{
    int yIndex = y * width;
    for (int x = 0; x < width; ++x)
    {
        pixelData[x + yIndex] = (0 << 24) + (255 << 16) + (255 << 8) + 255;
    }
}

var bmp = BitmapSource.Create(width, height, dpi, dpi, PixelFormats.Bgra32, null, pixelData, width * 4);
TestImage.Source = bmp;

WriteableBitmap Example

WriteableBitmap wb = new WriteableBitmap(width, height, dpi, dpi, pf, null);
byte[] pixelData = new byte[height * width * 4];

for (int i = 0; i < (int)wb.Height; i++)
{
    for (int j = 0; j < (int)wb.Width; j++)
    {
        var color = colors[i, j];
        var idx = j * 4 + i * width * 4;

        byte blue = color.B;
        byte green = color.G;
        byte red = color.R;

        pixelData[idx] = blue;
        pixelData[idx + 1] = green;
        pixelData[idx + 2] = red;
        pixelData[idx + 3] = 255;

        //byte[] colorData = { blue, green, red, 255 };
        //Int32Rect rect = new Int32Rect(j, i, 1, 1);
        //wb.WritePixels(rect, colorData, 4, 0);
    }
}

Int32Rect rect = new Int32Rect(0, 0, width, height);
wb.WritePixels(rect, pixelData, 4, 0);
TestImage.Source = wb;
3

3 Answers

4
votes

The following code works with a bitmap source and 32-bit colors:

var height = colorArray.GetUpperBound(0) + 1;
var width = colorArray.GetUpperBound(1) + 1;
var pixelFormat = PixelFormats.Bgra32;
var stride = width * 4; // bytes per row

byte[] pixelData = new byte[height * stride];

for (int y = 0; y < height; ++y)
{
    for (int x = 0; x < width; ++x)
    {
        var color = colorArray[y, x];
        var index = (y * stride) + (x * 4);

        pixelData[index] = color.B;
        pixelData[index + 1] = color.G;
        pixelData[index + 2] = color.R;
        pixelData[index + 3] = color.A; // color.A;
    }
}

var bitmap = BitmapSource.Create(width, height, 96, 96, pixelFormat, null, pixelData, stride);
Image.Source = bitmap;
1
votes

This blog describes how to use BitmapSource to create an Image from a byte array with pixels.

Here's the code taken from that blog:

double dpi = 96;
int width = 128;
int height = 128;
byte[] pixelData = new byte[width*height];

for (int y = 0; y < height; ++y)
{
    int yIndex = y * width;
    for (int x = 0; x < width; ++x)
    {
        pixelData[x + yIndex] = (byte) (x + y);
    }
}

BitmapSource bmpSource = BitmapSource.Create(width, height, dpi, dpi,PixelFormats.Gray8, null, pixelData, width);
1
votes

Use WriteableBitmap class. WritePixels method is exactly what you need. (msdn)

Your example:

WriteableBitmap wb = new WriteableBitmap(3, 3, 96, 96, PixelFormats.Bgra32, null);

for (int i = 0; i < (int) wb.Height; i++)
{
    byte blue  = (byte) (i == 2 ? 255 : 0),
         green = (byte) (i == 1 ? 255 : 0),
         red   = (byte) (i == 0 ? 255 : 0);

    byte[] colorData = {blue, green, red, 255};

    for (int j = 0; j < (int) wb.Width; j++)
    {
      Int32Rect rect = new Int32Rect(j, i, 1, 1);
      wb.WritePixels(rect, colorData, 4, 0);
    }
 }

 img.Source = wb;

But if you want to draw a big picture you shouldn't write one pixel everytime. It may be very slow. Instead you should fill byte array and copy it to your instance of WritableBitmap by portions.