EDN Admin
Well-known member
<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; using System;
<span style="color:Blue; using System.Drawing;
<span style="color:Blue; using System.IO;
<span style="color:Blue; #region .NET Disclaimer/Info
<span style="color:Green; //===============================================================================
<span style="color:Green; //
<span style="color:Green; // gOODiDEA, uland.com
<span style="color:Green; //===============================================================================
<span style="color:Green; //
<span style="color:Green; // $Header : $
<span style="color:Green; // $Author : $
<span style="color:Green; // $Date : $
<span style="color:Green; // $Revision: $
<span style="color:Green; // $History: $
<span style="color:Green; //
<span style="color:Green; //===============================================================================
<span style="color:Blue; #endregion
<span style="color:Blue; #region Java
<span style="color:Green; /**
* Class AnimatedGifEncoder - Encodes a GIF file consisting of one or
* more frames.
* <pre>
* Example:
* AnimatedGifEncoder e = new AnimatedGifEncoder();
* e.start(outputFileName);
* e.setDelay(1000); // 1 frame per sec
* e.addFrame(image1);
* e.addFrame(image2);
* e.finish();
* [/code]
* No copyright asserted on the source code of this class. May be used
* for any purpose, however, refer to the Unisys LZW patent for restrictions
* on use of the associated LZWEncoder class. Please forward any corrections
* to kweiner@fmsware.com.
*
* @author Kevin Weiner, FM Software
* @version 1.03 November 2003
*
*/
<span style="color:Blue; #endregion
<span style="color:Blue; namespace Gif.Components
{
<span style="color:Blue; public <span style="color:Blue; class AnimatedGifEncoder
{
<span style="color:Blue; protected <span style="color:Blue; int width; <span style="color:Green; // image size
<span style="color:Blue; protected <span style="color:Blue; int height;
<span style="color:Blue; protected Color transparent = Color.Empty; <span style="color:Green; // transparent color if given
<span style="color:Blue; protected <span style="color:Blue; int transIndex; <span style="color:Green; // transparent index in color table
<span style="color:Blue; protected <span style="color:Blue; int repeat = -1; <span style="color:Green; // no repeat
<span style="color:Blue; protected <span style="color:Blue; int delay = 0; <span style="color:Green; // frame delay (hundredths)
<span style="color:Blue; protected <span style="color:Blue; bool started = <span style="color:Blue; false; <span style="color:Green; // ready to output frames
<span style="color:Green; // protected BinaryWriter bw;
<span style="color:Blue; protected FileStream fs;
<span style="color:Blue; protected Image image; <span style="color:Green; // current frame
<span style="color:Blue; protected <span style="color:Blue; byte[] pixels; <span style="color:Green; // BGR byte array from frame
<span style="color:Blue; protected <span style="color:Blue; byte[] indexedPixels; <span style="color:Green; // converted frame indexed to palette
<span style="color:Blue; protected <span style="color:Blue; int colorDepth; <span style="color:Green; // number of bit planes
<span style="color:Blue; protected <span style="color:Blue; byte[] colorTab; <span style="color:Green; // RGB palette
<span style="color:Blue; protected <span style="color:Blue; bool[] usedEntry = <span style="color:Blue; new <span style="color:Blue; bool[256]; <span style="color:Green; // active palette entries
<span style="color:Blue; protected <span style="color:Blue; int palSize = 7; <span style="color:Green; // color table size (bits-1)
<span style="color:Blue; protected <span style="color:Blue; int dispose = -1; <span style="color:Green; // disposal code (-1 = use default)
<span style="color:Blue; protected <span style="color:Blue; bool closeStream = <span style="color:Blue; false; <span style="color:Green; // close stream when finished
<span style="color:Blue; protected <span style="color:Blue; bool firstFrame = <span style="color:Blue; true;
<span style="color:Blue; protected <span style="color:Blue; bool sizeSet = <span style="color:Blue; false; <span style="color:Green; // if false, get size from first frame
<span style="color:Blue; protected <span style="color:Blue; int sample = 10; <span style="color:Green; // default sample interval for quantizer
<span style="color:Green; /**
* Sets the delay time between each frame, or changes it
* for subsequent frames (applies to last frame added).
*
* @param ms int delay time in milliseconds
*/
<span style="color:Blue; public <span style="color:Blue; void SetDelay(<span style="color:Blue; int ms)
{
delay = ( <span style="color:Blue; int ) Math.Round(ms / 10.0f);
}
<span style="color:Green; /**
* Sets the GIF frame disposal code for the last added frame
* and any subsequent frames. Default is 0 if no transparent
* color has been set, otherwise 2.
* @param code int disposal code.
*/
<span style="color:Blue; public <span style="color:Blue; void SetDispose(<span style="color:Blue; int code)
{
<span style="color:Blue; if (code >= 0)
{
dispose = code;
}
}
<span style="color:Green; /**
* Sets the number of times the set of GIF frames
* should be played. Default is 1; 0 means play
* indefinitely. Must be invoked before the first
* image is added.
*
* @param iter int number of iterations.
* @return
*/
<span style="color:Blue; public <span style="color:Blue; void SetRepeat(<span style="color:Blue; int iter)
{
<span style="color:Blue; if (iter >= 0)
{
repeat = iter;
}
}
<span style="color:Green; /**
* Sets the transparent color for the last added frame
* and any subsequent frames.
* Since all colors are subject to modification
* in the quantization process, the color in the final
* palette for each frame closest to the given color
* becomes the transparent color for that frame.
* May be set to null to indicate no transparent color.
*
* @param c Color to be treated as transparent on display.
*/
<span style="color:Blue; public <span style="color:Blue; void SetTransparent(Color c)
{
transparent = c;
}
<span style="color:Green; /**
* Adds next GIF frame. The frame is not written immediately, but is
* actually deferred until the next frame is received so that timing
* data can be inserted. Invoking
flushes all
* frames. If
was not invoked, the size of the
* first image is used for all subsequent frames.
*
* @param im BufferedImage containing frame to write.
* @return true if successful.
*/
<span style="color:Blue; public <span style="color:Blue; bool AddFrame(Image im)
{
<span style="color:Blue; if ((im == <span style="color:Blue; null) || !started)
{
<span style="color:Blue; return <span style="color:Blue; false;
}
<span style="color:Blue; bool ok = <span style="color:Blue; true;
<span style="color:Blue; try
{
<span style="color:Blue; if (!sizeSet)
{
<span style="color:Green; // use first frames size
SetSize(im.Width, im.Height);
}
image = im;
GetImagePixels(); <span style="color:Green; // convert to correct format if necessary
AnalyzePixels(); <span style="color:Green; // build color table & map pixels
<span style="color:Blue; if (firstFrame)
{
WriteLSD(); <span style="color:Green; // logical screen descriptior
WritePalette(); <span style="color:Green; // global color table
<span style="color:Blue; if (repeat >= 0)
{
<span style="color:Green; // use NS app extension to indicate reps
WriteNetscapeExt();
}
}
WriteGraphicCtrlExt(); <span style="color:Green; // write graphic control extension
WriteImageDesc(); <span style="color:Green; // image descriptor
<span style="color:Blue; if (!firstFrame)
{
WritePalette(); <span style="color:Green; // local color table
}
WritePixels(); <span style="color:Green; // encode and write pixel data
firstFrame = <span style="color:Blue; false;
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return ok;
}
<span style="color:Green; /**
* Flushes any pending data and closes output file.
* If writing to an OutputStream, the stream is not
* closed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Finish()
{
<span style="color:Blue; if (!started) <span style="color:Blue; return <span style="color:Blue; false;
<span style="color:Blue; bool ok = <span style="color:Blue; true;
started = <span style="color:Blue; false;
<span style="color:Blue; try
{
fs.WriteByte( 0x3b ); <span style="color:Green; // gif trailer
fs.Flush();
<span style="color:Blue; if (closeStream)
{
fs.Close();
}
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Green; // reset for subsequent use
transIndex = 0;
fs = <span style="color:Blue; null;
image = <span style="color:Blue; null;
pixels = <span style="color:Blue; null;
indexedPixels = <span style="color:Blue; null;
colorTab = <span style="color:Blue; null;
closeStream = <span style="color:Blue; false;
firstFrame = <span style="color:Blue; true;
<span style="color:Blue; return ok;
}
<span style="color:Green; /**
* Sets frame rate in frames per second. Equivalent to
*
.
*
* @param fps float frame rate (frames per second)
*/
<span style="color:Blue; public <span style="color:Blue; void SetFrameRate(<span style="color:Blue; float fps)
{
<span style="color:Blue; if (fps != 0f)
{
delay = ( <span style="color:Blue; int ) Math.Round(100f / fps);
}
}
<span style="color:Green; /**
* Sets quality of color quantization (conversion of images
* to the maximum 256 colors allowed by the GIF specification).
* Lower values (minimum = 1) produce better colors, but slow
* processing significantly. 10 is the default, and produces
* good color mapping at reasonable speeds. Values greater
* than 20 do not yield significant improvements in speed.
*
* @param quality int greater than 0.
* @return
*/
<span style="color:Blue; public <span style="color:Blue; void SetQuality(<span style="color:Blue; int quality)
{
<span style="color:Blue; if (quality < 1) quality = 1;
sample = quality;
}
<span style="color:Green; /**
* Sets the GIF frame size. The default size is the
* size of the first frame added if this method is
* not invoked.
*
* @param w int frame width.
* @param h int frame width.
*/
<span style="color:Blue; public <span style="color:Blue; void SetSize(<span style="color:Blue; int w, <span style="color:Blue; int h)
{
<span style="color:Blue; if (started && !firstFrame) <span style="color:Blue; return;
width = w;
height = h;
<span style="color:Blue; if (width < 1) width = 320;
<span style="color:Blue; if (height < 1) height = 240;
sizeSet = <span style="color:Blue; true;
}
<span style="color:Green; /**
* Initiates GIF file creation on the given stream. The stream
* is not closed automatically.
*
* @param os OutputStream on which GIF images are written.
* @return false if initial write failed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Start( FileStream os)
{
<span style="color:Blue; if (os == <span style="color:Blue; null) <span style="color:Blue; return <span style="color:Blue; false;
<span style="color:Blue; bool ok = <span style="color:Blue; true;
closeStream = <span style="color:Blue; false;
fs = os;
<span style="color:Blue; try
{
WriteString(<span style="color:#A31515; "GIF89a"); <span style="color:Green; // header
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return started = ok;
}
<span style="color:Green; /**
* Initiates writing of a GIF file with the specified name.
*
* @param file String containing output file name.
* @return false if open or initial write failed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Start(String file)
{
<span style="color:Blue; bool ok = <span style="color:Blue; true;
<span style="color:Blue; try
{
<span style="color:Green; // bw = new BinaryWriter( new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None ) );
fs = <span style="color:Blue; new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None );
ok = Start(fs);
closeStream = <span style="color:Blue; true;
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return started = ok;
}
<span style="color:Green; /**
* Analyzes image colors and creates color map.
*/
<span style="color:Blue; protected <span style="color:Blue; void AnalyzePixels()
{
<span style="color:Blue; int len = pixels.Length;
<span style="color:Blue; int nPix = len / 3;
indexedPixels = <span style="color:Blue; new <span style="color:Blue; byte[nPix];
NeuQuant nq = <span style="color:Blue; new NeuQuant(pixels, len, sample);
<span style="color:Green; // initialize quantizer
colorTab = nq.Process(); <span style="color:Green; // create reduced palette
<span style="color:Green; // convert map from BGR to RGB
<span style="color:Green; // for (int i = 0; i < colorTab.Length; i += 3)
<span style="color:Green; // {
<span style="color:Green; // byte temp = colorTab;
<span style="color:Green; // colorTab = colorTab[i + 2];
<span style="color:Green; // colorTab[i + 2] = temp;
<span style="color:Green; // usedEntry[i / 3] = false;
<span style="color:Green; // }
<span style="color:Green; // map image pixels to new palette
<span style="color:Blue; int k = 0;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < nPix; i++)
{
<span style="color:Blue; int index =
nq.Map(pixels[k++] & 0xff,
pixels[k++] & 0xff,
pixels[k++] & 0xff);
usedEntry[index] = <span style="color:Blue; true;
indexedPixels = (<span style="color:Blue; byte) index;
}
pixels = <span style="color:Blue; null;
colorDepth = 8;
palSize = 7;
<span style="color:Green; // get closest match to transparent color if specified
<span style="color:Blue; if (transparent != Color.Empty )
{
transIndex = FindClosest(transparent);
}
}
<span style="color:Green; /**
* Returns index of palette color closest to c
*
*/
<span style="color:Blue; protected <span style="color:Blue; int FindClosest(Color c)
{
<span style="color:Blue; if (colorTab == <span style="color:Blue; null) <span style="color:Blue; return -1;
<span style="color:Blue; int r = c.R;
<span style="color:Blue; int g = c.G;
<span style="color:Blue; int b = c.B;
<span style="color:Blue; int minpos = 0;
<span style="color:Blue; int dmin = 256 * 256 * 256;
<span style="color:Blue; int len = colorTab.Length;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < len
{
<span style="color:Blue; int dr = r - (colorTab[i++] & 0xff);
<span style="color:Blue; int dg = g - (colorTab[i++] & 0xff);
<span style="color:Blue; int db = b - (colorTab & 0xff);
<span style="color:Blue; int d = dr * dr + dg * dg + db * db;
<span style="color:Blue; int index = i / 3;
<span style="color:Blue; if (usedEntry[index] && (d < dmin))
{
dmin = d;
minpos = index;
}
i++;
}
<span style="color:Blue; return minpos;
}
<span style="color:Green; /**
* Extracts image pixels into byte array "pixels"
*/
<span style="color:Blue; protected <span style="color:Blue; void GetImagePixels()
{
<span style="color:Blue; int w = image.Width;
<span style="color:Blue; int h = image.Height;
<span style="color:Green; // int type = image.GetType().;
<span style="color:Blue; if ((w != width)
|| (h != height)
)
{
<span style="color:Green; // create new image with right size/format
Image temp =
<span style="color:Blue; new Bitmap(width, height );
Graphics g = Graphics.FromImage( temp );
g.DrawImage(image, 0, 0);
image = temp;
g.Dispose();
}
<span style="color:Green; /*
ToDo:
improve performance: use unsafe code
*/
pixels = <span style="color:Blue; new Byte [ 3 * image.Width * image.Height ];
<span style="color:Blue; int count = 0;
Bitmap tempBitmap = <span style="color:Blue; new Bitmap( image );
<span style="color:Blue; for (<span style="color:Blue; int th = 0; th < image.Height; th++)
{
<span style="color:Blue; for (<span style="color:Blue; int tw = 0; tw < image.Width; tw++)
{
Color color = tempBitmap.GetPixel(tw, th);
pixels[count] = color.R;
count++;
pixels[count] = color.G;
count++;
pixels[count] = color.B;
count++;
}
}
<span style="color:Green; // pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
}
<span style="color:Green; /**
* Writes Graphic Control Extension
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteGraphicCtrlExt()
{
fs.WriteByte(0x21); <span style="color:Green; // extension introducer
fs.WriteByte(0xf9); <span style="color:Green; // GCE label
fs.WriteByte(4); <span style="color:Green; // data block size
<span style="color:Blue; int transp, disp;
<span style="color:Blue; if (transparent == Color.Empty )
{
transp = 0;
disp = 0; <span style="color:Green; // dispose = no action
}
<span style="color:Blue; else
{
transp = 1;
disp = 2; <span style="color:Green; // force clear if using transparent color
}
<span style="color:Blue; if (dispose >= 0)
{
disp = dispose & 7; <span style="color:Green; // user override
}
disp <<= 2;
<span style="color:Green; // packed fields
fs.WriteByte( Convert.ToByte( 0 | <span style="color:Green; // 1:3 reserved
disp | <span style="color:Green; // 4:6 disposal
0 | <span style="color:Green; // 7 user input - 0 = none
transp )); <span style="color:Green; // 8 transparency flag
WriteShort(delay); <span style="color:Green; // delay x 1/100 sec
fs.WriteByte( Convert.ToByte( transIndex)); <span style="color:Green; // transparent color index
fs.WriteByte(0); <span style="color:Green; // block terminator
}
<span style="color:Green; /**
* Writes Image Descriptor
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteImageDesc()
{
fs.WriteByte(0x2c); <span style="color:Green; // image separator
WriteShort(0); <span style="color:Green; // image position x,y = 0,0
WriteShort(0);
WriteShort(width); <span style="color:Green; // image size
WriteShort(height);
<span style="color:Green; // packed fields
<span style="color:Blue; if (firstFrame)
{
<span style="color:Green; // no LCT - GCT is used for first (or only) frame
fs.WriteByte(0);
}
<span style="color:Blue; else
{
<span style="color:Green; // specify normal LCT
fs.WriteByte( Convert.ToByte( 0x80 | <span style="color:Green; // 1 local color table 1=yes
0 | <span style="color:Green; // 2 interlace - 0=no
0 | <span style="color:Green; // 3 sorted - 0=no
0 | <span style="color:Green; // 4-5 reserved
palSize ) ); <span style="color:Green; // 6-8 size of color table
}
}
<span style="color:Green; /**
* Writes Logical Screen Descriptor
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteLSD()
{
<span style="color:Green; // logical screen size
WriteShort(width);
WriteShort(height);
<span style="color:Green; // packed fields
fs.WriteByte( Convert.ToByte (0x80 | <span style="color:Green; // 1 : global color table flag = 1 (gct used)
0x70 | <span style="color:Green; // 2-4 : color resolution = 7
0x00 | <span style="color:Green; // 5 : gct sort flag = 0
palSize) ); <span style="color:Green; // 6-8 : gct size
fs.WriteByte(0); <span style="color:Green; // background color index
fs.WriteByte(0); <span style="color:Green; // pixel aspect ratio - assume 1:1
}
<span style="color:Green; /**
* Writes Netscape application extension to define
* repeat count.
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteNetscapeExt()
{
fs.WriteByte(0x21); <span style="color:Green; // extension introducer
fs.WriteByte(0xff); <span style="color:Green; // app extension label
fs.WriteByte(11); <span style="color:Green; // block size
WriteString(<span style="color:#A31515; "NETSCAPE" + <span style="color:#A31515; "2.0"); <span style="color:Green; // app id + auth code
fs.WriteByte(3); <span style="color:Green; // sub-block size
fs.WriteByte(1); <span style="color:Green; // loop sub-block id
WriteShort(repeat); <span style="color:Green; // loop count (extra iterations, 0=repeat forever)
fs.WriteByte(0); <span style="color:Green; // block terminator
}
<span style="color:Green; /**
* Writes color table
*/
<span style="color:Blue; protected <span style="color:Blue; void WritePalette()
{
fs.Write(colorTab, 0, colorTab.Length);
<span style="color:Blue; int n = (3 * 256) - colorTab.Length;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < n; i++)
{
fs.WriteByte(0);
}
}
<span style="color:Green; /**
* Encodes and writes pixel data
*/
<span style="color:Blue; protected <span style="color:Blue; void WritePixels()
{
LZWEncoder encoder =
<span style="color:Blue; new LZWEncoder(width, height, indexedPixels, colorDepth);
encoder.Encode( fs );
}
<span style="color:Green; /**
* Write 16-bit value to output stream, LSB first
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteShort(<span style="color:Blue; int value)
{
fs.WriteByte( Convert.ToByte( value & 0xff));
fs.WriteByte( Convert.ToByte( (value >> 8) & 0xff ));
}
<span style="color:Green; /**
* Writes string to output stream
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteString(String s)
{
<span style="color:Blue; char[] chars = s.ToCharArray();
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < chars.Length; i++)
{
fs.WriteByte((<span style="color:Blue; byte) chars);
}
}
}
}
[/code]
<br/>
Im thinking that the FileStream should change to be somehow async but i dont know how to do it in this code.
And maybe its not the FileStream.
This is the code how im using it on Form1:
<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; using System;
<span style="color:Blue; using System.Collections.Generic;
<span style="color:Blue; using System.ComponentModel;
<span style="color:Blue; using System.Data;
<span style="color:Blue; using System.Drawing;
<span style="color:Blue; using System.Linq;
<span style="color:Blue; using System.Text;
<span style="color:Blue; using System.Windows.Forms;
<span style="color:Blue; using Gif.Components;
<span style="color:Blue; using System.IO;
<span style="color:Blue; using System.Drawing.Imaging;
<span style="color:Blue; namespace testing_gif_animation_saving
{
<span style="color:Blue; public <span style="color:Blue; partial <span style="color:Blue; class Form1 : Form
{
<span style="color:Blue; public Form1()
{
InitializeComponent();
<span style="color:Green; /* create Gif */
<span style="color:Green; //you should replace filepath
<span style="color:Blue; string[] dir = Directory.GetFiles(<span style="color:#A31515; @"d:Israel_Satellite_Images" , <span style="color:#A31515; "satellite*.png");
<span style="color:Green; //String[] imageFilePaths = new String[] { dir };//"d:\Israel_Satellite_Images\satellite000001.png", "D:\Israel_Satellite_Images\satellite000002.png"};
String outputFilePath = <span style="color:#A31515; "d:\test.gif";
AnimatedGifEncoder e = <span style="color:Blue; new AnimatedGifEncoder();
e.Start(outputFilePath);
e.SetDelay(80);
<span style="color:Green; //-1:no repeat,0:always repeat
e.SetRepeat(0);
<span style="color:Blue; for (<span style="color:Blue; int i = 0, count = dir.Length; i < count; i++)
{
e.AddFrame(Image.FromFile(dir));
}
e.Finish();
}
<span style="color:Blue; private <span style="color:Blue; void Form1_Load(<span style="color:Blue; object sender, EventArgs e)
{
}
}
}
[/code]
On form1 i used the AnimatedGifEncoder dll file added the dll file as reference.
I just wonder if there is any way to make it work Async ? I could invoke it maybe somehow on Form1 but i want to it to work Async.
Thanks.
<hr class="sig danieli
View the full article
<span style="color:Blue; using System;
<span style="color:Blue; using System.Drawing;
<span style="color:Blue; using System.IO;
<span style="color:Blue; #region .NET Disclaimer/Info
<span style="color:Green; //===============================================================================
<span style="color:Green; //
<span style="color:Green; // gOODiDEA, uland.com
<span style="color:Green; //===============================================================================
<span style="color:Green; //
<span style="color:Green; // $Header : $
<span style="color:Green; // $Author : $
<span style="color:Green; // $Date : $
<span style="color:Green; // $Revision: $
<span style="color:Green; // $History: $
<span style="color:Green; //
<span style="color:Green; //===============================================================================
<span style="color:Blue; #endregion
<span style="color:Blue; #region Java
<span style="color:Green; /**
* Class AnimatedGifEncoder - Encodes a GIF file consisting of one or
* more frames.
* <pre>
* Example:
* AnimatedGifEncoder e = new AnimatedGifEncoder();
* e.start(outputFileName);
* e.setDelay(1000); // 1 frame per sec
* e.addFrame(image1);
* e.addFrame(image2);
* e.finish();
* [/code]
* No copyright asserted on the source code of this class. May be used
* for any purpose, however, refer to the Unisys LZW patent for restrictions
* on use of the associated LZWEncoder class. Please forward any corrections
* to kweiner@fmsware.com.
*
* @author Kevin Weiner, FM Software
* @version 1.03 November 2003
*
*/
<span style="color:Blue; #endregion
<span style="color:Blue; namespace Gif.Components
{
<span style="color:Blue; public <span style="color:Blue; class AnimatedGifEncoder
{
<span style="color:Blue; protected <span style="color:Blue; int width; <span style="color:Green; // image size
<span style="color:Blue; protected <span style="color:Blue; int height;
<span style="color:Blue; protected Color transparent = Color.Empty; <span style="color:Green; // transparent color if given
<span style="color:Blue; protected <span style="color:Blue; int transIndex; <span style="color:Green; // transparent index in color table
<span style="color:Blue; protected <span style="color:Blue; int repeat = -1; <span style="color:Green; // no repeat
<span style="color:Blue; protected <span style="color:Blue; int delay = 0; <span style="color:Green; // frame delay (hundredths)
<span style="color:Blue; protected <span style="color:Blue; bool started = <span style="color:Blue; false; <span style="color:Green; // ready to output frames
<span style="color:Green; // protected BinaryWriter bw;
<span style="color:Blue; protected FileStream fs;
<span style="color:Blue; protected Image image; <span style="color:Green; // current frame
<span style="color:Blue; protected <span style="color:Blue; byte[] pixels; <span style="color:Green; // BGR byte array from frame
<span style="color:Blue; protected <span style="color:Blue; byte[] indexedPixels; <span style="color:Green; // converted frame indexed to palette
<span style="color:Blue; protected <span style="color:Blue; int colorDepth; <span style="color:Green; // number of bit planes
<span style="color:Blue; protected <span style="color:Blue; byte[] colorTab; <span style="color:Green; // RGB palette
<span style="color:Blue; protected <span style="color:Blue; bool[] usedEntry = <span style="color:Blue; new <span style="color:Blue; bool[256]; <span style="color:Green; // active palette entries
<span style="color:Blue; protected <span style="color:Blue; int palSize = 7; <span style="color:Green; // color table size (bits-1)
<span style="color:Blue; protected <span style="color:Blue; int dispose = -1; <span style="color:Green; // disposal code (-1 = use default)
<span style="color:Blue; protected <span style="color:Blue; bool closeStream = <span style="color:Blue; false; <span style="color:Green; // close stream when finished
<span style="color:Blue; protected <span style="color:Blue; bool firstFrame = <span style="color:Blue; true;
<span style="color:Blue; protected <span style="color:Blue; bool sizeSet = <span style="color:Blue; false; <span style="color:Green; // if false, get size from first frame
<span style="color:Blue; protected <span style="color:Blue; int sample = 10; <span style="color:Green; // default sample interval for quantizer
<span style="color:Green; /**
* Sets the delay time between each frame, or changes it
* for subsequent frames (applies to last frame added).
*
* @param ms int delay time in milliseconds
*/
<span style="color:Blue; public <span style="color:Blue; void SetDelay(<span style="color:Blue; int ms)
{
delay = ( <span style="color:Blue; int ) Math.Round(ms / 10.0f);
}
<span style="color:Green; /**
* Sets the GIF frame disposal code for the last added frame
* and any subsequent frames. Default is 0 if no transparent
* color has been set, otherwise 2.
* @param code int disposal code.
*/
<span style="color:Blue; public <span style="color:Blue; void SetDispose(<span style="color:Blue; int code)
{
<span style="color:Blue; if (code >= 0)
{
dispose = code;
}
}
<span style="color:Green; /**
* Sets the number of times the set of GIF frames
* should be played. Default is 1; 0 means play
* indefinitely. Must be invoked before the first
* image is added.
*
* @param iter int number of iterations.
* @return
*/
<span style="color:Blue; public <span style="color:Blue; void SetRepeat(<span style="color:Blue; int iter)
{
<span style="color:Blue; if (iter >= 0)
{
repeat = iter;
}
}
<span style="color:Green; /**
* Sets the transparent color for the last added frame
* and any subsequent frames.
* Since all colors are subject to modification
* in the quantization process, the color in the final
* palette for each frame closest to the given color
* becomes the transparent color for that frame.
* May be set to null to indicate no transparent color.
*
* @param c Color to be treated as transparent on display.
*/
<span style="color:Blue; public <span style="color:Blue; void SetTransparent(Color c)
{
transparent = c;
}
<span style="color:Green; /**
* Adds next GIF frame. The frame is not written immediately, but is
* actually deferred until the next frame is received so that timing
* data can be inserted. Invoking
Code:
finish()
* frames. If
Code:
setSize
* first image is used for all subsequent frames.
*
* @param im BufferedImage containing frame to write.
* @return true if successful.
*/
<span style="color:Blue; public <span style="color:Blue; bool AddFrame(Image im)
{
<span style="color:Blue; if ((im == <span style="color:Blue; null) || !started)
{
<span style="color:Blue; return <span style="color:Blue; false;
}
<span style="color:Blue; bool ok = <span style="color:Blue; true;
<span style="color:Blue; try
{
<span style="color:Blue; if (!sizeSet)
{
<span style="color:Green; // use first frames size
SetSize(im.Width, im.Height);
}
image = im;
GetImagePixels(); <span style="color:Green; // convert to correct format if necessary
AnalyzePixels(); <span style="color:Green; // build color table & map pixels
<span style="color:Blue; if (firstFrame)
{
WriteLSD(); <span style="color:Green; // logical screen descriptior
WritePalette(); <span style="color:Green; // global color table
<span style="color:Blue; if (repeat >= 0)
{
<span style="color:Green; // use NS app extension to indicate reps
WriteNetscapeExt();
}
}
WriteGraphicCtrlExt(); <span style="color:Green; // write graphic control extension
WriteImageDesc(); <span style="color:Green; // image descriptor
<span style="color:Blue; if (!firstFrame)
{
WritePalette(); <span style="color:Green; // local color table
}
WritePixels(); <span style="color:Green; // encode and write pixel data
firstFrame = <span style="color:Blue; false;
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return ok;
}
<span style="color:Green; /**
* Flushes any pending data and closes output file.
* If writing to an OutputStream, the stream is not
* closed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Finish()
{
<span style="color:Blue; if (!started) <span style="color:Blue; return <span style="color:Blue; false;
<span style="color:Blue; bool ok = <span style="color:Blue; true;
started = <span style="color:Blue; false;
<span style="color:Blue; try
{
fs.WriteByte( 0x3b ); <span style="color:Green; // gif trailer
fs.Flush();
<span style="color:Blue; if (closeStream)
{
fs.Close();
}
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Green; // reset for subsequent use
transIndex = 0;
fs = <span style="color:Blue; null;
image = <span style="color:Blue; null;
pixels = <span style="color:Blue; null;
indexedPixels = <span style="color:Blue; null;
colorTab = <span style="color:Blue; null;
closeStream = <span style="color:Blue; false;
firstFrame = <span style="color:Blue; true;
<span style="color:Blue; return ok;
}
<span style="color:Green; /**
* Sets frame rate in frames per second. Equivalent to
*
Code:
setDelay(1000/fps)
*
* @param fps float frame rate (frames per second)
*/
<span style="color:Blue; public <span style="color:Blue; void SetFrameRate(<span style="color:Blue; float fps)
{
<span style="color:Blue; if (fps != 0f)
{
delay = ( <span style="color:Blue; int ) Math.Round(100f / fps);
}
}
<span style="color:Green; /**
* Sets quality of color quantization (conversion of images
* to the maximum 256 colors allowed by the GIF specification).
* Lower values (minimum = 1) produce better colors, but slow
* processing significantly. 10 is the default, and produces
* good color mapping at reasonable speeds. Values greater
* than 20 do not yield significant improvements in speed.
*
* @param quality int greater than 0.
* @return
*/
<span style="color:Blue; public <span style="color:Blue; void SetQuality(<span style="color:Blue; int quality)
{
<span style="color:Blue; if (quality < 1) quality = 1;
sample = quality;
}
<span style="color:Green; /**
* Sets the GIF frame size. The default size is the
* size of the first frame added if this method is
* not invoked.
*
* @param w int frame width.
* @param h int frame width.
*/
<span style="color:Blue; public <span style="color:Blue; void SetSize(<span style="color:Blue; int w, <span style="color:Blue; int h)
{
<span style="color:Blue; if (started && !firstFrame) <span style="color:Blue; return;
width = w;
height = h;
<span style="color:Blue; if (width < 1) width = 320;
<span style="color:Blue; if (height < 1) height = 240;
sizeSet = <span style="color:Blue; true;
}
<span style="color:Green; /**
* Initiates GIF file creation on the given stream. The stream
* is not closed automatically.
*
* @param os OutputStream on which GIF images are written.
* @return false if initial write failed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Start( FileStream os)
{
<span style="color:Blue; if (os == <span style="color:Blue; null) <span style="color:Blue; return <span style="color:Blue; false;
<span style="color:Blue; bool ok = <span style="color:Blue; true;
closeStream = <span style="color:Blue; false;
fs = os;
<span style="color:Blue; try
{
WriteString(<span style="color:#A31515; "GIF89a"); <span style="color:Green; // header
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return started = ok;
}
<span style="color:Green; /**
* Initiates writing of a GIF file with the specified name.
*
* @param file String containing output file name.
* @return false if open or initial write failed.
*/
<span style="color:Blue; public <span style="color:Blue; bool Start(String file)
{
<span style="color:Blue; bool ok = <span style="color:Blue; true;
<span style="color:Blue; try
{
<span style="color:Green; // bw = new BinaryWriter( new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None ) );
fs = <span style="color:Blue; new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None );
ok = Start(fs);
closeStream = <span style="color:Blue; true;
}
<span style="color:Blue; catch (IOException e)
{
ok = <span style="color:Blue; false;
}
<span style="color:Blue; return started = ok;
}
<span style="color:Green; /**
* Analyzes image colors and creates color map.
*/
<span style="color:Blue; protected <span style="color:Blue; void AnalyzePixels()
{
<span style="color:Blue; int len = pixels.Length;
<span style="color:Blue; int nPix = len / 3;
indexedPixels = <span style="color:Blue; new <span style="color:Blue; byte[nPix];
NeuQuant nq = <span style="color:Blue; new NeuQuant(pixels, len, sample);
<span style="color:Green; // initialize quantizer
colorTab = nq.Process(); <span style="color:Green; // create reduced palette
<span style="color:Green; // convert map from BGR to RGB
<span style="color:Green; // for (int i = 0; i < colorTab.Length; i += 3)
<span style="color:Green; // {
<span style="color:Green; // byte temp = colorTab;
<span style="color:Green; // colorTab = colorTab[i + 2];
<span style="color:Green; // colorTab[i + 2] = temp;
<span style="color:Green; // usedEntry[i / 3] = false;
<span style="color:Green; // }
<span style="color:Green; // map image pixels to new palette
<span style="color:Blue; int k = 0;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < nPix; i++)
{
<span style="color:Blue; int index =
nq.Map(pixels[k++] & 0xff,
pixels[k++] & 0xff,
pixels[k++] & 0xff);
usedEntry[index] = <span style="color:Blue; true;
indexedPixels = (<span style="color:Blue; byte) index;
}
pixels = <span style="color:Blue; null;
colorDepth = 8;
palSize = 7;
<span style="color:Green; // get closest match to transparent color if specified
<span style="color:Blue; if (transparent != Color.Empty )
{
transIndex = FindClosest(transparent);
}
}
<span style="color:Green; /**
* Returns index of palette color closest to c
*
*/
<span style="color:Blue; protected <span style="color:Blue; int FindClosest(Color c)
{
<span style="color:Blue; if (colorTab == <span style="color:Blue; null) <span style="color:Blue; return -1;
<span style="color:Blue; int r = c.R;
<span style="color:Blue; int g = c.G;
<span style="color:Blue; int b = c.B;
<span style="color:Blue; int minpos = 0;
<span style="color:Blue; int dmin = 256 * 256 * 256;
<span style="color:Blue; int len = colorTab.Length;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < len
{
<span style="color:Blue; int dr = r - (colorTab[i++] & 0xff);
<span style="color:Blue; int dg = g - (colorTab[i++] & 0xff);
<span style="color:Blue; int db = b - (colorTab & 0xff);
<span style="color:Blue; int d = dr * dr + dg * dg + db * db;
<span style="color:Blue; int index = i / 3;
<span style="color:Blue; if (usedEntry[index] && (d < dmin))
{
dmin = d;
minpos = index;
}
i++;
}
<span style="color:Blue; return minpos;
}
<span style="color:Green; /**
* Extracts image pixels into byte array "pixels"
*/
<span style="color:Blue; protected <span style="color:Blue; void GetImagePixels()
{
<span style="color:Blue; int w = image.Width;
<span style="color:Blue; int h = image.Height;
<span style="color:Green; // int type = image.GetType().;
<span style="color:Blue; if ((w != width)
|| (h != height)
)
{
<span style="color:Green; // create new image with right size/format
Image temp =
<span style="color:Blue; new Bitmap(width, height );
Graphics g = Graphics.FromImage( temp );
g.DrawImage(image, 0, 0);
image = temp;
g.Dispose();
}
<span style="color:Green; /*
ToDo:
improve performance: use unsafe code
*/
pixels = <span style="color:Blue; new Byte [ 3 * image.Width * image.Height ];
<span style="color:Blue; int count = 0;
Bitmap tempBitmap = <span style="color:Blue; new Bitmap( image );
<span style="color:Blue; for (<span style="color:Blue; int th = 0; th < image.Height; th++)
{
<span style="color:Blue; for (<span style="color:Blue; int tw = 0; tw < image.Width; tw++)
{
Color color = tempBitmap.GetPixel(tw, th);
pixels[count] = color.R;
count++;
pixels[count] = color.G;
count++;
pixels[count] = color.B;
count++;
}
}
<span style="color:Green; // pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
}
<span style="color:Green; /**
* Writes Graphic Control Extension
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteGraphicCtrlExt()
{
fs.WriteByte(0x21); <span style="color:Green; // extension introducer
fs.WriteByte(0xf9); <span style="color:Green; // GCE label
fs.WriteByte(4); <span style="color:Green; // data block size
<span style="color:Blue; int transp, disp;
<span style="color:Blue; if (transparent == Color.Empty )
{
transp = 0;
disp = 0; <span style="color:Green; // dispose = no action
}
<span style="color:Blue; else
{
transp = 1;
disp = 2; <span style="color:Green; // force clear if using transparent color
}
<span style="color:Blue; if (dispose >= 0)
{
disp = dispose & 7; <span style="color:Green; // user override
}
disp <<= 2;
<span style="color:Green; // packed fields
fs.WriteByte( Convert.ToByte( 0 | <span style="color:Green; // 1:3 reserved
disp | <span style="color:Green; // 4:6 disposal
0 | <span style="color:Green; // 7 user input - 0 = none
transp )); <span style="color:Green; // 8 transparency flag
WriteShort(delay); <span style="color:Green; // delay x 1/100 sec
fs.WriteByte( Convert.ToByte( transIndex)); <span style="color:Green; // transparent color index
fs.WriteByte(0); <span style="color:Green; // block terminator
}
<span style="color:Green; /**
* Writes Image Descriptor
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteImageDesc()
{
fs.WriteByte(0x2c); <span style="color:Green; // image separator
WriteShort(0); <span style="color:Green; // image position x,y = 0,0
WriteShort(0);
WriteShort(width); <span style="color:Green; // image size
WriteShort(height);
<span style="color:Green; // packed fields
<span style="color:Blue; if (firstFrame)
{
<span style="color:Green; // no LCT - GCT is used for first (or only) frame
fs.WriteByte(0);
}
<span style="color:Blue; else
{
<span style="color:Green; // specify normal LCT
fs.WriteByte( Convert.ToByte( 0x80 | <span style="color:Green; // 1 local color table 1=yes
0 | <span style="color:Green; // 2 interlace - 0=no
0 | <span style="color:Green; // 3 sorted - 0=no
0 | <span style="color:Green; // 4-5 reserved
palSize ) ); <span style="color:Green; // 6-8 size of color table
}
}
<span style="color:Green; /**
* Writes Logical Screen Descriptor
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteLSD()
{
<span style="color:Green; // logical screen size
WriteShort(width);
WriteShort(height);
<span style="color:Green; // packed fields
fs.WriteByte( Convert.ToByte (0x80 | <span style="color:Green; // 1 : global color table flag = 1 (gct used)
0x70 | <span style="color:Green; // 2-4 : color resolution = 7
0x00 | <span style="color:Green; // 5 : gct sort flag = 0
palSize) ); <span style="color:Green; // 6-8 : gct size
fs.WriteByte(0); <span style="color:Green; // background color index
fs.WriteByte(0); <span style="color:Green; // pixel aspect ratio - assume 1:1
}
<span style="color:Green; /**
* Writes Netscape application extension to define
* repeat count.
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteNetscapeExt()
{
fs.WriteByte(0x21); <span style="color:Green; // extension introducer
fs.WriteByte(0xff); <span style="color:Green; // app extension label
fs.WriteByte(11); <span style="color:Green; // block size
WriteString(<span style="color:#A31515; "NETSCAPE" + <span style="color:#A31515; "2.0"); <span style="color:Green; // app id + auth code
fs.WriteByte(3); <span style="color:Green; // sub-block size
fs.WriteByte(1); <span style="color:Green; // loop sub-block id
WriteShort(repeat); <span style="color:Green; // loop count (extra iterations, 0=repeat forever)
fs.WriteByte(0); <span style="color:Green; // block terminator
}
<span style="color:Green; /**
* Writes color table
*/
<span style="color:Blue; protected <span style="color:Blue; void WritePalette()
{
fs.Write(colorTab, 0, colorTab.Length);
<span style="color:Blue; int n = (3 * 256) - colorTab.Length;
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < n; i++)
{
fs.WriteByte(0);
}
}
<span style="color:Green; /**
* Encodes and writes pixel data
*/
<span style="color:Blue; protected <span style="color:Blue; void WritePixels()
{
LZWEncoder encoder =
<span style="color:Blue; new LZWEncoder(width, height, indexedPixels, colorDepth);
encoder.Encode( fs );
}
<span style="color:Green; /**
* Write 16-bit value to output stream, LSB first
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteShort(<span style="color:Blue; int value)
{
fs.WriteByte( Convert.ToByte( value & 0xff));
fs.WriteByte( Convert.ToByte( (value >> 8) & 0xff ));
}
<span style="color:Green; /**
* Writes string to output stream
*/
<span style="color:Blue; protected <span style="color:Blue; void WriteString(String s)
{
<span style="color:Blue; char[] chars = s.ToCharArray();
<span style="color:Blue; for (<span style="color:Blue; int i = 0; i < chars.Length; i++)
{
fs.WriteByte((<span style="color:Blue; byte) chars);
}
}
}
}
[/code]
<br/>
Im thinking that the FileStream should change to be somehow async but i dont know how to do it in this code.
And maybe its not the FileStream.
This is the code how im using it on Form1:
<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; using System;
<span style="color:Blue; using System.Collections.Generic;
<span style="color:Blue; using System.ComponentModel;
<span style="color:Blue; using System.Data;
<span style="color:Blue; using System.Drawing;
<span style="color:Blue; using System.Linq;
<span style="color:Blue; using System.Text;
<span style="color:Blue; using System.Windows.Forms;
<span style="color:Blue; using Gif.Components;
<span style="color:Blue; using System.IO;
<span style="color:Blue; using System.Drawing.Imaging;
<span style="color:Blue; namespace testing_gif_animation_saving
{
<span style="color:Blue; public <span style="color:Blue; partial <span style="color:Blue; class Form1 : Form
{
<span style="color:Blue; public Form1()
{
InitializeComponent();
<span style="color:Green; /* create Gif */
<span style="color:Green; //you should replace filepath
<span style="color:Blue; string[] dir = Directory.GetFiles(<span style="color:#A31515; @"d:Israel_Satellite_Images" , <span style="color:#A31515; "satellite*.png");
<span style="color:Green; //String[] imageFilePaths = new String[] { dir };//"d:\Israel_Satellite_Images\satellite000001.png", "D:\Israel_Satellite_Images\satellite000002.png"};
String outputFilePath = <span style="color:#A31515; "d:\test.gif";
AnimatedGifEncoder e = <span style="color:Blue; new AnimatedGifEncoder();
e.Start(outputFilePath);
e.SetDelay(80);
<span style="color:Green; //-1:no repeat,0:always repeat
e.SetRepeat(0);
<span style="color:Blue; for (<span style="color:Blue; int i = 0, count = dir.Length; i < count; i++)
{
e.AddFrame(Image.FromFile(dir));
}
e.Finish();
}
<span style="color:Blue; private <span style="color:Blue; void Form1_Load(<span style="color:Blue; object sender, EventArgs e)
{
}
}
}
[/code]
On form1 i used the AnimatedGifEncoder dll file added the dll file as reference.
I just wonder if there is any way to make it work Async ? I could invoke it maybe somehow on Form1 but i want to it to work Async.
Thanks.
<hr class="sig danieli
View the full article