PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/common/src/core/Utils/Image/GifImage.cs

https://github.com/microdee/vvvv-sdk
C# | 291 lines | 217 code | 51 blank | 23 comment | 12 complexity | 4a9bc1de667480452a2239cb4f474cbe MD5 | raw file
Possible License(s): AGPL-3.0, LGPL-2.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.IO;
  8. namespace VVVV.Utils.Imaging
  9. {
  10. //adapted from:
  11. //http://stackoverflow.com/questions/1196322/how-to-create-an-animated-gif-in-net
  12. //gif file format specification:
  13. //http://www.fileformat.info/format/gif/egff.htm
  14. /// <summary>
  15. /// Uses default .net GIF encoding and adds animation headers.
  16. /// </summary>
  17. public class Gif : IDisposable, IEnumerable<Image>
  18. {
  19. #region Header Constants
  20. const byte FileTrailer = 0x3b,
  21. ApplicationBlockSize = 0x0b,
  22. GraphicControlExtensionBlockSize = 0x04;
  23. const int ApplicationExtensionBlockIdentifier = 0xff21,
  24. GraphicControlExtensionBlockIdentifier = 0xf921;
  25. const long SourceGlobalColorInfoPosition = 10,
  26. SourceGraphicControlExtensionLength = 8,
  27. SourceImageBlockHeaderLength = 11,
  28. SourceColorBlockPosition = 13;
  29. long SourceColorBlockLength, SourceGraphicControlExtensionPosition, SourceImageBlockPosition;
  30. const string NetscapeIdentification = "NETSCAPE2.0",
  31. VVVVIdentification = "vvvv50.....",
  32. FileType = "GIF",
  33. FileVersion = "89a";
  34. #endregion
  35. class GifFrame : IDisposable
  36. {
  37. public Image Image;
  38. public double Delay;
  39. public int XOffset, YOffset;
  40. public GifFrame(Image image, double delay, int xOffset, int yOffset)
  41. {
  42. Image = image;
  43. Delay = delay;
  44. XOffset = xOffset;
  45. YOffset = yOffset;
  46. }
  47. public void Dispose()
  48. {
  49. Image.Dispose();
  50. }
  51. }
  52. List<GifFrame> Frames = new List<GifFrame>();
  53. public Gif() { DefaultFrameDelay = 1; }
  54. public Gif(Stream InStream, int Repeat = 0, int Delay = 500)
  55. {
  56. using (var Animation = Bitmap.FromStream(InStream))
  57. {
  58. int Length = Animation.GetFrameCount(FrameDimension.Time);
  59. DefaultFrameDelay = Delay;
  60. this.Repeat = Repeat;
  61. for (int i = 0; i < Length; ++i)
  62. {
  63. Animation.SelectActiveFrame(FrameDimension.Time, i);
  64. var Frame = new Bitmap(Animation.Size.Width, Animation.Size.Height);
  65. Graphics.FromImage(Frame).DrawImage(Animation, new Point(0, 0));
  66. Frames.Add(new GifFrame(Frame, Delay, 0, 0));
  67. }
  68. }
  69. }
  70. #region Properties
  71. public int DefaultWidth { get; set; }
  72. public int DefaultHeight { get; set; }
  73. public int Count { get { return Frames.Count; } }
  74. /// <summary>
  75. /// Default Delay in Milliseconds
  76. /// </summary>
  77. public int DefaultFrameDelay { get; set; }
  78. public int Repeat { get; private set; }
  79. #endregion
  80. /// <summary>
  81. /// Adds a frame to this animation.
  82. /// </summary>
  83. /// <param name="Image">The image to add</param>
  84. /// <param name="XOffset">The positioning x offset this image should be displayed at.</param>
  85. /// <param name="YOffset">The positioning y offset this image should be displayed at.</param>
  86. public void AddFrame(Image Image, double? frameDelay = null, int XOffset = 0, int YOffset = 0)
  87. {
  88. Frames.Add(new GifFrame(Image, frameDelay ?? DefaultFrameDelay, XOffset, YOffset));
  89. }
  90. public void AddFrame(string FilePath, double? frameDelay = null, int XOffset = 0, int YOffset = 0)
  91. {
  92. AddFrame(new Bitmap(FilePath), frameDelay, XOffset, YOffset);
  93. }
  94. public void RemoveAt(int Index) { Frames.RemoveAt(Index); }
  95. public void Clear()
  96. {
  97. foreach (var frame in Frames)
  98. frame.Dispose();
  99. Frames.Clear();
  100. }
  101. public void Save(Stream OutStream, PaletteType paletteType, DitherType ditherType, int colorCount)
  102. {
  103. using (var Writer = new BinaryWriter(OutStream))
  104. {
  105. //Header
  106. Writer.Write(FileType.ToCharArray());
  107. Writer.Write(FileVersion.ToCharArray());
  108. //Logical Screen Descriptor
  109. var w = Frames[0].Image.Width;
  110. var h = Frames[0].Image.Height;
  111. Writer.Write((short)(DefaultWidth == 0 ? w : DefaultWidth)); // Initial Logical Width
  112. Writer.Write((short)(DefaultHeight == 0 ? h : DefaultHeight)); // Initial Logical Height
  113. byte gctInfo = 0;
  114. if (paletteType != PaletteType.Optimal) //use a global colortable
  115. {
  116. using (var gifStream = new MemoryStream())
  117. {
  118. var bitmap = new Bitmap(Frames[0].Image);
  119. bitmap.ChangeTo8bppIndexed(paletteType, ditherType, colorCount);
  120. bitmap.Save(gifStream, ImageFormat.Gif);
  121. gifStream.Position = SourceGlobalColorInfoPosition;
  122. gctInfo = (byte)gifStream.ReadByte();
  123. var bv = new BitVector32(gctInfo);
  124. var sizeSection = BitVector32.CreateSection(7);
  125. SourceColorBlockLength = (1 << (bv[sizeSection] + 1)) * 3;
  126. SourceGraphicControlExtensionPosition = SourceColorBlockLength + 13;
  127. SourceImageBlockPosition = SourceGraphicControlExtensionPosition;
  128. Writer.Write(gctInfo); // Global Color Table Info
  129. Writer.Write((byte)0); // Background Color Index
  130. Writer.Write((byte)0); // Pixel aspect ratio
  131. WriteColorTable(gifStream, Writer);
  132. }
  133. }
  134. else //local colortables will be used for each frame
  135. {
  136. Writer.Write(gctInfo); // Global Color Table Info
  137. Writer.Write((byte)0); // Background Color Index
  138. Writer.Write((byte)0); // Pixel aspect ratio
  139. }
  140. //Netscape extension for looping animations
  141. unchecked { Writer.Write((short)ApplicationExtensionBlockIdentifier); };
  142. Writer.Write((byte)ApplicationBlockSize);
  143. Writer.Write(NetscapeIdentification.ToCharArray());
  144. Writer.Write((byte)3); // Application block length
  145. Writer.Write((byte)1);
  146. Writer.Write((short)Repeat); // Repeat count for images.
  147. Writer.Write((byte)0); // terminator
  148. // vvvv Extension Header
  149. unchecked { Writer.Write((short)ApplicationExtensionBlockIdentifier); };
  150. Writer.Write((byte)ApplicationBlockSize);
  151. Writer.Write(VVVVIdentification.ToCharArray());
  152. Writer.Write((byte)1); // Application block length
  153. Writer.Write((byte)0); // dummy
  154. Writer.Write((byte)0); // terminator
  155. //individual frames
  156. for (int i = 0; i < Count; ++i)
  157. {
  158. var frame = Frames[i];
  159. {
  160. using (var gifStream = new MemoryStream())
  161. {
  162. var bitmap = new Bitmap(frame.Image);
  163. if (bitmap.ChangeTo8bppIndexed(paletteType, ditherType, colorCount) == 0)
  164. bitmap.Save(gifStream, ImageFormat.Gif);
  165. else
  166. frame.Image.Save(gifStream, ImageFormat.Gif);
  167. WriteGraphicControlBlock(gifStream, Writer, frame.Delay);
  168. WriteImageBlock(gifStream, Writer, paletteType == PaletteType.Optimal, frame.XOffset, frame.YOffset, bitmap.Width, bitmap.Height);
  169. }
  170. }
  171. }
  172. // Complete File
  173. Writer.Write(FileTrailer);
  174. }
  175. }
  176. #region Write
  177. void WriteColorTable(Stream sourceGif, BinaryWriter Writer)
  178. {
  179. sourceGif.Position = SourceColorBlockPosition; // Locating the image color table
  180. var colorTable = new byte[SourceColorBlockLength];
  181. sourceGif.Read(colorTable, 0, colorTable.Length);
  182. Writer.Write(colorTable, 0, colorTable.Length);
  183. }
  184. void WriteGraphicControlBlock(Stream sourceGif, BinaryWriter Writer, double frameDelay)
  185. {
  186. unchecked { Writer.Write((short)GraphicControlExtensionBlockIdentifier); }; // Identifier
  187. Writer.Write((byte)GraphicControlExtensionBlockSize); // Block Size
  188. Writer.Write((byte)(0 & 0xf7 | 0x08)); // Setting disposal flag
  189. Writer.Write((short)(frameDelay)); // Setting frame delay
  190. Writer.Write((byte)0); // (byte)blockhead[6]); // Transparent color index
  191. Writer.Write((byte)0); // Terminator
  192. }
  193. void WriteImageBlock(Stream sourceGif, BinaryWriter Writer, bool includeColorTable, int x, int y, int w, int h)
  194. {
  195. sourceGif.Position = SourceGlobalColorInfoPosition;
  196. var gctInfo = (byte)sourceGif.ReadByte();
  197. var bv = new BitVector32(gctInfo);
  198. var sizeSection = BitVector32.CreateSection(7);
  199. SourceColorBlockLength = (1 << (bv[sizeSection] + 1)) * 3;
  200. SourceImageBlockPosition = SourceColorBlockLength + 13;
  201. sourceGif.Position = SourceImageBlockPosition; // Locating the image block
  202. var header = new byte[SourceImageBlockHeaderLength];
  203. sourceGif.Read(header, 0, header.Length);
  204. Writer.Write((byte)header[0]); // Separator
  205. Writer.Write((short)x); // Position X
  206. Writer.Write((short)y); // Position Y
  207. Writer.Write((short)w); // Width
  208. Writer.Write((short)h); // Height
  209. if (includeColorTable)
  210. {
  211. Writer.Write((byte)(gctInfo & 0x3f | 0x80)); // Enabling local color table
  212. WriteColorTable(sourceGif, Writer);
  213. }
  214. else
  215. Writer.Write((byte)0); // (header[9] & 0x07 | 0x07)); // Disabling local color table
  216. Writer.Write((byte)header[10]); // LZW Min Code Size
  217. // Read/Write image data
  218. sourceGif.Position = SourceImageBlockPosition + SourceImageBlockHeaderLength;
  219. var dataLength = sourceGif.ReadByte();
  220. while (dataLength > 0)
  221. {
  222. var imgData = new byte[dataLength];
  223. sourceGif.Read(imgData, 0, dataLength);
  224. Writer.Write((byte)dataLength);
  225. Writer.Write(imgData, 0, dataLength);
  226. dataLength = sourceGif.ReadByte();
  227. }
  228. Writer.Write((byte)0); // Terminator
  229. }
  230. #endregion
  231. public void Dispose()
  232. {
  233. Frames.Clear();
  234. Frames = null;
  235. }
  236. public Image this[int Index] { get { return Frames[Index].Image; } }
  237. public IEnumerator<Image> GetEnumerator() { foreach (var Frame in Frames) yield return Frame.Image; }
  238. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
  239. }
  240. }