2009/01/01

Optimizing the generation of gifs in .Net

While debugging some problems that a client had with CKFinder I found out that the thumbnails that were generated using the asp.net code of gif files were in reality png files, just a little oversight and should be easy to fix. Previously the code did test only for jpg files in order to adjust the quality, and the rest of extensions did were saved with

oResampled.Save( targetFile );


But as I said, the .Net framework doesn't check the extension of the file that you want to generate, instead it uses the png format.

So the new code just needed to do a switch:

switch ( extension )
{
    case ".gif":
        oResampled.Save( targetFile, System.Drawing.Imaging.ImageFormat.Gif );
        break ;
    case ".png":
        oResampled.Save( targetFile, System.Drawing.Imaging.ImageFormat.Png );
        break ;
    case ".bmp":
        oResampled.Save( targetFile, System.Drawing.Imaging.ImageFormat.Bmp );
        break ;
}
easy fix, isn't it?

You're wrong, for some strange design decision at MS, decreasing the palette from full color to 256 colors uses the infamous "web safe" palette, so your page will look again like those done at the start of the times, back when Nestcape was synonymous to Internet.

Why has the .Net team gone that way at this time and age?. I don't care, I didn't want to know why it's done that way, I just wanted to know how to do it the right way, how to specify a proper palette for the new file.

So searching a little you can find this very interesting article at MSDN that explains everything that you want to know about gifs and colors, it has links to other resources that try to explain the miracle that is a web safe palette, but it has been years since I saw a system that wasn't able to work with 24 bit-images.

That article does even include a sample that shows exactly how to do the quantization of images and provides also an implementation of an Octree palette generator. Too good to be true :-)

So there we go, add the class and quickly I see that there are some strange attributes on the classes. "unsafe", uh?

Let's recap: the article was written for the .Net Framework 1.1, and nowadays the servers in hosted providers are using mostly (AFAIK) Asp.net 2.0 in medium trust, so that code won't work at all.

Fortunately there was another nice blog by Brendan Tompkins that did found this same problem and does provide an explanation of the required changes in order to run properly. So I tested and now I didn't get the same compilation problems that I had with the original code, the unsafeness seemed to have gone away.

But when I tested really with medium trust, oh no!, a System.Security.SecurityException! Did I make something wrong?

I reviewed the changes and used directly the modified classes, nops, no way, it fails. Reading the comments I found that other people also reported the same problem but no one answered them. Could it be that initially the code did work in .Net 2.0 but some patch later the behavior was changed and now the calls to Marshal aren't allowed?

Searching to find the solution to this problem hasn't been successful, no one seems to know a solution and I spent one or two days reading everything that seemed interesting trying different combinations of search terms. Even trying to find another method to do the quantization hasn't been fruitful, there are questions posted in different places but no one has provided a positive answer.

Obviously I don't need to read the raw memory of the picture, using GetPixel seems that should be enough to create the palette, but this isn't the final solution because to generate the new picture with the specially created palette I need to specify the color/index of each pixel, and when you try to use SetPixel, you get:

"SetPixel is not supported for images with indexed pixel formats"

And again the solutions to this problem suggest to use the LockBits and then writing directly to memory, but that will fail in Medium Trust.

To recap:

  • If you're running in High Trust, CKFinder 1.3 the asp.net or asp version (with asp.net handling) will create gif thumbnails correctly
  • If you're in Medium Trust the call to create the 256-colors image will fail, and so far we haven't been able to find any solution. The GetPixel method is useful only for half of the process, it does still require direct memory access to create the new image so it hasn't been implemented. In this situation the "gif" image is really a 24-bit color png, not optimal and if someday the browsers stop sniffing the content of images they won't be shown, but hopefully before we reach that day MS would have released .Net v8 with some api to control the color-reduction method and/or SetPixel method expanded to handle indexed images.

No comments: