AnimatedGifEncoder.cs
上传用户:lqb116
上传日期:2014-04-04
资源大小:2712k
文件大小:13k
源码类别:

P2P编程

开发平台:

C#

  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. namespace LanMsg.Gif.Components
  5. {
  6. public class AnimatedGifEncoder
  7. {
  8. protected int width; // image size
  9. protected int height;
  10. protected Color transparent = Color.Empty; // transparent color if given
  11. protected int transIndex; // transparent index in color table
  12. protected int repeat = -1; // no repeat
  13. protected int delay = 0; // frame delay (hundredths)
  14. protected bool started = false; // ready to output frames
  15. // protected BinaryWriter bw;
  16. protected FileStream fs;
  17. protected Image image; // current frame
  18. protected byte[] pixels; // BGR byte array from frame
  19. protected byte[] indexedPixels; // converted frame indexed to palette
  20. protected int colorDepth; // number of bit planes
  21. protected byte[] colorTab; // RGB palette
  22. protected bool[] usedEntry = new bool[256]; // active palette entries
  23. protected int palSize = 7; // color table size (bits-1)
  24. protected int dispose = -1; // disposal code (-1 = use default)
  25. protected bool closeStream = false; // close stream when finished
  26. protected bool firstFrame = true;
  27. protected bool sizeSet = false; // if false, get size from first frame
  28. protected int sample = 10; // default sample interval for quantizer
  29. /**
  30.  * Sets the delay time between each frame, or changes it
  31.  * for subsequent frames (applies to last frame added).
  32.  *
  33.  * @param ms int delay time in milliseconds
  34.  */
  35. public void SetDelay(int ms) 
  36. {
  37. delay = ( int ) Math.Round(ms / 10.0f);
  38. }
  39. /**
  40.  * Sets the GIF frame disposal code for the last added frame
  41.  * and any subsequent frames.  Default is 0 if no transparent
  42.  * color has been set, otherwise 2.
  43.  * @param code int disposal code.
  44.  */
  45. public void SetDispose(int code) 
  46. {
  47. if (code >= 0) 
  48. {
  49. dispose = code;
  50. }
  51. }
  52. /**
  53.  * Sets the number of times the set of GIF frames
  54.  * should be played.  Default is 1; 0 means play
  55.  * indefinitely.  Must be invoked before the first
  56.  * image is added.
  57.  *
  58.  * @param iter int number of iterations.
  59.  * @return
  60.  */
  61. public void SetRepeat(int iter) 
  62. {
  63. if (iter >= 0) 
  64. {
  65. repeat = iter;
  66. }
  67. }
  68. /**
  69.  * Sets the transparent color for the last added frame
  70.  * and any subsequent frames.
  71.  * Since all colors are subject to modification
  72.  * in the quantization process, the color in the final
  73.  * palette for each frame closest to the given color
  74.  * becomes the transparent color for that frame.
  75.  * May be set to null to indicate no transparent color.
  76.  *
  77.  * @param c Color to be treated as transparent on display.
  78.  */
  79. public void SetTransparent(Color c) 
  80. {
  81. transparent = c;
  82. }
  83. /**
  84.  * Adds next GIF frame.  The frame is not written immediately, but is
  85.  * actually deferred until the next frame is received so that timing
  86.  * data can be inserted.  Invoking <code>finish()</code> flushes all
  87.  * frames.  If <code>setSize</code> was not invoked, the size of the
  88.  * first image is used for all subsequent frames.
  89.  *
  90.  * @param im BufferedImage containing frame to write.
  91.  * @return true if successful.
  92.  */
  93. public bool AddFrame(Image im) 
  94. {
  95. if ((im == null) || !started) 
  96. {
  97. return false;
  98. }
  99. bool ok = true;
  100. try 
  101. {
  102. if (!sizeSet) 
  103. {
  104. // use first frame's size
  105. SetSize(im.Width, im.Height);
  106. }
  107. image = im;
  108. GetImagePixels(); // convert to correct format if necessary
  109. AnalyzePixels(); // build color table & map pixels
  110. if (firstFrame) 
  111. {
  112. WriteLSD(); // logical screen descriptior
  113. WritePalette(); // global color table
  114. if (repeat >= 0) 
  115. {
  116. // use NS app extension to indicate reps
  117. WriteNetscapeExt();
  118. }
  119. }
  120. WriteGraphicCtrlExt(); // write graphic control extension
  121. WriteImageDesc(); // image descriptor
  122. if (!firstFrame) 
  123. {
  124. WritePalette(); // local color table
  125. }
  126. WritePixels(); // encode and write pixel data
  127. firstFrame = false;
  128. catch (IOException e) 
  129. {
  130. ok = false;
  131. }
  132. return ok;
  133. }
  134. /**
  135.  * Flushes any pending data and closes output file.
  136.  * If writing to an OutputStream, the stream is not
  137.  * closed.
  138.  */
  139. public bool Finish() 
  140. {
  141. if (!started) return false;
  142. bool ok = true;
  143. started = false;
  144. try 
  145. {
  146. fs.WriteByte( 0x3b ); // gif trailer
  147. fs.Flush();
  148. if (closeStream) 
  149. {
  150. fs.Close();
  151. }
  152. catch (IOException e) 
  153. {
  154. ok = false;
  155. }
  156. // reset for subsequent use
  157. transIndex = 0;
  158. fs = null;
  159. image = null;
  160. pixels = null;
  161. indexedPixels = null;
  162. colorTab = null;
  163. closeStream = false;
  164. firstFrame = true;
  165. return ok;
  166. }
  167. /**
  168.  * Sets frame rate in frames per second.  Equivalent to
  169.  * <code>setDelay(1000/fps)</code>.
  170.  *
  171.  * @param fps float frame rate (frames per second)
  172.  */
  173. public void SetFrameRate(float fps) 
  174. {
  175. if (fps != 0f) 
  176. {
  177. delay = ( int ) Math.Round(100f / fps);
  178. }
  179. }
  180. /**
  181.  * Sets quality of color quantization (conversion of images
  182.  * to the maximum 256 colors allowed by the GIF specification).
  183.  * Lower values (minimum = 1) produce better colors, but slow
  184.  * processing significantly.  10 is the default, and produces
  185.  * good color mapping at reasonable speeds.  Values greater
  186.  * than 20 do not yield significant improvements in speed.
  187.  *
  188.  * @param quality int greater than 0.
  189.  * @return
  190.  */
  191. public void SetQuality(int quality) 
  192. {
  193. if (quality < 1) quality = 1;
  194. sample = quality;
  195. }
  196. /**
  197.  * Sets the GIF frame size.  The default size is the
  198.  * size of the first frame added if this method is
  199.  * not invoked.
  200.  *
  201.  * @param w int frame width.
  202.  * @param h int frame width.
  203.  */
  204. public void SetSize(int w, int h) 
  205. {
  206. if (started && !firstFrame) return;
  207. width = w;
  208. height = h;
  209. if (width < 1) width = 320;
  210. if (height < 1) height = 240;
  211. sizeSet = true;
  212. }
  213. /**
  214.  * Initiates GIF file creation on the given stream.  The stream
  215.  * is not closed automatically.
  216.  *
  217.  * @param os OutputStream on which GIF images are written.
  218.  * @return false if initial write failed.
  219.  */
  220. public bool Start( FileStream os) 
  221. {
  222. if (os == null) return false;
  223. bool ok = true;
  224. closeStream = false;
  225. fs = os;
  226. try 
  227. {
  228. WriteString("GIF89a"); // header
  229. catch (IOException e) 
  230. {
  231. ok = false;
  232. }
  233. return started = ok;
  234. }
  235. /**
  236.  * Initiates writing of a GIF file with the specified name.
  237.  *
  238.  * @param file String containing output file name.
  239.  * @return false if open or initial write failed.
  240.  */
  241. public bool Start(String file) 
  242. {
  243. bool ok = true;
  244. try 
  245. {
  246. // bw = new BinaryWriter( new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None ) );
  247. fs = new FileStream( file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None );
  248. ok = Start(fs);
  249. closeStream = true;
  250. catch (IOException e) 
  251. {
  252. ok = false;
  253. }
  254. return started = ok;
  255. }
  256. /**
  257.  * Analyzes image colors and creates color map.
  258.  */
  259. protected void AnalyzePixels() 
  260. {
  261. int len = pixels.Length;
  262. int nPix = len / 3;
  263. indexedPixels = new byte[nPix];
  264. NeuQuant nq = new NeuQuant(pixels, len, sample);
  265. // initialize quantizer
  266. colorTab = nq.Process(); // create reduced palette
  267. // convert map from BGR to RGB
  268. // for (int i = 0; i < colorTab.Length; i += 3) 
  269. // {
  270. // byte temp = colorTab[i];
  271. // colorTab[i] = colorTab[i + 2];
  272. // colorTab[i + 2] = temp;
  273. // usedEntry[i / 3] = false;
  274. // }
  275. // map image pixels to new palette
  276. int k = 0;
  277. for (int i = 0; i < nPix; i++) 
  278. {
  279. int index =
  280. nq.Map(pixels[k++] & 0xff,
  281. pixels[k++] & 0xff,
  282. pixels[k++] & 0xff);
  283. usedEntry[index] = true;
  284. indexedPixels[i] = (byte) index;
  285. }
  286. pixels = null;
  287. colorDepth = 8;
  288. palSize = 7;
  289. // get closest match to transparent color if specified
  290. if (transparent != Color.Empty ) 
  291. {
  292. transIndex = FindClosest(transparent);
  293. }
  294. }
  295. /**
  296.  * Returns index of palette color closest to c
  297.  *
  298.  */
  299. protected int FindClosest(Color c) 
  300. {
  301. if (colorTab == null) return -1;
  302. int r = c.R;
  303. int g = c.G;
  304. int b = c.B;
  305. int minpos = 0;
  306. int dmin = 256 * 256 * 256;
  307. int len = colorTab.Length;
  308. for (int i = 0; i < len;) 
  309. {
  310. int dr = r - (colorTab[i++] & 0xff);
  311. int dg = g - (colorTab[i++] & 0xff);
  312. int db = b - (colorTab[i] & 0xff);
  313. int d = dr * dr + dg * dg + db * db;
  314. int index = i / 3;
  315. if (usedEntry[index] && (d < dmin)) 
  316. {
  317. dmin = d;
  318. minpos = index;
  319. }
  320. i++;
  321. }
  322. return minpos;
  323. }
  324. /**
  325.  * Extracts image pixels into byte array "pixels"
  326.  */
  327. protected void GetImagePixels() 
  328. {
  329. int w = image.Width;
  330. int h = image.Height;
  331. // int type = image.GetType().;
  332. if ((w != width)
  333. || (h != height)
  334. {
  335. // create new image with right size/format
  336. Image temp =
  337. new Bitmap(width, height );
  338. Graphics g = Graphics.FromImage( temp );
  339. g.DrawImage(image, 0, 0);
  340. image = temp;
  341. g.Dispose();
  342. }
  343. /*
  344. ToDo:
  345. improve performance: use unsafe code 
  346. */
  347. pixels = new Byte [ 3 * image.Width * image.Height ];
  348. int count = 0;
  349. Bitmap tempBitmap = new Bitmap( image );
  350. for (int th = 0; th < image.Height; th++)
  351. {
  352. for (int tw = 0; tw < image.Width; tw++)
  353. {
  354. Color color = tempBitmap.GetPixel(tw, th);
  355. pixels[count] = color.R;
  356. count++;
  357. pixels[count] = color.G;
  358. count++;
  359. pixels[count] = color.B;
  360. count++;
  361. }
  362. }
  363. // pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
  364. }
  365. /**
  366.  * Writes Graphic Control Extension
  367.  */
  368. protected void WriteGraphicCtrlExt() 
  369. {
  370. fs.WriteByte(0x21); // extension introducer
  371. fs.WriteByte(0xf9); // GCE label
  372. fs.WriteByte(4); // data block size
  373. int transp, disp;
  374. if (transparent == Color.Empty ) 
  375. {
  376. transp = 0;
  377. disp = 0; // dispose = no action
  378. else 
  379. {
  380. transp = 1;
  381. disp = 2; // force clear if using transparent color
  382. }
  383. if (dispose >= 0) 
  384. {
  385. disp = dispose & 7; // user override
  386. }
  387. disp <<= 2;
  388. // packed fields
  389. fs.WriteByte( Convert.ToByte( 0 | // 1:3 reserved
  390. disp | // 4:6 disposal
  391. 0 | // 7   user input - 0 = none
  392. transp )); // 8   transparency flag
  393. WriteShort(delay); // delay x 1/100 sec
  394. fs.WriteByte( Convert.ToByte( transIndex)); // transparent color index
  395. fs.WriteByte(0); // block terminator
  396. }
  397. /**
  398.  * Writes Image Descriptor
  399.  */
  400. protected void WriteImageDesc()
  401. {
  402. fs.WriteByte(0x2c); // image separator
  403. WriteShort(0); // image position x,y = 0,0
  404. WriteShort(0);
  405. WriteShort(width); // image size
  406. WriteShort(height);
  407. // packed fields
  408. if (firstFrame) 
  409. {
  410. // no LCT  - GCT is used for first (or only) frame
  411. fs.WriteByte(0);
  412. else 
  413. {
  414. // specify normal LCT
  415. fs.WriteByte( Convert.ToByte( 0x80 | // 1 local color table  1=yes
  416. 0 | // 2 interlace - 0=no
  417. 0 | // 3 sorted - 0=no
  418. 0 | // 4-5 reserved
  419. palSize ) ); // 6-8 size of color table
  420. }
  421. }
  422. /**
  423.  * Writes Logical Screen Descriptor
  424.  */
  425. protected void WriteLSD()  
  426. {
  427. // logical screen size
  428. WriteShort(width);
  429. WriteShort(height);
  430. // packed fields
  431. fs.WriteByte( Convert.ToByte (0x80 | // 1   : global color table flag = 1 (gct used)
  432. 0x70 | // 2-4 : color resolution = 7
  433. 0x00 | // 5   : gct sort flag = 0
  434. palSize) ); // 6-8 : gct size
  435. fs.WriteByte(0); // background color index
  436. fs.WriteByte(0); // pixel aspect ratio - assume 1:1
  437. }
  438. /**
  439.  * Writes Netscape application extension to define
  440.  * repeat count.
  441.  */
  442. protected void WriteNetscapeExt()
  443. {
  444. fs.WriteByte(0x21); // extension introducer
  445. fs.WriteByte(0xff); // app extension label
  446. fs.WriteByte(11); // block size
  447. WriteString("NETSCAPE" + "2.0"); // app id + auth code
  448. fs.WriteByte(3); // sub-block size
  449. fs.WriteByte(1); // loop sub-block id
  450. WriteShort(repeat); // loop count (extra iterations, 0=repeat forever)
  451. fs.WriteByte(0); // block terminator
  452. }
  453. /**
  454.  * Writes color table
  455.  */
  456. protected void WritePalette()
  457. {
  458. fs.Write(colorTab, 0, colorTab.Length);
  459. int n = (3 * 256) - colorTab.Length;
  460. for (int i = 0; i < n; i++) 
  461. {
  462. fs.WriteByte(0);
  463. }
  464. }
  465. /**
  466.  * Encodes and writes pixel data
  467.  */
  468. protected void WritePixels()
  469. {
  470. LZWEncoder encoder =
  471. new LZWEncoder(width, height, indexedPixels, colorDepth);
  472. encoder.Encode( fs );
  473. }
  474. /**
  475.  *    Write 16-bit value to output stream, LSB first
  476.  */
  477. protected void WriteShort(int value)
  478. {
  479. fs.WriteByte( Convert.ToByte( value & 0xff));
  480. fs.WriteByte( Convert.ToByte( (value >> 8) & 0xff ));
  481. }
  482. /**
  483.  * Writes string to output stream
  484.  */
  485. protected void WriteString(String s)
  486. {
  487. char[] chars = s.ToCharArray();
  488. for (int i = 0; i < chars.Length; i++) 
  489. {
  490. fs.WriteByte((byte) chars[i]);
  491. }
  492. }
  493. }
  494. }