1 using System; 2 using System.IO; 3 using System.Collections.Generic; 4 using System.Windows; 5 using System.Windows.Controls; 6 using System.Windows.Media.Imaging; 7 using System.Windows.Threading; 8 9 namespace GifImageLib 10 { 11 12 class GifAnimation : Viewbox 13 { 14 15 private class GifFrame : Image 16 { 17 18 public int delayTime; 19 20 public int disposalMethod; 21 22 public int left; 23 24 public int top; 25 26 public int width; 27 28 public int height; 29 } 30 31 private Canvas canvas = null; 32 33 private List<GifFrame> frameList = null; 34 35 private int frameCounter = 0; 36 private int numberOfFrames = 0; 37 38 private int numberOfLoops = -1; 39 private int currentLoop = 0; 40 41 private int logicalWidth = 0; 42 private int logicalHeight = 0; 43 44 private DispatcherTimer frameTimer = null; 45 46 private GifFrame currentParseGifFrame; 47 48 public GifAnimation() 49 { 50 canvas = new Canvas(); 51 this.Child = canvas; 52 } 53 54 private void Reset() 55 { 56 if (frameList != null) 57 { 58 frameList.Clear(); 59 } 60 frameList = null; 61 frameCounter = 0; 62 numberOfFrames = 0; 63 numberOfLoops = -1; 64 currentLoop = 0; 65 logicalWidth = 0; 66 logicalHeight = 0; 67 if (frameTimer != null) 68 { 69 frameTimer.Stop(); 70 frameTimer = null; 71 } 72 } 73 74 #region PARSE 75 private void ParseGif(byte[] gifData) 76 { 77 frameList = new List<GifFrame>(); 78 currentParseGifFrame = new GifFrame(); 79 ParseGifDataStream(gifData, 0); 80 } 81 82 83 private int ParseBlock(byte[] gifData, int offset) 84 { 85 switch (gifData[offset]) 86 { 87 case 0x21: 88 if (gifData[offset + 1] == 0xF9) 89 { 90 return ParseGraphicControlExtension(gifData, offset); 91 } 92 else 93 { 94 return ParseExtensionBlock(gifData, offset); 95 } 96 case 0x2C: 97 offset = ParseGraphicBlock(gifData, offset); 98 frameList.Add(currentParseGifFrame); 99 currentParseGifFrame = new GifFrame(); 100 return offset; 101 case 0x3B: 102 return -1; 103 default: 104 throw new Exception("GIF format incorrect: missing graphic block or special-purpose block. "); 105 } 106 } 107 108 private int ParseGraphicControlExtension(byte[] gifData, int offset) 109 { 110 int returnOffset = offset; 111 int length = gifData[offset + 2]; 112 returnOffset = offset + length + 2 + 1; 113 114 byte packedField = gifData[offset + 3]; 115 currentParseGifFrame.disposalMethod = (packedField & 0x1C) >> 2; 116 117 int delay = BitConverter.ToUInt16(gifData, offset + 4); 118 currentParseGifFrame.delayTime = delay; 119 while (gifData[returnOffset] != 0x00) 120 { 121 returnOffset = returnOffset + gifData[returnOffset] + 1; 122 } 123 124 returnOffset++; 125 126 return returnOffset; 127 } 128 129 private int ParseLogicalScreen(byte[] gifData, int offset) 130 { 131 logicalWidth = BitConverter.ToUInt16(gifData, offset); 132 logicalHeight = BitConverter.ToUInt16(gifData, offset + 2); 133 134 byte packedField = gifData[offset + 4]; 135 bool hasGlobalColorTable = (int)(packedField & 0x80) > 0 ? true : false; 136 137 int currentIndex = offset + 7; 138 if (hasGlobalColorTable) 139 { 140 int colorTableLength = packedField & 0x07; 141 colorTableLength = (int)Math.Pow(2, colorTableLength + 1) * 3; 142 currentIndex = currentIndex + colorTableLength; 143 } 144 return currentIndex; 145 } 146 147 private int ParseGraphicBlock(byte[] gifData, int offset) 148 { 149 currentParseGifFrame.left = BitConverter.ToUInt16(gifData, offset + 1); 150 currentParseGifFrame.top = BitConverter.ToUInt16(gifData, offset + 3); 151 currentParseGifFrame.width = BitConverter.ToUInt16(gifData, offset + 5); 152 currentParseGifFrame.height = BitConverter.ToUInt16(gifData, offset + 7); 153 if (currentParseGifFrame.width > logicalWidth) 154 { 155 logicalWidth = currentParseGifFrame.width; 156 } 157 if (currentParseGifFrame.height > logicalHeight) 158 { 159 logicalHeight = currentParseGifFrame.height; 160 } 161 byte packedField = gifData[offset + 9]; 162 bool hasLocalColorTable = (int)(packedField & 0x80) > 0 ? true : false; 163 164 int currentIndex = offset + 9; 165 if (hasLocalColorTable) 166 { 167 int colorTableLength = packedField & 0x07; 168 colorTableLength = (int)Math.Pow(2, colorTableLength + 1) * 3; 169 currentIndex = currentIndex + colorTableLength; 170 } 171 currentIndex++; 172 173 currentIndex++; 174 175 while (gifData[currentIndex] != 0x00) 176 { 177 int length = gifData[currentIndex]; 178 currentIndex = currentIndex + gifData[currentIndex]; 179 currentIndex++; 180 } 181 currentIndex = currentIndex + 1; 182 return currentIndex; 183 } 184 185 private int ParseExtensionBlock(byte[] gifData, int offset) 186 { 187 int returnOffset = offset; 188 int length = gifData[offset + 2]; 189 returnOffset = offset + length + 2 + 1; 190 if (gifData[offset + 1] == 0xFF && length > 10) 191 { 192 string netscape = System.Text.ASCIIEncoding.ASCII.GetString(gifData, offset + 3, 8); 193 if (netscape == "NETSCAPE") 194 { 195 numberOfLoops = BitConverter.ToUInt16(gifData, offset + 16); 196 if (numberOfLoops > 0) 197 { 198 numberOfLoops++; 199 } 200 } 201 } 202 while (gifData[returnOffset] != 0x00) 203 { 204 returnOffset = returnOffset + gifData[returnOffset] + 1; 205 } 206 207 returnOffset++; 208 209 return returnOffset; 210 } 211 212 private int ParseHeader(byte[] gifData, int offset) 213 { 214 string str = System.Text.ASCIIEncoding.ASCII.GetString(gifData, offset, 3); 215 if (str != "GIF") 216 { 217 throw new Exception("Not a proper GIF file: missing GIF header"); 218 } 219 return 6; 220 } 221 222 private void ParseGifDataStream(byte[] gifData, int offset) 223 { 224 offset = ParseHeader(gifData, offset); 225 offset = ParseLogicalScreen(gifData, offset); 226 while (offset != -1) 227 { 228 offset = ParseBlock(gifData, offset); 229 } 230 } 231 232 #endregion 233 234 public void CreateGifAnimation(MemoryStream memoryStream) 235 { 236 Reset(); 237 238 byte[] gifData = memoryStream.GetBuffer(); // Use GetBuffer so that there is no memory copy 239 240 GifBitmapDecoder decoder = new GifBitmapDecoder(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 241 242 numberOfFrames = decoder.Frames.Count; 243 244 try 245 { 246 ParseGif(gifData); 247 } 248 catch 249 { 250 throw new FileFormatException("Unable to parse Gif file format."); 251 } 252 253 for (int i = 0; i < decoder.Frames.Count; i++) 254 { 255 frameList[i].Source = decoder.Frames[i]; 256 frameList[i].Visibility = Visibility.Hidden; 257 canvas.Children.Add(frameList[i]); 258 Canvas.SetLeft(frameList[i], frameList[i].left); 259 Canvas.SetTop(frameList[i], frameList[i].top); 260 Canvas.SetZIndex(frameList[i], i); 261 } 262 canvas.Height = logicalHeight; 263 canvas.Width = logicalWidth; 264 265 frameList[0].Visibility = Visibility.Visible; 266 267 for (int i = 0; i < frameList.Count; i++) 268 { 269 Console.WriteLine(frameList[i].disposalMethod.ToString() + " " + frameList[i].width.ToString() + " " + frameList[i].delayTime.ToString()); 270 } 271 272 if (frameList.Count > 1) 273 { 274 if (numberOfLoops == -1) 275 { 276 numberOfLoops = 1; 277 } 278 frameTimer = new System.Windows.Threading.DispatcherTimer(); 279 frameTimer.Tick += NextFrame; 280 frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[0].delayTime * 10); 281 frameTimer.Start(); 282 } 283 } 284 285 public void NextFrame() 286 { 287 NextFrame(null, null); 288 } 289 290 public void NextFrame(object sender, EventArgs e) 291 { 292 frameTimer.Stop(); 293 if (numberOfFrames == 0) return; 294 if (frameList[frameCounter].disposalMethod == 2) 295 { 296 frameList[frameCounter].Visibility = Visibility.Hidden; 297 } 298 if (frameList[frameCounter].disposalMethod >= 3) 299 { 300 frameList[frameCounter].Visibility = Visibility.Hidden; 301 } 302 frameCounter++; 303 304 if (frameCounter < numberOfFrames) 305 { 306 frameList[frameCounter].Visibility = Visibility.Visible; 307 frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[frameCounter].delayTime * 10); 308 frameTimer.Start(); 309 } 310 else 311 { 312 if (numberOfLoops != 0) 313 { 314 currentLoop++; 315 } 316 if (currentLoop < numberOfLoops || numberOfLoops == 0) 317 { 318 for (int f = 0; f < frameList.Count; f++) 319 { 320 frameList[f].Visibility = Visibility.Hidden; 321 } 322 frameCounter = 0; 323 frameList[frameCounter].Visibility = Visibility.Visible; 324 frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[frameCounter].delayTime * 10); 325 frameTimer.Start(); 326 } 327 } 328 } 329 } 330 }
相关文章: