0

I have been trying to get a simple byte array in to a Picture box using c#. Most examples I tried give me parameter invalid. If I don't use a using or converter and break it out like so (see code below) I do get an image but it looks wrong (all black with some random colored dots up top).

 Bitmap bmp = new Bitmap(48, 32, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bmp.LockBits(
                                 new Rectangle(0, 0, bmp.Width, bmp.Height),
                                 ImageLockMode.WriteOnly, bmp.PixelFormat);
            Marshal.Copy(blob, 0, bmpData.Scan0, blob.Length);
            bmp.UnlockBits(bmpData);
            return bmp;

The byte array looks like this.

01 c7 f0 70 3f 03 00 c3 f0 60 1e 01 80 63 ff c3 1c 71 88 21 ff c7 3c f3 8e 31 f0 c7 fc ff 86 31 f0 c7 fc ff c4 31 f0 c1 fc 3f c0 31 e0 e0 7e 0f c0 f1 e1 f8 3f 83 c0 31 e1 fe 1f e1 c6 31 e0 ff 9f f9 86 30 f0 fb 9c 79 8f 38 f0 e3 98 79 8f 38 f8 63 98 79 80 30 38 62 1c 61 80 70 10 70 3e 03 ff ff ff ff ff ff ff ff ff ff ff ff 00 7e 00 78 7f f0 88 3c 18 18 3f f0 8e 3c 7e 0f 0f c3 86 30 ff 07 87 87 c4 31 ff 87 e0 8f c0 71 ff e7 f8 0f c0 71 ff e7 f0 0f c0 71 ff e7 f0 0f c6 71 ff e7 f0 1f 86 10 ff 87 e1 1f 8f 18 ff 8f 87 87 8f 18 7f 0f 0f c3 80 1c 0c 18 3f f0 80 7e 00 38 7f f0

My image should be 192 bytes and a 48 x 32 res image. What am I doing wrong?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
sean green
  • 53
  • 6
  • 48 x 32 = 1536 pixels. 192 bytes x 8 bits/byte = 1536 bits. You have just 1 bit per pixel, so this is a monochrome bitmap, and Format24bppRgb (24 bits per pixel) is wrong. – Michael Liu May 17 '18 at 03:51
  • Calculate the space one pixel can take, from the number of bytes and the number of pixels. Then find the correct pixel format from the doc. – mghie May 17 '18 at 03:51
  • Can you post what the image should look like? – TaW May 17 '18 at 09:00
  • Michael I only have Format16bppGrayScale and that gives me a red box with a square. I dont see a monochrome. – sean green May 17 '18 at 22:40
  • Here is the image in characters using a byte to text function.https://pastebin.com/7qKgFksK – sean green May 17 '18 at 22:43
  • Ok I see the issue now, I had to set to Format1bppIndexed but the images was offset. So I tried playing with the data and now I see the issue but do not understand why its doing this. I can set 0-125 and these bytes show as they should anything after byte 125 is not shown and it overwrites 125. So if I do 0-125 all 0xff I get as solid bar up top. but adding 126 as 0x00 it replaces 125 with 0x00. Hope that makes sense to someone because it makes no sense to me – sean green May 17 '18 at 23:58
  • Ah, stride problems. Yeah, that took a while for me to wrap my head around too. – Nyerguds May 23 '18 at 12:58
  • @seangreen If my answer helped you, [please mark it as accepted answer](https://stackoverflow.com/help/someone-answers). – Nyerguds May 28 '18 at 11:08

1 Answers1

1

As Michael Liu commented, if your data is 192 bytes, and you need to get 32 lines of data out of that, then each line will be 6 bytes, meaning that to get 48 pixels out of such a line, you need to multiply it by 8, or, in other words, use 1 bit per pixel, which is PixelFormat.Format1bppIndexed.

As for your problems in visualizing the data, you need to take the stride into account; the actual amount of bytes per line of pixels.

Images created by the .Net framework always use a multiple of four bytes as width of each line of pixels, so the new 48x32 image you make will have 8 bytes on each line. However, your image data is completely compact data, so it only has 6 bytes per line.

So you need to copy it line by line, keeping the stride of both your input and output into account. This means you should replace the single Marshal.Copy by a loop over the height that does one Marshal.Copy per line, copying the data per 6 bytes from the input array to the correct start pointer of each line on the output.

The adjusted Marshal.Copy operation should be something like this:

Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Int32 targetStride = bmpData.Stride;
Int64 scan0 = bmpData.Scan0.ToInt64();
for (Int32 y = 0; y < height; y++)
    Marshal.Copy(blob, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);

...with stride the stride of your input array (so in this case, 6).

To automate the input stride calculation, if you know you have completely compact data, you can use the formula I already kind of showed in the previous block:

public static Int32 GetMinimumStride(Int32 width, Int32 bitsLength)
{
    return ((bitsLength * width) + 7) / 8;
}

I already posted code here on SO to build an image from bytes in any format, so you can just use that, I guess. Using that BuildImage function, you can build your image like this:

public static Bitmap BuildMonoBitmap(Byte monoBytes, Int32 width, Int32 height)
{
    Color[] pal = new Color[] {Color.Black, Color.White};
    Int32 stride = GetMinimumStride(width, 1);
    return BuildImage(monoBytes, width, height, stride, PixelFormat.Format1bppIndexed, pal, null);
}

The result, shown in my test program:

Bliss Box

Nyerguds
  • 5,360
  • 1
  • 31
  • 63