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 }
1 using System.Net; 2 using System.IO; 3 using System.Security; 4 using System.Windows; 5 using System.Windows.Controls; 6 using System.Windows.Media; 7 using System.Windows.Media.Imaging; 8 using System.Windows.Threading; 9 using System.Windows.Resources; 10 using System; 11 12 namespace GifImageLib 13 { 14 public class GifImageExceptionRoutedEventArgs : RoutedEventArgs 15 { 16 public Exception ErrorException; 17 18 public GifImageExceptionRoutedEventArgs(RoutedEvent routedEvent, object obj) 19 : base(routedEvent, obj) 20 { 21 } 22 } 23 24 class WebReadState 25 { 26 public WebRequest webRequest; 27 public MemoryStream memoryStream; 28 public Stream readStream; 29 public byte[] buffer; 30 } 31 32 33 public class GifImage : System.Windows.Controls.UserControl 34 { 35 private GifAnimation gifAnimation = null; 36 private Image image = null; 37 38 public GifImage() 39 { 40 } 41 42 public static readonly DependencyProperty ForceGifAnimProperty = DependencyProperty.Register("ForceGifAnim", typeof(bool), typeof(GifImage), new FrameworkPropertyMetadata(false)); 43 public bool ForceGifAnim 44 { 45 get 46 { 47 return (bool)this.GetValue(ForceGifAnimProperty); 48 } 49 set 50 { 51 this.SetValue(ForceGifAnimProperty, value); 52 } 53 } 54 55 public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(GifImage), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnSourceChanged))); 56 private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 57 { 58 GifImage obj = (GifImage)d; 59 string s = (string)e.NewValue; 60 obj.CreateFromSourceString(s); 61 } 62 public string Source 63 { 64 get 65 { 66 return (string)this.GetValue(SourceProperty); 67 } 68 set 69 { 70 this.SetValue(SourceProperty, value); 71 } 72 } 73 74 75 public static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(GifImage), new FrameworkPropertyMetadata(Stretch.Fill, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnStretchChanged))); 76 private static void OnStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 77 { 78 GifImage obj = (GifImage)d; 79 Stretch s = (Stretch)e.NewValue; 80 if (obj.gifAnimation != null) 81 { 82 obj.gifAnimation.Stretch = s; 83 } 84 else if (obj.image != null) 85 { 86 obj.image.Stretch = s; 87 } 88 } 89 public Stretch Stretch 90 { 91 get 92 { 93 return (Stretch)this.GetValue(StretchProperty); 94 } 95 set 96 { 97 this.SetValue(StretchProperty, value); 98 } 99 } 100 101 public static readonly DependencyProperty StretchDirectionProperty = DependencyProperty.Register("StretchDirection", typeof(StretchDirection), typeof(GifImage), new FrameworkPropertyMetadata(StretchDirection.Both, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnStretchDirectionChanged))); 102 private static void OnStretchDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 103 { 104 GifImage obj = (GifImage)d; 105 StretchDirection s = (StretchDirection)e.NewValue; 106 if (obj.gifAnimation != null) 107 { 108 obj.gifAnimation.StretchDirection = s; 109 } 110 else if (obj.image != null) 111 { 112 obj.image.StretchDirection = s; 113 } 114 } 115 public StretchDirection StretchDirection 116 { 117 get 118 { 119 return (StretchDirection)this.GetValue(StretchDirectionProperty); 120 } 121 set 122 { 123 this.SetValue(StretchDirectionProperty, value); 124 } 125 } 126 127 public delegate void ExceptionRoutedEventHandler(object sender, GifImageExceptionRoutedEventArgs args); 128 129 public static readonly RoutedEvent ImageFailedEvent = EventManager.RegisterRoutedEvent("ImageFailed", RoutingStrategy.Bubble, typeof(ExceptionRoutedEventHandler), typeof(GifImage)); 130 131 public event ExceptionRoutedEventHandler ImageFailed 132 { 133 add 134 { 135 AddHandler(ImageFailedEvent, value); 136 } 137 remove 138 { 139 RemoveHandler(ImageFailedEvent, value); 140 } 141 } 142 143 void image_ImageFailed(object sender, ExceptionRoutedEventArgs e) 144 { 145 RaiseImageFailedEvent(e.ErrorException); 146 } 147 148 149 void RaiseImageFailedEvent(Exception exp) 150 { 151 GifImageExceptionRoutedEventArgs newArgs = new GifImageExceptionRoutedEventArgs(ImageFailedEvent, this); 152 newArgs.ErrorException = exp; 153 RaiseEvent(newArgs); 154 } 155 156 157 private void DeletePreviousImage() 158 { 159 if (image != null) 160 { 161 this.RemoveLogicalChild(image); 162 image = null; 163 } 164 if (gifAnimation != null) 165 { 166 this.RemoveLogicalChild(gifAnimation); 167 gifAnimation = null; 168 } 169 } 170 171 private void CreateNonGifAnimationImage() 172 { 173 image = new Image(); 174 image.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(image_ImageFailed); 175 ImageSource src = (ImageSource)(new ImageSourceConverter().ConvertFromString(Source)); 176 image.Source = src; 177 image.Stretch = Stretch; 178 image.StretchDirection = StretchDirection; 179 this.AddChild(image); 180 } 181 182 183 private void CreateGifAnimation(MemoryStream memoryStream) 184 { 185 gifAnimation = new GifAnimation(); 186 gifAnimation.CreateGifAnimation(memoryStream); 187 gifAnimation.Stretch = Stretch; 188 gifAnimation.StretchDirection = StretchDirection; 189 this.AddChild(gifAnimation); 190 } 191 192 193 private void CreateFromSourceString(string source) 194 { 195 DeletePreviousImage(); 196 Uri uri; 197 198 try 199 { 200 uri = new Uri(source, UriKind.RelativeOrAbsolute); 201 } 202 catch (Exception exp) 203 { 204 RaiseImageFailedEvent(exp); 205 return; 206 } 207 208 if (source.Trim().ToUpper().EndsWith(".GIF") || ForceGifAnim) 209 { 210 if (!uri.IsAbsoluteUri) 211 { 212 GetGifStreamFromPack(uri); 213 } 214 else 215 { 216 217 string leftPart = uri.GetLeftPart(UriPartial.Scheme); 218 219 if (leftPart == "http://" || leftPart == "ftp://" || leftPart == "file://") 220 { 221 GetGifStreamFromHttp(uri); 222 } 223 else if (leftPart == "pack://") 224 { 225 GetGifStreamFromPack(uri); 226 } 227 else 228 { 229 CreateNonGifAnimationImage(); 230 } 231 } 232 } 233 else 234 { 235 CreateNonGifAnimationImage(); 236 } 237 } 238 239 private delegate void WebRequestFinishedDelegate(MemoryStream memoryStream); 240 241 private void WebRequestFinished(MemoryStream memoryStream) 242 { 243 CreateGifAnimation(memoryStream); 244 } 245 246 private delegate void WebRequestErrorDelegate(Exception exp); 247 248 private void WebRequestError(Exception exp) 249 { 250 RaiseImageFailedEvent(exp); 251 } 252 253 private void WebResponseCallback(IAsyncResult asyncResult) 254 { 255 WebReadState webReadState = (WebReadState)asyncResult.AsyncState; 256 WebResponse webResponse; 257 try 258 { 259 webResponse = webReadState.webRequest.EndGetResponse(asyncResult); 260 webReadState.readStream = webResponse.GetResponseStream(); 261 webReadState.buffer = new byte[100000]; 262 webReadState.readStream.BeginRead(webReadState.buffer, 0, webReadState.buffer.Length, new AsyncCallback(WebReadCallback), webReadState); 263 } 264 catch (WebException exp) 265 { 266 this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestErrorDelegate(WebRequestError), exp); 267 } 268 } 269 270 private void WebReadCallback(IAsyncResult asyncResult) 271 { 272 WebReadState webReadState = (WebReadState)asyncResult.AsyncState; 273 int count = webReadState.readStream.EndRead(asyncResult); 274 if (count > 0) 275 { 276 webReadState.memoryStream.Write(webReadState.buffer, 0, count); 277 try 278 { 279 webReadState.readStream.BeginRead(webReadState.buffer, 0, webReadState.buffer.Length, new AsyncCallback(WebReadCallback), webReadState); 280 } 281 catch (WebException exp) 282 { 283 this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestErrorDelegate(WebRequestError), exp); 284 } 285 } 286 else 287 { 288 this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestFinishedDelegate(WebRequestFinished), webReadState.memoryStream); 289 } 290 } 291 292 private void GetGifStreamFromHttp(Uri uri) 293 { 294 try 295 { 296 WebReadState webReadState = new WebReadState(); 297 webReadState.memoryStream = new MemoryStream(); 298 webReadState.webRequest = WebRequest.Create(uri); 299 webReadState.webRequest.Timeout = 10000; 300 301 webReadState.webRequest.BeginGetResponse(new AsyncCallback(WebResponseCallback), webReadState); 302 } 303 catch (SecurityException) 304 { 305 CreateNonGifAnimationImage(); 306 } 307 } 308 309 310 private void ReadGifStreamSynch(Stream s) 311 { 312 byte[] gifData; 313 MemoryStream memoryStream; 314 using (s) 315 { 316 memoryStream = new MemoryStream((int)s.Length); 317 BinaryReader br = new BinaryReader(s); 318 gifData = br.ReadBytes((int)s.Length); 319 memoryStream.Write(gifData, 0, (int)s.Length); 320 memoryStream.Flush(); 321 } 322 CreateGifAnimation(memoryStream); 323 } 324 325 private void GetGifStreamFromPack(Uri uri) 326 { 327 try 328 { 329 StreamResourceInfo streamInfo; 330 331 if (!uri.IsAbsoluteUri) 332 { 333 streamInfo = Application.GetContentStream(uri); 334 if (streamInfo == null) 335 { 336 streamInfo = Application.GetResourceStream(uri); 337 } 338 } 339 else 340 { 341 if (uri.GetLeftPart(UriPartial.Authority).Contains("siteoforigin")) 342 { 343 streamInfo = Application.GetRemoteStream(uri); 344 } 345 else 346 { 347 streamInfo = Application.GetContentStream(uri); 348 if (streamInfo == null) 349 { 350 streamInfo = Application.GetResourceStream(uri); 351 } 352 } 353 } 354 if (streamInfo == null) 355 { 356 throw new FileNotFoundException("Resource not found.", uri.ToString()); 357 } 358 ReadGifStreamSynch(streamInfo.Stream); 359 } 360 catch (Exception exp) 361 { 362 RaiseImageFailedEvent(exp); 363 } 364 } 365 } 366 }
使用:
var image = new GifImage { Source = "Path", Stretch = Stretch.Fill };