InkPicture control - How to save both ink and image to an image file

4.2k views Asked by At

I've seen articles demonstrating that the ink can be saved with the background image so that the ink is overlayed onto the image and then saved as a single image file. But, unfortunately the articles I've seen don't go into any detail about how it is done. I can save the background image easy enough with the following statement:

axInkPicture1.Picture.Save(@"\path\to\file.gif");

...but there is no ink overlayed onto the background image

I don't know of any direct method to save the ink, but this is how I am currently doing it:

string tempPath = Path.GetTempPath();
string tempFile = tempPath + @"\file.gif";
// save to a temp file
if (axInkPicture1.Ink.Strokes.Count > 0)
    {
        byte[] bytes = (byte[])axInkPicture1.Ink.Save(InkPersistenceFormat.IPF_GIF, InkPersistenceCompressionMode.IPCM_Default);

        using (MemoryStream ms = new MemoryStream(bytes))
        {
            using (Bitmap gif = new Bitmap(ms))
            {
                gif.Save(tempFile);
            }
        }

    }

This saves the ink, but there is no background image.

How do I save both the ink and image into a single file?

Any help or direction pointing is appreciated...

### EDIT

Here is what else I have tried so far

 InkRectangle inkBox = axInkPicture1.Ink.GetBoundingBox();
        byte[] gifbits = (byte[])axInkPicture1.Ink.Save(InkPersistenceFormat.IPF_GIF, InkPersistenceCompressionMode.IPCM_Default);
        using (System.IO.MemoryStream buffer = new System.IO.MemoryStream(gifbits))
        using (var inkImage = new Bitmap(buffer))
        using (var picture = new Bitmap(axInkPicture1.Picture))
        using (var g = Graphics.FromImage(picture))
        {
            g.DrawImage(inkImage, new Rectangle(inkBox.Left, inkBox.Right, axInkPicture1.Picture.Width, axInkPicture1.Height));
            picture.Save(tempFile, System.Drawing.Imaging.ImageFormat.Gif);
        }

...but I'm still not having any luck. This saves only the background picture and not the ink. Any ideas what I'm doing wrong?

Thanks in advance

2

There are 2 answers

0
jwitt98 On

Soooo.... after much head banging and gnashing of teeth, I finally came up with a solution to my problem, although maybe not the solution I had expected and maybe not the best solution, but it works. I'm new to the world of c# and .net so please feel free to correct me me if I state anything stupid here.

What I found was that the code in my edit above was actually working, I just couldn't see the ink because it was outside the bounds of the rectangle. Apparently rectangles acquired using the GetBoundingBox() method gives you inkSpace coordinates which are vastly different than pixel coordinates.

With that in mind, my next goal became to convert the inkSpace coordinates to pixel coordinates using the inkSpaceToPixels method. I struggled with this for awhile and could never figure out how to do this using the activex version of inkPicure or axInkPicture. It required some handle to hdcDisplay which I couldn't quite wrap my head around.

In the end, I used the .net version (Microsoft.Ink) from Microsoft.Ink.dll. The only reason I was using the activeX version to begin with was because I couldn't get the Windows Tablet PC SDK to install on my development machine for some reason. It kept complaining that I needed the WinXP Tablet PC OS to install it. I figured out that I was able to install it on another (Win7) PC and then just copy the .dlls over from that PC.

So, here is the final version that is now working... whew!

        //set the temporary file paths
        string tempPath = Path.GetTempPath();
        string tempFile = tempPath + @"tmpFile.png";

        //get the bounding box and reference point for the ink strokes (in inkspace coordinates!)
        Rectangle inkBox = inkPicture1.Ink.GetBoundingBox();
        Point point = new Point(inkBox.Left, inkBox.Top);
        MessageBox.Show(point.X.ToString() + ", " + point.Y.ToString());//"before" coordinates

        // Convert inkspace coordinates to pixel coordinates using Renderer
        Graphics tempGraphics = CreateGraphics();
        Microsoft.Ink.Renderer inkRenderer = new Renderer();
        inkRenderer.InkSpaceToPixel(tempGraphics, ref point);;
        MessageBox.Show(point.X.ToString() + ", " + point.Y.ToString());//"after" coordinates
        // Clean up
        tempGraphics.Dispose();

        // save the ink and background image to a temp file
        if (inkPicture1.Ink.Strokes.Count > 0)
        {
            byte[] bytes = inkPicture1.Ink.Save(PersistenceFormat.Gif, CompressionMode.NoCompression);
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                using (Bitmap gif = new Bitmap(ms))
                using (var bmp = new Bitmap(inkPicture1.BackgroundImage))
                using (var g = Graphics.FromImage(bmp))
                {
                    g.DrawImage(gif, new Rectangle(point.X, point.Y, gif.Width, gif.Height));
                    bmp.Save(tempFile, System.Drawing.Imaging.ImageFormat.Png);

                }
            }
        }

Sorry for the long winded explanation, but I felt that simply posting the solution without adequate explanation would leave too many open questions...

1
user3295847 On

I guess I don't have enough 50 reputation to comment, so I will answer with this comment for others that might land here after a google search like I did:

This was very helpful. I translated to VB and it worked.

After some flailing around I found the DLL at:

C:\Program Files (x86)\Common Files\Microsoft Shared\Ink\Microsoft.Ink.dll

Apparently I am not the only one to have a problem locating the DLL as there are many google entries on how to get it -- many are wrong and most are not this simple.

If you are just starting out with signature capture, remember to add a background image to your control or it will fail on this line:

Using bmp = New Bitmap(InkPicture1.BackgroundImage)