Jaume
Jaume

Reputation: 3780

C# bitmap from 2D Array

I am trying to build a bitmap from a 2D array, with a low size just for testing. Even if I set the matrix properly (one pix black, one white), when I build the bitmap it seems like some interpolation filter is applied. How I can get just pixel values?

bool switchColor = false;
int width = 30;
int height = 15;
int stride = width * 4;
int[,] integers = new int[width, height];

int thres = 0;
for (int i = 0; i < width; ++i)
{
    for (int j = 0; j < height; ++j)
    {                        
        if (switchColor) 
        {
            switchColor = false;
            thres = 0;
        }
        else 
        { 
            switchColor = true;
            thres = 255;
        }
        byte[] bgra = new byte[] { (byte)(thres), (byte)(thres), (byte)(thres), 255 };
        integers[i, j] = BitConverter.ToInt32(bgra, 0);
    }
}

// Copy into bitmap
Bitmap bitmap;
unsafe
{
    fixed (int* intPtr = &integers[0, 0])
    {
        bitmap = new Bitmap(width, height, stride, PixelFormat.Format32bppRgb, new IntPtr(intPtr)); 
        pictureBox1.Image = bitmap;                                         
    }
}

I've got:

enter image description here

Upvotes: 0

Views: 1501

Answers (2)

Nyerguds
Nyerguds

Reputation: 5629

There are quite some problems with what you're trying to do in general...

  • Images don't use 2D arrays; they use 1D arrays, and, to make an image, so should you. If you do have a 2D array, transform it to a row-by-row 1D byte array so it can be used as source for building an image using LockBits.
  • Raw pointers are a scary thing. Given the fact they are unmanaged, your image might mess up whenever the garbage collector decides you probably don't need that byte array anymore. Use LockBits and Marshal.Copy to fill the image properly.
  • When looping over the contents of an image, you normally work row by row, since that's how image data is saved in its 1D array. Your outer loop is the width, and the inner is the height, so since the inner loop is the one going over the direct data sequentially, you are looping column by column instead. Simply naming your actual loop variables x and y can already clear up a lot of confusion there.
  • There's no need to set the 4th byte to 255 if you're interpreting the data as RGB anyway. It's just padding, not alpha.
  • In fact, you don't need the whole array operation. You can just use existing Color.White and Color.Black, and use their ToArgb() function to get their Int32 value. And since you're only using two values here, it'd be simpler to just store them in two Int32 variables in advance.

All that aside, your real issue is just the smooth zooming set on the picture box. You can get around this problem by making a subclass of PictureBox which overrides the OnPaint function, in which you set the graphics object to use InterpolationMode.NearestNeighbor:

public class PixelBox : PictureBox
{
    protected override void OnPaint (PaintEventArgs pe)
    {
        pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
        // Either the docs on this are wrong, or it behaves differently in NearestNeighbor.
        // putting it to Half makes it NOT shift the whole thing up and to the left by half a (zoomed) pixel.
        pe.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
        base.OnPaint(pe);
    }
}

And, as Ehz said, you might simply want to not zoom. To see what you actually ended up with, save the image from the code instead of using print screen on a control that stretches it.

Upvotes: 1

Ehz
Ehz

Reputation: 2067

I think this is just an expected behavior when a picture box is scaled up in stretch mode. You are only creating a small 30x15 bitmap, which I am assuming is smaller than the PictureBox in the designer. Try setting:

pictureBox1.SizeMode = PictureBoxSizeMode.Normal;

and then to get a larger view of your test you can just scale up your test image:

int width = 300;
int height = 150;

Upvotes: 1

Related Questions