views:

202

answers:

2

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;
        }
    }
}
+2  A: 

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: http://stackoverflow.com/questions/812125/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;
        }
    }
}
MusiGenesis
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
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
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
@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
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
... 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
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
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
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
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
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
Un-accepted so I can "tip" the answerer. See: http://meta.stackoverflow.com/questions/36567/how-come-no-option-to-tip-answerers
Vaccano
OK, I'll stop. :)
MusiGenesis
+3  A: 

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
Yeah, where were you? I wasted a couple of hours on this thing. :)
MusiGenesis
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
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