10

I have a page that sends html5 canvas data, encoded as a base64 bmp image (using this algorithm http://devpro.it/code/216.html) to a serverside process that converts it into a System.Drawing.Image object and does some operations on it.

In my local environment, this works just fine, but on my ec2 instance I get the following error:

System.ArgumentException: Parameter is not valid. at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement, Boolean validateImageData) at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement)

My code looks as follows:

System.Drawing.Image image = null;
string b64string = "...";
byte[] sf = Convert.FromBase64String(b64string );
using (MemoryStream s = new MemoryStream(sf, 0, sf.Length))
{
   image = System.Drawing.Image.FromStream(s, false);
}
...

Here's a text file with a sample b64string that I'm using to test: https://docs.google.com/leaf?id=0BzVLGmig1YZ3MTM0ODBiNjItNzk4Yi00MzI5LWI5ZWMtMzU1OThlNWEyMTU5&hl=en_US

I've also tried the following and had the same results:

System.Drawing.ImageConverter converter = new System.Drawing.ImageConverter();
image = converter.ConvertFrom(sf) as System.Drawing.Image;         

Any insight would be greatly appreciated!

ari
  • 295
  • 1
  • 4
  • 15
  • What version of the framework are you running this on, locally and on the ec2 instance? – Oded Sep 23 '11 at 19:31
  • Do you mean that the actual test data (that file) cannot be decoded on the ec2 instance, but can be on your dev machine? E.g. there's no possibility that there is different data being processed in your dev and in your server test. – Jamie Treworgy Sep 23 '11 at 19:39
  • Sorry, forgot to mention .NET 4.0 on both. – ari Sep 23 '11 at 19:40
  • Correct, that test data can be decoded locally, but not on the ec2 instance. – ari Sep 23 '11 at 19:41
  • Have you tried comparing binary files? E.g. write the bytes to a file on the server after base64 decoding, compare to the same file produced locally. Perhaps it is a padding or character set issue, maybe some environment settings are causing something to be decoded differently? – Jamie Treworgy Sep 23 '11 at 19:43
  • I've tried writing to a file locally, and the image data is indeed valid. I can't try it on the server because I can't decode the image there ;) – ari Sep 23 '11 at 19:47
  • Your error is happening when you try to read the data from a byte array into an image - I'm saying write the byte array to a file directly so you can inspect it/compare it to what you got locally. I suspect the problem is happening in base64 decoding. – Jamie Treworgy Sep 23 '11 at 19:50
  • Ah! sorry I misunderstood. Just tried that, and the files are equivalent :/ – ari Sep 23 '11 at 20:01
  • Just to be absolutely sure - the byte arrays are the same length too? (e.g. the files could be the same because of padding). – Jamie Treworgy Sep 23 '11 at 20:05
  • yup, both arrays have a length of 1080074. – ari Sep 23 '11 at 20:07
  • Well that's disappointing :) Only other thing I can think of trying is see what happens when you explicitly use the 3rd parameter (validateImageData). Framework patch level? Very odd. – Jamie Treworgy Sep 23 '11 at 20:19
  • Yeah, I've tried both true and false for the 3rd parameter to no avail :/ – ari Sep 23 '11 at 20:25
  • Here's a thing, it works fine for a base64 encoded png using the standard canvas toDataURL() function. – ari Sep 23 '11 at 20:28
  • 1
    It's unlikely that this is the cause of your problem, but you shouldn't be using a `using` here. Documentation at http://msdn.microsoft.com/en-us/library/1kcb3wy4.aspx says, "You must keep the stream open for the lifetime of the Image." – Jim Mischel Sep 23 '11 at 23:23
  • Yeah, this is a simplified version of the code. The production version keeps the stream open. The problem is in decoding before the stream is closed though :/ – ari Sep 25 '11 at 21:38
  • Revisiting this issue and realized that my local environment is running a 64-bit version of windows 7 while the server (where it's failing) is running a 32-bit version of Windows Server. Does this ring any bells for anyone? – ari Oct 18 '11 at 19:12
  • I'm getting this on my 64-bit desktop dev machine. Quick fix was to just swallow the exception because it seems to be working correctly otherwise. – IrishChieftain May 25 '12 at 15:55

3 Answers3

5

I still don't know the real cause of your problem, but i guess it is related with a image format which Image class doesn't recognize. After inspecting the binary data a little bit, I could be able to form your image. I hope this helps.

Bitmap GetBitmap(byte[] buf)
{
    Int16 width = BitConverter.ToInt16(buf, 18);
    Int16 height = BitConverter.ToInt16(buf, 22);

    Bitmap bitmap = new Bitmap(width, height);

    int imageSize = width * height * 4;
    int headerSize = BitConverter.ToInt16(buf, 10);

    System.Diagnostics.Debug.Assert(imageSize == buf.Length - headerSize);

    int offset = headerSize;
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            bitmap.SetPixel(x, height - y - 1, Color.FromArgb(buf[offset + 3], buf[offset], buf[offset + 1], buf[offset + 2]));
            offset += 4;
        }
    }
    return bitmap;
}

private void Form1_Load(object sender, EventArgs e)
{
    using (FileStream f = File.OpenRead("base64.txt"))
    {
        byte[] buf = Convert.FromBase64String(new StreamReader(f).ReadToEnd());

        Bitmap bmp = GetBitmap(buf);
        this.ClientSize = new Size(bmp.Width, bmp.Height);
        this.BackgroundImage = bmp;
    }
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • Thanks! this gets me a good deal closer. The resulting output does change the colors though - R and B swapped? Original: http://i.imgur.com/a0UXf.gif Output: http://i.imgur.com/TVUmk.gif (note: the black background might be because of the canvas data conversion) – ari Oct 25 '11 at 15:36
  • Source? No I made it up. I don't know the original image. So, you have to play a little bit with SetPixel like this `bitmap.SetPixel(x, height - y - 1, Color.FromArgb(buf[offset + 3], buf[offset+2], buf[offset + 1], buf[offset]));` Note: I swapped `buf[offset+2]` and `buf[offset]` – L.B Oct 25 '11 at 17:05
  • awesome! thank you so much, that did it. Do you have an algorithm reference or bitmap data spec I can review? I hate to use code I don't completely understand. – ari Oct 25 '11 at 18:44
  • I must admit, I have no idea about image formats etc. I opened the binary data with a binary editor and tried to guess. If you do the same you'll see that first 70-80 bytes seem different that the rest. So, i thought this must be header, etc...etc... Shortly, colors are respresented with 4 bytes in the file. R-G-B-Alpha – L.B Oct 25 '11 at 18:56
3

The posted code seems correct. I have tested it and it works fine.

The exception "System.ArgumentException: Parameter is not valid." without any other hint (especially not the name of the parameter) is a wrapper for GDI+ (the underlying technology behind .NET Image class) standard InvalidParameter error, which does not tell use exactly what parameter is invalid.

So, following the FromStream code with .NET Reflector, we can see that the parameters used in GDI+ calls are essentially ... the input stream.

So my guess is the input stream you provide is sometimes invalid as an image? You should save the failing input streams (using File.SaveAllBytes(sf) for example) for further investigation.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • `So my guess is the input stream you provide is sometimes invalid as an image?` What do you think about sample data? Image class fails to load but I think it is somehow correct. – L.B Oct 19 '11 at 18:35
  • @L.B - I can load sample data (the link given by the OP) as a .BMP successfully. – Simon Mourier Oct 19 '11 at 20:39
  • But I can't with my XP-Home(32bit). I get the same error `Parameter is not valid` – L.B Oct 19 '11 at 21:06
  • @L.B - Interesting. Maybe the GDI+ version is not the same, so it does not support the input image data. – Simon Mourier Oct 19 '11 at 21:11
  • I think --until someome comes with a better solution-- using a conditional compilation or trying to load images with both (`GetBitmap` or `Image.FromStream`) functions would be the best solution – L.B Oct 19 '11 at 21:54
  • That's what I'll have to do for now. I wish I knew why it wasn't working to begin with though. – ari Oct 25 '11 at 18:47
0

This could happen if sf contained invalid image data. Verify the validity of the data you're passing into the stream, and see if that fixes your issue.

James Johnson
  • 45,496
  • 8
  • 73
  • 110