Search code examples
c#imagebitmapimagepixelsense

Capture image from touch of PixelSense-compatible PC and save it as .bmp


I'm using Microsoft Surface 2.0 SDK with SUR40 PixelSense compatible computer. I need to capture image from it's touch and save it as .bmp. Since Surface SDK comes with RawImageVisualizer example, which displays picture from touch on the screen, I've tried to modify program for writing picture to HDD. The problem is, I get ArgumentException: Parameter is invalid during building Image from byte array captured from touch.

This is how I retrieve byte array with image data from FrameReceivedEventArgs on FrameReceived event:

event.UpdateRawImage(
                ImageType.Normalized,
                normalizedImage,
                0, 0,
                InteractiveSurface.PrimarySurfaceDevice.WorkingAreaWidth,
                InteractiveSurface.PrimarySurfaceDevice.WorkingAreaHeight);

And that's how I try to write bytes as .bmp to disk:

System.Drawing.Image img;
using (System.Drawing.Image raw = System.Drawing.Image.FromStream(new MemoryStream(normalizedImage)))
{
    img = raw.Clone() as System.Drawing.Bitmap;
}
img.Save("C:/img.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

So I get the exception trying to create Image from stream. Nevertheless this byte array works totally fine with Texture2D and SpriteBatch which displays it. How can I fix ArgumentException?


Solution

  • i've just realized, that UpdateRawImage does not return a byte representation of PNG file, but only an array of pixels. So, to build an image from it, one have to write all other parts of file structure to the array: header and color table (if needed). In many cases this can be simply done with one of System.Drawing.Bitmap constructors:

    public Bitmap(
        int width,
        int height,
        int stride,
        PixelFormat format,
        IntPtr scan0
    )
    

    But I was not so lucky, because UpdateRawImage returns 8bpp grayscale pixels, and PixelFormat enum doesn't support them (the most close is Format16bppGrayScale, but it uses 2 bytes for pixel, not one). So, in this particular situation, there are two obvious solutions. The first is making a new array of pixels, which meets one of PixelFormat standards (that was my choice, because I need 24-bit RGB image, despite it's actually black-white with only 256 shades). The second is writing BMP headers manually (and it's not very difficult due to open specs).