UIImageAnimation.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.Runtime.InteropServices;
  6. using Microsoft.Drawing;
  7. namespace Microsoft.Windows.Forms.Animate
  8. {
  9. /// <summary>
  10. /// 图片颜色切换动画
  11. /// </summary>
  12. public sealed class UIImageAnimation : Animation<Bitmap>
  13. {
  14. /// <summary>
  15. /// 默认动画是否随机
  16. /// </summary>
  17. public const bool DEFAULT_ANIMATION_RANDOM_PLAY = false;
  18. /// <summary>
  19. /// 默认动画执行时间,毫秒
  20. /// </summary>
  21. public const int DEFAULT_ANIMATION_SPAN = 3000;
  22. /// <summary>
  23. /// 空索引
  24. /// </summary>
  25. public const int NONE_INDEX = -1;
  26. private List<AnimationFrame> m_Origins = new List<AnimationFrame>(); //原始图片集合
  27. private List<byte[]> m_Frames = new List<byte[]>(); //截至帧集合
  28. private AnimationOperations m_SuspendedOps = new AnimationOperations(); //挂起的操作
  29. private RandomColor m_RandomColor = new RandomColor(); //随机颜色生成器
  30. private Random m_Random = new Random(unchecked((int)DateTime.Now.Ticks)); //随机数生成器
  31. private byte[] m_From; //起始帧数据
  32. private int m_ToIndex = NONE_INDEX; //截至帧索引
  33. private bool m_CurrentValid; //当前帧是否有效
  34. #region 属性
  35. private bool m_RandomPlay = DEFAULT_ANIMATION_RANDOM_PLAY;
  36. /// <summary>
  37. /// 获取或设置是否随机播放
  38. /// </summary>
  39. public bool RandomPlay
  40. {
  41. get
  42. {
  43. return this.m_RandomPlay;
  44. }
  45. set
  46. {
  47. this.m_RandomPlay = value;
  48. }
  49. }
  50. private Size m_Size = new Size(1, 1);
  51. /// <summary>
  52. /// 获取或设置动画大小
  53. /// </summary>
  54. public Size Size
  55. {
  56. get
  57. {
  58. return this.m_Size;
  59. }
  60. set
  61. {
  62. if (value.Width <= 0)
  63. value.Width = 1;
  64. if (value.Height <= 0)
  65. value.Height = 1;
  66. this.Resize(value);
  67. }
  68. }
  69. private Bitmap m_Current;
  70. /// <summary>
  71. /// 获取当前帧图像
  72. /// </summary>
  73. public override unsafe Bitmap Current
  74. {
  75. get
  76. {
  77. //已停止
  78. if (this.Stopped)
  79. {
  80. if (!this.m_CurrentValid)
  81. {
  82. byte[] current = this.UpdateCurrent();
  83. if (current != null)
  84. CopyBitmap(current, this.m_Current);
  85. this.m_CurrentValid = true;
  86. }
  87. return this.m_Current;
  88. }
  89. //计算信息
  90. byte[] from = this.m_From;
  91. byte[] to = this.m_ToIndex == NONE_INDEX ? null : this.m_Frames[this.m_ToIndex];
  92. //图像处理
  93. BlendBitmap(from, to, this.m_Current, this.Percentage);
  94. this.m_CurrentValid = true;
  95. return this.m_Current;
  96. }
  97. }
  98. #endregion 属性
  99. #region 构造函数
  100. /// <summary>
  101. /// 构造函数
  102. /// </summary>
  103. public UIImageAnimation()
  104. {
  105. base.Span = DEFAULT_ANIMATION_SPAN;
  106. }
  107. #endregion
  108. #region 动画操作
  109. /// <summary>
  110. /// 动画停止状态,当前帧无效时重新获取当前帧数据
  111. /// </summary>
  112. /// <returns>帧数据</returns>
  113. private byte[] UpdateCurrent()
  114. {
  115. int count = this.m_Origins.Count;
  116. switch (count)
  117. {
  118. case 0:
  119. this.m_ToIndex = NONE_INDEX;
  120. return null;
  121. case 1:
  122. this.m_ToIndex = 0;
  123. return this.m_Frames[0];
  124. default:
  125. if (this.m_ToIndex < 0 || this.m_ToIndex >= count)
  126. this.m_ToIndex = this.GetNextIndex();
  127. return this.m_Frames[this.m_ToIndex];
  128. }
  129. }
  130. /// <summary>
  131. /// 获取下一个目标帧
  132. /// </summary>
  133. /// <returns>下一个目标帧</returns>
  134. private int GetNextIndex()
  135. {
  136. int count = this.m_Origins.Count;
  137. if (this.m_RandomPlay)
  138. {
  139. int current = this.m_ToIndex;
  140. int temp;
  141. do
  142. {
  143. temp = this.m_Random.Next(count);
  144. } while (temp == current);
  145. return temp;
  146. }
  147. else
  148. {
  149. return (this.m_ToIndex + 1 >= count) ? 0 : (this.m_ToIndex + 1);
  150. }
  151. }
  152. /// <summary>
  153. /// 切换下一帧,开始动画
  154. /// </summary>
  155. public void Next()
  156. {
  157. //动画未结束
  158. if (!this.Stopped)
  159. return;
  160. //恢复挂起的操作,截止帧变起始帧
  161. this.Resume();
  162. //查找下一个截止帧
  163. switch (this.m_Origins.Count)
  164. {
  165. case 0:
  166. this.m_ToIndex = NONE_INDEX;
  167. break;
  168. case 1:
  169. this.m_ToIndex = 0;
  170. break;
  171. default:
  172. this.m_ToIndex = this.GetNextIndex();
  173. break;
  174. }
  175. //开始动画
  176. this.Start();
  177. }
  178. #endregion
  179. #region 集合操作
  180. /// <summary>
  181. /// 恢复大小改变操作,重新创建所有帧
  182. /// </summary>
  183. private void ResumeResize(Size size)
  184. {
  185. //赋值
  186. this.m_Size = size;
  187. //重新创建关键帧
  188. this.m_Frames.Clear();
  189. foreach (AnimationFrame frame in this.m_Origins)
  190. this.m_Frames.Add(GetFrameData(frame, size));
  191. //重新创建当前图像
  192. if (this.m_Current != null)
  193. this.m_Current.Dispose();
  194. this.m_Current = new Bitmap(this.m_Size.Width < 1 ? 1 : this.m_Size.Width, this.m_Size.Height < 1 ? 1 : this.m_Size.Height, PixelFormat.Format32bppArgb);
  195. this.m_CurrentValid = false;
  196. }
  197. /// <summary>
  198. /// 恢复添加关键帧操作
  199. /// </summary>
  200. /// <param name="frame">关键帧</param>
  201. private void ResumeAddFrame(AnimationFrame frame)
  202. {
  203. this.m_Origins.Add(frame);
  204. this.m_Frames.Add(GetFrameData(frame, this.m_Size));
  205. }
  206. /// <summary>
  207. /// 恢复清空关键帧操作
  208. /// </summary>
  209. private void ResumeClearFrame()
  210. {
  211. foreach (AnimationFrame frame in this.m_Origins)
  212. frame.Dispose();
  213. this.m_Origins.Clear();
  214. this.m_Frames.Clear();
  215. }
  216. /// <summary>
  217. /// 恢复挂起的操作,截止帧变起始帧
  218. /// </summary>
  219. private void Resume()
  220. {
  221. //截止帧变起始帧
  222. bool getLater = false;
  223. if (this.m_ToIndex == NONE_INDEX)
  224. {
  225. this.m_From = null;
  226. }
  227. else if (this.m_SuspendedOps.Resized)
  228. {
  229. if (this.m_SuspendedOps.Cleared)
  230. this.m_From = GetFrameData(this.m_Origins[this.m_ToIndex], this.m_SuspendedOps.Size);
  231. else
  232. getLater = true;
  233. }
  234. else
  235. {
  236. this.m_From = this.m_Frames[this.m_ToIndex];
  237. }
  238. //清理帧
  239. if (this.m_SuspendedOps.Cleared)
  240. this.ResumeClearFrame();
  241. //大小改变
  242. if (this.m_SuspendedOps.Resized)
  243. this.ResumeResize(this.m_SuspendedOps.Size);
  244. //大小改变后获取
  245. if (getLater)
  246. this.m_From = this.m_Frames[this.m_ToIndex];
  247. //添加帧
  248. foreach (AnimationFrame frame in this.m_SuspendedOps)
  249. this.ResumeAddFrame(frame);
  250. //清空操作
  251. this.m_SuspendedOps.Clear();
  252. }
  253. #endregion
  254. #region 用户操作
  255. /// <summary>
  256. /// 大小改变
  257. /// </summary>
  258. /// <param name="size">大小</param>
  259. private void Resize(Size size)
  260. {
  261. if (this.m_Current == null)
  262. this.ResumeResize(size);
  263. else
  264. this.m_SuspendedOps.Resize(size);
  265. }
  266. /// <summary>
  267. /// 添加一个图像帧
  268. /// </summary>
  269. /// <param name="image">图像</param>
  270. public void AddFrame(Image image)
  271. {
  272. if (image == null)
  273. throw new ArgumentNullException("image");
  274. AnimationFrame frame = new AnimationFrame(image);
  275. if (this.m_SuspendedOps.Cleared)
  276. this.m_SuspendedOps.AddFrame(frame);
  277. else
  278. this.ResumeAddFrame(frame);
  279. }
  280. /// <summary>
  281. /// 添加一个颜色帧
  282. /// </summary>
  283. /// <param name="color">颜色</param>
  284. public void AddFrame(Color color)
  285. {
  286. AnimationFrame frame = new AnimationFrame(color);
  287. if (this.m_SuspendedOps.Cleared)
  288. this.m_SuspendedOps.AddFrame(frame);
  289. else
  290. this.ResumeAddFrame(frame);
  291. }
  292. /// <summary>
  293. /// 添加一个随机颜色帧
  294. /// </summary>
  295. public void AddFrame()
  296. {
  297. AnimationFrame frame = new AnimationFrame(this.m_RandomColor.Next());
  298. if (this.m_SuspendedOps.Cleared)
  299. this.m_SuspendedOps.AddFrame(frame);
  300. else
  301. this.ResumeAddFrame(frame);
  302. }
  303. /// <summary>
  304. /// 清空所有帧,并释放占用的资源
  305. /// </summary>
  306. public void ClearFrame()
  307. {
  308. this.m_SuspendedOps.ClearFrame();
  309. }
  310. #endregion
  311. #region 图像处理
  312. /// <summary>
  313. /// 从颜色创建帧数据
  314. /// </summary>
  315. /// <param name="color">颜色</param>
  316. /// <param name="destSize">帧大小</param>
  317. /// <param name="destData">帧数据</param>
  318. private unsafe static void CopyColor(Color color, Size destSize, ref byte[] destData)
  319. {
  320. if (destData == null)
  321. destData = new byte[destSize.Width * destSize.Height * 4];
  322. byte b = color.B;
  323. byte g = color.G;
  324. byte r = color.R;
  325. byte a = color.A;
  326. for (int i = 0, length = destData.Length; i < length; i += 4)
  327. {
  328. destData[i] = b;
  329. destData[i + 1] = g;
  330. destData[i + 2] = r;
  331. destData[i + 3] = a;
  332. }
  333. }
  334. /// <summary>
  335. /// 从图像创建帧数据
  336. /// </summary>
  337. /// <param name="srcBmp">源图像</param>
  338. /// <param name="destData">目标帧数据</param>
  339. private static void CopyBitmap(Bitmap srcBmp, ref byte[] destData)
  340. {
  341. if (destData == null)
  342. destData = new byte[srcBmp.Width * srcBmp.Height * 4];
  343. using (LockedBitmapData bmpData = new LockedBitmapData(srcBmp, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb))
  344. Marshal.Copy(bmpData.Scan0, destData, 0, destData.Length);
  345. }
  346. /// <summary>
  347. /// 从帧数据快照图像
  348. /// </summary>
  349. /// <param name="srcData">源帧数据</param>
  350. /// <param name="destBmp">目标图像</param>
  351. private static void CopyBitmap(byte[] srcData, Bitmap destBmp)
  352. {
  353. using (LockedBitmapData bmpData = new LockedBitmapData(destBmp, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb))
  354. Marshal.Copy(srcData, 0, bmpData.Scan0, srcData.Length);
  355. }
  356. /// <summary>
  357. /// 获取两个帧之间指定百分比的动画帧
  358. /// </summary>
  359. /// <param name="from">起始帧数据</param>
  360. /// <param name="to">截止帧数据</param>
  361. /// <param name="destBmp">目标图像</param>
  362. /// <param name="percentage">百分比</param>
  363. private unsafe static void BlendBitmap(byte[] from, byte[] to, Bitmap destBmp, double percentage)
  364. {
  365. using (LockedBitmapData bmpData = new LockedBitmapData(destBmp, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb))
  366. {
  367. if (from == null)
  368. {
  369. if (to == null)
  370. {
  371. byte* ptr = (byte*)bmpData.Scan0;
  372. for (int i = 0, length = bmpData.Stride * bmpData.Height; i < length; i++)
  373. *(ptr++) = 0;
  374. }
  375. else if (percentage == STOPPED)
  376. {
  377. Marshal.Copy(to, 0, bmpData.Scan0, to.Length);
  378. }
  379. else
  380. {
  381. byte* ptr = (byte*)bmpData.Scan0;
  382. for (int i = 0, length = to.Length; i < length; i++)
  383. *(ptr++) = (byte)(to[i] * percentage);
  384. }
  385. }
  386. else
  387. {
  388. if (to == null)
  389. {
  390. percentage = STOPPED - percentage;
  391. byte* ptr = (byte*)bmpData.Scan0;
  392. for (int i = 0, length = from.Length; i < length; i++)
  393. *(ptr++) = (byte)(from[i] * percentage);
  394. }
  395. else if (percentage == STOPPED)
  396. {
  397. Marshal.Copy(to, 0, bmpData.Scan0, from.Length);
  398. }
  399. else
  400. {
  401. byte tmp;
  402. byte* ptr = (byte*)bmpData.Scan0;
  403. for (int i = 0, length = from.Length; i < length; i++)
  404. *(ptr++) = (byte)((tmp = from[i]) + (to[i] - tmp) * percentage);
  405. }
  406. }
  407. }
  408. }
  409. /// <summary>
  410. /// 获取帧数据
  411. /// </summary>
  412. /// <param name="frame">原始帧</param>
  413. /// <param name="size">帧数据图像大小</param>
  414. /// <returns>帧数据</returns>
  415. private static byte[] GetFrameData(AnimationFrame frame, Size size)
  416. {
  417. byte[] frameData = null;
  418. switch (frame.FrameType)
  419. {
  420. case AnimationFrameType.Image:
  421. using (Bitmap bmp = new Bitmap((Image)frame.Value, size.Width, size.Height))
  422. CopyBitmap(bmp, ref frameData);
  423. return frameData;
  424. case AnimationFrameType.Color:
  425. CopyColor((Color)frame.Value, size, ref frameData);
  426. return frameData;
  427. default:
  428. throw new ArgumentException("frame type error.");
  429. }
  430. }
  431. #endregion
  432. #region 释放资源
  433. /// <summary>
  434. /// 释放资源
  435. /// </summary>
  436. /// <param name="disposing">释放托管资源为true,否则为false</param>
  437. protected override void Dispose(bool disposing)
  438. {
  439. if (this.m_Origins != null)
  440. {
  441. foreach (AnimationFrame frame in this.m_Origins)
  442. frame.Dispose();
  443. this.m_Origins.Clear();
  444. this.m_Origins = null;
  445. }
  446. if (this.m_Frames != null)
  447. {
  448. this.m_Frames.Clear();
  449. this.m_Frames = null;
  450. }
  451. if (this.m_SuspendedOps != null)
  452. {
  453. this.m_SuspendedOps.Dispose();
  454. this.m_SuspendedOps = null;
  455. }
  456. if (this.m_Current != null)
  457. {
  458. this.m_Current.Dispose();
  459. this.m_Current = null;
  460. }
  461. this.m_RandomColor = null;
  462. this.m_Random = null;
  463. this.m_From = null;
  464. }
  465. #endregion
  466. }
  467. }