3

I am using the Signature control in OpenNETCF. It works great for most everything I need.

However, I need a way invert the signature and load it back in.

It has a call to get the "bytes" for the signature (GetSignatureEx()). It returns a byte[] of the signature. This signature can then be loaded back in with LoadSignatureEx().

I can't seem to figure out the system for these bytes. I thought they may be coordinates, but it does not seem so now.

If anyone out there knows a way to invert the signature and load it back in, I would be grateful to hear it.


Note for others who may care:

These bytes seem to have the following structure (in order):

2 bytes to show Width  
2 bytes to show Height  
  -- This next part repeats till the end of the array  
  2 bytes to show How many points are in the next line  
    -- This next part repeats as many times as the previous line indicated  
    1 byte for the x coordinate of the point  
    1 byte for the y coordinate of the point  
    2 bytes for the width of the pen (I am not 100% sure on this one)  

I will post my final code once I have it done.


Later Note: Ok after tons of work, I found how easy it is to flip the view using the built in stuff (thanks MusiGenesis). That seems to be way less error prone a process to me.

Just in case someone else wants it, here is my unfinished code. (I was close but the stuff to advance to the next "line" does not work quite right.) (EDIT: I decided that I liked the way this worked a bit more. I have updated the code below. It will work as long as the width or height of the Signature control is not greater than 256. (See ctacke's answer below).)

But first, big thanks to MusiGenesis who helped me figure all this out. You are very helpful and I appreciate your efforts a lot!

Now the code:

private void InvertSignature(ref byte[] original)
{
    int currentIndex = 0;
    short width = BitConverter.ToInt16(original, 0);
    short height = BitConverter.ToInt16(original, 2);
    while (currentIndex < original.Length - 4)
    {
        // Move past the last iteration (or the width and hight for the first time through).
        currentIndex += 4;
        // Find the length of the next segment.
        short nextGroup = BitConverter.ToInt16(original, currentIndex);
        //Advance one so we get past the 2 byte group
        currentIndex += 2;
        // Find the actual index of the last set of coordinates for this segment.
        int nextNumberOfItems = ((nextGroup) * 4) + currentIndex;
        // Invert the coordinates
        for (int i = currentIndex; i < (nextNumberOfItems - 1); i += 4)
        {
            currentIndex = i;

            //Invert Horizontal
            int newHorzPoint = width - original[i] - 1;
            if (newHorzPoint <= 0)
                newHorzPoint = 0;
            else if (newHorzPoint >= width - 1)
                newHorzPoint = width - 1;
            original[i] = (byte)newHorzPoint;

            // Invert Vertical
            int newVertPoint = height - original[i + 1] - 1;
            if (newVertPoint <= 0)
                newVertPoint = 0;
            else if (newVertPoint >= height - 1)
                newVertPoint = height - 1;
            original[i + 1] = (byte)newVertPoint;
        }
    }
}
Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • At a guess, a signature can be represented by a UTF8 string. Perhaps that's what it is. – Oded Apr 17 '10 at 05:42
  • This just occurred to me: why the heck do you need to invert a signature? – MusiGenesis Apr 17 '10 at 22:01
  • Good question. I am doing a hand held app that involves delivery guys picking up deliveries. I am showing a screen with a count of what was picked-up and then I want the client that is giving the pickups to sign for how much stuff they gave to the delivery guy. It is easier if I can flip the whole screen upside down and let them sign and then flip it back. Otherwise the delivery guy has to hand our $1800 symbol devices to the client. (For convenience, time and safety of the device this is something we would like to avoid if we can.) – Vaccano Apr 17 '10 at 22:10
  • Gah, it's even worse than I thought! Nice detective work, getting that far at least. – MusiGenesis Apr 18 '10 at 12:57
  • Un-accepted so I can "tip" the answerer. See: http://meta.stackexchange.com/questions/36567/how-come-no-option-to-tip-answerers. Do not bother answering this question as it will go to MusiGenesis. @MusiGenesis – Vaccano Apr 19 '10 at 16:33

2 Answers2

3

I might be a little late, but I'm looking at the code right now and here are some points worth noting.

  • The first 2 bytes are the width.
  • The next 2 bytes are the Height.
  • The remainder of the data are X,Y coordinates, however the storage format is deceptive and it can change. If the dimension (x or y) is < 256, we use one byte to store the value. If it's greater, we use 2 bytes. This means you might see vales stored as XYXY, XXYXXY or XYYXYY.
ctacke
  • 66,480
  • 18
  • 94
  • 155
  • 1
    Yeah, where were you? I wasted a couple of hours on this thing. :) – MusiGenesis Apr 19 '10 at 15:36
  • Thanks for the info ctacke. I put my 180 degree rotate code above. I think as long as the size of the control does not exceed 256 in either direction it should work fine. As I don't plan to exceed that limit, I am not going to worry about that part. – Vaccano Apr 19 '10 at 18:20
  • Never say never! I am upgrading to a VGA device and have exceeded 256 in both dimensions. This post will save me a lot of time scratching my head! Thanks! – Vaccano Oct 04 '10 at 20:53
2

Completely-untested-code Golf:

public void InvertSignature(ref byte[] original, 
    bool invertHorizontal, bool invertVertical)
{
    for (int i = 0; i < original.Length; i += 2)
    {
        if ((original[i] != 0) && (original[i + 1] != 0))
        {
            if (invertHorizontal)
            {
                original[i] = 232 - original[i] - 1;
            }
            if (invertVertical)
            {
                original[i + 1] = 64 - original[i + 1] - 1;
            }
        }
    }
}

Or try this version, on the assumption that the first 4 bytes are used to store the width and height of the signature (2 byte short ints for each):

public void InvertSignature(ref byte[] original, 
    bool invertHorizontal, bool invertVertical)
{
    byte w = (byte)BitConverter.ToInt16(original, 0) - 1;
    byte h = (byte)BitConverter.ToInt16(original, 2) - 1;
    // TO DO: blow up if w or h are > 255
    for (int i = 4; i < original.Length; i += 2)
    {
        if ((original[i] != 0) && (original[i + 1] != 0))
        {
            if (invertHorizontal)
            {
                original[i] = w - original[i];
            }
            if (invertVertical)
            {
                original[i + 1] = h - original[i + 1];
            }
        }
    }
}

See: Converting OpenNetCF GetSignatureEx to Bitmap on Desktop

Update: Given your description of why you need to invert the signature, it might be easier for you to just invert the ScreenOrientation of your device by 180 degrees (and then back after the customer signs). This way you could also have labels that tell the customer what they're signing - otherwise they're going to be looking at a bunch of upside-down stuff (other than the signature control itself).

To do this, add a reference to Microsoft.WindowsCE.Forms to your project, then add using Microsoft.WindowsCE.Forms; to the top of your file.

To invert the screen by 180 degrees:

SystemSettings.ScreenOrientation = ScreenOrientation.Angle180;

To set back to normal:

SystemSettings.ScreenOrientation = ScreenOrientation.Angle0;

If you're running this in the emulator, your screen will still appear normally upright, but the skin gets flipped upside-down.

Update: one last shot at this, based on ctacke's answer (this should work for signatures with any dimensions):

public void InvertSignature(ref byte[] original, 
    bool invertHorizontal, bool invertVertical)
{
    short w = BitConverter.ToInt16(original, 0);
    short h = BitConverter.ToInt16(original, 2);
    int i = 4;
    while (i < original.Length)
    {
        if (invertHorizontal)
        {
            if (w < 256)
            {
                if (original[i] != 0)
                {
                    original[i] = (byte)w - original[i] - 1;
                }
                i++;
            }
            else
            {
                short val = BitConverter.ToInt16(original, i);
                if (val != 0)
                {
                    val = w - val - 1;
                    byte[] valbytes = BitConverter.GetBytes(val);
                    Buffer.BlockCopy(valbytes, 0, original, i, 2);
                }
                i += 2;
            }
        }
        else
        {
            i += (w < 256) ? 1 : 2;
        }
        if (invertVertical)
        {
            if (h < 256)
            {
                if (original[i] != 0)
                {
                    original[i] = (byte)h - original[i] - 1;
                }
                i++;
            }
            else
            {
                short val = BitConverter.ToInt16(original, i);
                if (val != 0)
                {
                    val = h - val - 1;
                    byte[] valbytes = BitConverter.GetBytes(val);
                    Buffer.BlockCopy(valbytes, 0, original, i, 2);
                }
                i += 2;
            }
        }
        else
        {
            i += (h < 256) ? 1 : 2;
        }
    }
}
Community
  • 1
  • 1
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • OK, this is really impressive and is very close. It inverted the image but it moved part of it off the top of the signature pad and added some vertical lines. (When I did a vertical invert). Can you explain how your code works? You clearly know something about what the bytes mean. Like why can the current one not = 0 or the next one? Why 232 for horizontal and 64 for vertical? I will keep tinkering, with the code, but any links or explanation would be awesome! – Vaccano Apr 17 '10 at 16:43
  • After a bit more digging, I found that the 64 controls the vertical offset of the flipped image and the vertical lines are shown when the flipped image goes off the screen. So I think I just need to dial it in till I find the right sweet spot. (Unless there is some calculation I can do based on the height of the signature control?) – Vaccano Apr 17 '10 at 16:52
  • I am guessing the odd and even bytes are for horizontal and vertical coordinates? (Just a rough guess). I still don't know what the zero values are all about though. – Vaccano Apr 17 '10 at 16:54
  • @Vaccano: my code was based on an answer to a question on Expert Sex Change (scroll down to the bottom of the link to see the actual answers, paying no attention to the requests for money): http://www.experts-exchange.com/OS/Microsoft_Operating_Systems/Windows/Windows_Mobile-_PocketPC-_WinCE/Q_22904026.html – MusiGenesis Apr 17 '10 at 21:09
  • The byte array from GetSignatureEx is formatted (I think) so that each pair of bytes represents the x and y coordinates of a series of points that make up a line. Using bytes for the coordinates restricts you to a 255 x 255 area, and it looks like the OpenNetCF guys chose 232 x 64 as the area, because that's a good size for a signature box on a screen 240 pixels wide. If doing a horizontal invert works fine then I guessed right about the 232 width. If I'm wrong about the height of 64 then you'd probably get the results you describe - the vertical lines are almost certainly because the ... – MusiGenesis Apr 17 '10 at 21:18
  • ... improperly inverted y values are wrapping around in a weird way. I'd recomment experimenting with larger values than 64 until you get one that works correctly. I have found nearly no documentation on this at all, which I suspect is because this byte format was never intended to be manipulated externally, and is instead just meant as a space-saving way of saving and recreating a signature. – MusiGenesis Apr 17 '10 at 21:21
  • Chris Tacke of OpenNetCF is a regular here on StackOverflow, and he will surely steer you right once he sees this. Personally, I would recomment just saving a signature as a `Bitmap`, even on a Windows Mobile device. 232 x 64 is only about 60K (and highly compressible) as a 32bpp bitmap, and you can use more compact formats for this as well. – MusiGenesis Apr 17 '10 at 21:28
  • A few years ago I wrote a signature control that worked virtually the same (only mine stored each coordinate as a pair of `short` integers instead of a pair of bytes). Our company lawyer recommended (not based on any hard knowledge of anything) that we instead store the signatures as standard bitmaps, because their legitimacy as a form of electronic signature would be harder to call into question in a trial situation. – MusiGenesis Apr 17 '10 at 21:32
  • One last point about the check for 0 in both coordinates: the code in the ExpertSexChange answer only draws a line if all 4 of the values involved (x1, y1, x2 and y2) are non-zero. I took this to mean that a coordinate value of (0, 0) is used to indicate the end of one continuous line segment and the start of another (since most signatures are made up of more than one curvy line). Therefore the inverting code should just ignore these coordinate pairs. – MusiGenesis Apr 17 '10 at 21:36
  • Also, what is the actual height of the signature control when you put it on a form? I'm guessing the width is 232 (maybe off by 2 pixels because of a border?), and whatever the height of the control is should tell you what value to use in the invert method. – MusiGenesis Apr 17 '10 at 21:39
  • It occurred to me that the height and width of the signature control might be adjustable in the designer (by you), so you may know what height and width to use in the invert method. By the way, I think the format may actually use the first 8 bytes to store the height and width of the control (as two 4-byte integers) and then stores the data as 1 byte each for x and y if the height and width are both less than 255, or 4 bytes each for x and y otherwise. If the first 4 2-byte coordinate pairs appear to give you weird, glitchy points, this is probably why. – MusiGenesis Apr 17 '10 at 21:52
  • Un-accepted so I can "tip" the answerer. See: http://meta.stackexchange.com/questions/36567/how-come-no-option-to-tip-answerers – Vaccano Apr 19 '10 at 16:32