using System;
using System.Drawing;
using System.Windows.Forms;
namespace GifTest {
public partial class Form1 : Form {
private AnimateImage _image;
public Form1() {
InitializeComponent();
_image = new AnimateImage(Image.FromFile(@"C:\Pictures\animation.gif"));
_image.OnFrameChanged += Image_OnFrameChanged;
// 雙緩衝三件套:消除閃爍
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
}
private void Image_OnFrameChanged(object sender, EventArgs e) {
Invalidate(); // 觸發重繪
}
private void Form1_Load(object sender, EventArgs e) {
_image.Play();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
lock (_image.Image) { // 必須鎖定,避免跨執行緒存取衝突
e.Graphics.DrawImage(_image.Image, Point.Empty);
}
}
private void btnToggle_Click(object sender, EventArgs e) {
if (_image.IsPlaying) {
_image.Stop();
btnToggle.Text = "Play";
} else {
_image.Play();
btnToggle.Text = "Stop";
}
Invalidate();
}
private void btnReset_Click(object sender, EventArgs e) {
_image.Reset();
btnToggle.Text = "Play";
Invalidate();
}
}
}
SetStyle(ControlStyles.OptimizedDoubleBuffer | // 雙緩衝
ControlStyles.AllPaintingInWmPaint | // 禁用 WM_ERASEBKGND
ControlStyles.UserPaint, true); // 自繪模式
OptimizedDoubleBuffer:在記憶體位圖繪製完成後一次性複製到螢幕AllPaintingInWmPaint:跳過背景擦除,減少閃爍UserPaint:完全接管繪製,不呼叫預設 OnPaintBackgroundImageAnimator 內部執行緒會周期性呼叫 SelectActiveFrame 修改 Image 物件,而 UI 執行緒在 OnPaint 同時讀取。未加鎖會拋出 InvalidOperationException: Object currently in use elsewhere。
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Image frame;
lock (_image.Image) {
// 複製當前幀,縮短鎖持有時間
frame = (Image)_image.Image.Clone();
}
e.Graphics.DrawImage(frame, Point.Empty);
frame.Dispose();
}
適合高幀率/大尺寸圖片,權衡記憶體配置開銷。
public class AnimateImage {
private readonly Image _image;
private readonly FrameDimension _dimension;
private readonly int _frameCount;
private int _currentFrame;
private bool _playing;
public event EventHandler OnFrameChanged;
public Image Image => _image;
public bool CanAnimate { get; }
public int FrameCount => _frameCount;
public int CurrentFrame => _currentFrame;
public bool IsPlaying => _playing;
public AnimateImage(Image img) {
_image = img;
lock (_image) {
CanAnimate = ImageAnimator.CanAnimate(_image);
if (CanAnimate) {
var guid = _image.FrameDimensionsList[0];
_dimension = new FrameDimension(guid);
_frameCount = _image.GetFrameCount(_dimension);
}
}
}
public void Play() {
if (!CanAnimate || _playing) return;
lock (_image) {
ImageAnimator.Animate(_image, OnFrameChangedHandler);
_playing = true;
}
}
public void Stop() {
if (!CanAnimate || !_playing) return;
lock (_image) {
ImageAnimator.StopAnimate(_image, OnFrameChangedHandler);
_playing = false;
}
}
public void Reset() {
Stop();
lock (_image) {
_image.SelectActiveFrame(_dimension, 0);
_currentFrame = 0;
}
}
private void OnFrameChangedHandler(object sender, EventArgs e) {
lock (_image) {
_currentFrame = (_currentFrame + 1) % _frameCount;
_image.SelectActiveFrame(_dimension, _currentFrame);
}
OnFrameChanged?.Invoke(this, e);
}
}
DrawImage 即時縮放Graphics.InterpolationMode = HighQualityBicubic 平衡品質/速度BufferedGraphics 手動管理緩衝區