|
I looked all over the NET. This by far is the only good enough solution I see (even in April 2007) -- full 2 years after this article was written.
The only thing I would want to change in this code would be to optimize for speed and size; nothing else!
|
|
|
|
|
I have been able to optimize the GifDecoder quiet a bit, but at the expense of compatability. The realy slow code which runs a lot (realy A LOT) of times is the GetPixel() and SetPixel() functions. I have rewritten the code to avoid the use of those, but this have to be done in "usafe" context. This means that you have to compile with the compiler option "unsafe", and that the executable will only run on the processor type you compiled it on (afair). You can just replace the two functions below in the GifDecoder.cs file.
Here is the code for the GetPixels function
int [] GetPixels( Bitmap bitmap )
{
int [] pixels = new int [ 3 * image.Width * image.Height ];
int count = 0;
int tw, th;
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
int scanwidth = data.Stride;
System.IntPtr Scan = data.Scan0;
int width = bitmap.Width;
int height = bitmap.Height;
unsafe
{
int offset;
if (bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppPArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppRgb)
{
offset = scanwidth - bitmap.Width * 4;
}
else
offset = scanwidth - bitmap.Width * 3;
int x, y;
byte colr, colg, colb;
byte* p = (byte*)(void*)Scan;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
pixels[count++] = (int)*(p++);
pixels[count++] = (int)*(p++);
pixels[count++] = (int)*(p++);
}
p += offset;
}
}
bitmap.UnlockBits(data);
return pixels;
}
Here is the code for the SetPixels function
void SetPixels( int [] pixels )
{
int count = 0;
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, bitmap.PixelFormat);
int scanwidth = data.Stride;
System.IntPtr Scan = data.Scan0;
int width = image.Width;
int height = image.Height;
unsafe
{
bool fourBytePerPixel = false;
int offset;
if (bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppPArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppRgb)
{
offset = scanwidth - bitmap.Width * 4;
fourBytePerPixel = true;
}
else
offset = scanwidth - bitmap.Width * 3;
int x, y;
byte* p = (byte*)(void*)Scan;
Int32 pixeldata;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
pixeldata = pixels[count++];
*(p++) = (byte)(pixeldata);
*(p++) = (byte)(pixeldata >> 8);
*(p++) = (byte)(pixeldata >> 16);
if (fourBytePerPixel)
*(p++) = (byte)(pixeldata >> 24);
}
p += offset;
}
}
bitmap.UnlockBits(data);
}
You must have me excused if the code is a bit cluttered, but it should be usefull anyway. Actually I have not gone into details to see whether it is necessary to test for 24 or 32 bit images, as the code might use purly 32 bit images.
- Lothver
|
|
|
|
|
Has anyone been able to optimize this code for the encoder?
I have 15 frames which amount to 82kb, however if I use a animated gif application its only 38kb, something isn't right?
modified 19-Aug-21 21:01pm.
|
|
|
|
|
Do you mean size optimization such that the resulting GIF file is smaller?
- Lothver
|
|
|
|
|
Yeah
I've managed to do it now
modified 19-Aug-21 21:01pm.
|
|
|
|
|
Any possibility of sharing your optimisations?
Thanks
|
|
|
|
|
Do you know how I can play a gif in a Form?
_____________________________
...and justice for all
APe
|
|
|
|
|
|
Just FYI
This will violate LZW Patent No. 4,558,302 by Unisys.
Compuserve hold copyright on the GIF89a but the compression used for the images internaly is LZW.
Schneider
Schneider
|
|
|
|
|
I am no lawyer, but I think the patent expired few years ago...
|
|
|
|
|
It did a few YEARS ago
So png freaks put a cap on it
|
|
|
|
|
Feature request number 1 on my pet project, www.foodcandy.com after its launch six months ago was that there're no animated gifs supported on the site. I now have it working! It should go live next week. So many thanks for this code. If someone maintains an open source version I'll be glad to contribute pieces.
|
|
|
|
|
Default loop count should be -1 (don't loop), 1 means loop once (animate twice).
|
|
|
|
|
I think that forcing the GIF to crop is not very nice Here's a change to resize the GIF frames to whatever the target (or first frame) size is.
<br />
private static Image GetResizedImage(Image imgPhoto, Size ts)<br />
{<br />
<br />
int sourceWidth = imgPhoto.Width;<br />
int sourceHeight = imgPhoto.Height;<br />
int sourceX = 0;<br />
int sourceY = 0;<br />
int destX = 0;<br />
int destY = 0;<br />
<br />
float nPercent = 0;<br />
float nPercentW = 0;<br />
float nPercentH = 0;<br />
<br />
bool sourceVertical = sourceWidth < sourceHeight;<br />
bool targetVeritcal = ts.Width < ts.Height;<br />
<br />
if (sourceVertical != targetVeritcal)<br />
{<br />
int t = ts.Width;<br />
ts.Width = ts.Height;<br />
ts.Height = t;<br />
}<br />
<br />
nPercentW = ((float)ts.Width / (float)sourceWidth);<br />
nPercentH = ((float)ts.Height / (float)sourceHeight);<br />
if (nPercentH < nPercentW)<br />
{<br />
nPercent = nPercentH;<br />
destX = System.Convert.ToInt16((ts.Width -<br />
(sourceWidth * nPercent)) / 2);<br />
}<br />
else<br />
{<br />
nPercent = nPercentW;<br />
destY = System.Convert.ToInt16((ts.Height -<br />
(sourceHeight * nPercent)) / 2);<br />
}<br />
<br />
int destWidth = (int)(sourceWidth * nPercent);<br />
int destHeight = (int)(sourceHeight * nPercent);<br />
<br />
Bitmap bmPhoto = new Bitmap(ts.Width, ts.Height,<br />
PixelFormat.Format24bppRgb);<br />
bmPhoto.SetResolution(imgPhoto.HorizontalResolution,<br />
imgPhoto.VerticalResolution);<br />
<br />
Graphics grPhoto = Graphics.FromImage(bmPhoto);<br />
grPhoto.Clear(Color.White);<br />
grPhoto.InterpolationMode =<br />
InterpolationMode.HighQualityBicubic;<br />
<br />
grPhoto.DrawImage(imgPhoto,<br />
new Rectangle(destX, destY, destWidth, destHeight),<br />
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),<br />
GraphicsUnit.Pixel);<br />
<br />
grPhoto.Dispose();<br />
return bmPhoto;<br />
}<br />
<br />
protected void GetImagePixels()<br />
{<br />
int w = image.Width;<br />
int h = image.Height;<br />
if ((w != width)<br />
|| (h != height)<br />
)<br />
{<br />
image = GetResizedImage(image, new Size(width, height));<br />
}<br />
....<br />
<br />
public bool IsTransparent()<br />
{<br />
return transparency;<br />
}<br />
<br />
public Color GetTransparency()<br />
{<br />
Color c = Color.Empty;<br />
if (transparency)<br />
{<br />
c = Color.FromArgb(0, 0, 0, 0);
}<br />
else<br />
{<br />
c = Color.FromArgb(lastBgColor);<br />
}<br />
return c;<br />
}<br />
and finally the whole resize, very simple
<br />
<br />
public static void Resize(Stream inStream, Stream outStream, int width, int height)<br />
{<br />
GifDecoder decoder = new GifDecoder();<br />
decoder.Read(inStream);<br />
<br />
AnimatedGifEncoder encoder = new AnimatedGifEncoder();<br />
encoder.SetSize(width, height);<br />
encoder.SetFrameRate(5);<br />
encoder.SetRepeat(decoder.GetLoopCount());<br />
if (decoder.IsTransparent()) encoder.SetTransparent(decoder.GetTransparency());<br />
encoder.Start(outStream);<br />
for (int i = 0; i < decoder.GetFrameCount(); i++)<br />
{<br />
encoder.SetDelay(decoder.GetDelay(i));<br />
encoder.AddFrame(decoder.GetFrame(i));<br />
}<br />
encoder.Finish();<br />
outStream.Flush();<br />
}<br />
|
|
|
|
|
Access to the path "c:\test.gif" is denied.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.UnauthorizedAccessException: Access to the path "c:\test.gif" is denied.
Line 253: String outputFilePath = "c:\\test.gif"; //Server.MapPath("NameLogoTool/test.gif");
Line 254: AnimatedGifEncoder e = new AnimatedGifEncoder();
Line 255: e.Start(outputFilePath);
Line 256: e.SetDelay(500);
Line 257: //-1:no repeat,0:always repeat
|
|
|
|
|
Would you be interested in releasing this as an open source project? I'd be happy to set it up on SourceForge, CodePlex, or Google Code. The code you've posted works well, but it would be nice to extend it:
- allow alternate quantization methode (Octree, etc.)
- include optimizations (unsafe code, image checksums, etc.)
- leverage System.Drawing
- convert to allow use of individual functions by shifting some of the class variables to method parameters
I've already made some of these changes. It would be great to get your go-ahead to release a next version. I saw that the original Java source allowed reuse for any reason, so if I don't hear back I may just start over from the original Java source.
Jon Galloway
http://weblogs.asp.net/jgalloway
|
|
|
|
|
|
Oh, your code do not works.
Well, the follow may works.
private int NextPixel()
{
if (remaining == 0)
return EOF;
--remaining;
if (curPixel <= pixAry.GetUpperBound(0))
return pixAry[curPixel++];
return EOF;
}
Day Day Up
|
|
|
|
|
The solution for the problem with LZWEncoder making the last two pixels white is replacing "private int NextPixel()" with:
<br />
<br />
<br />
private int NextPixel() <br />
{<br />
if (remaining == 0)<br />
return EOF;<br />
<br />
--remaining;<br />
<br />
byte pix = pixAry[curPixel++];<br />
<br />
return pix & 0xff;<br />
}<br />
<br />
It is just the java code in the Refs Directory --> Gif.zip ->LZWEncoder.java
I tested it,and it works well~hoho~
|
|
|
|
|
Hi! There is a bug in your programm. It makes 2 last pixels of each frame "index 255", that often makes that pixels shining.
The bug is in private int NextPixel() in LZWEncoder.cs. If you take a look you may see that 2 last pixels returned are 0xff. Fix is obvious.
Good luck!
|
|
|
|
|
Fix is the following:
private int NextPixel()
{
if (curPixel <= pixAry.GetUpperBound(0))
{
byte pix = pixAry[curPixel++];
return pix & 0xff;
}
else
return (EOF);
}
Variable remaining in the original code is unuseful and can be removed at all.
|
|
|
|
|
Any one else having the same problem I am?
The bottom right hand corner of my animated GIFs always has a white pixel. I am 100% sure the Image i'm adding as a frame doesn't have that white pixel.
Thoughts?
Thanks.
|
|
|
|
|
Great Job gOODiDEA.NET!!! This is exactly what I have been looking for!
I modified your code a bit to support returning the animated GIF as a MemoryStream object instead of a file. This is helpful on a web server where GIF graphics are generated on the fly and it's not possible to send the output to a file first.
The modified code can be downloaded here:
http://www.thinkedgesoftware.com/download/NGif_src2.zip[^]
Feel free to incorporate the changes.
|
|
|
|
|
Sorry for my english, i'm french
Can you update the link for your modified code (
http://www.thinkedgesoftware.com/download/NGif_src2.zip) ?
Now, it doesn't exist at the server
You can send it me at etr_matty AT hotmail.com
Thank you
|
|
|
|
|
hi guy,I also need it ,if you see the msg,
please send code to ssh591@hotmail.com
thank you
|
|
|
|