「螞蟻線」是圖形編輯器、截圖工具中常見的虛線選取框動畫效果。本文示範如何在 C# WinForms 中,結合 **GDI+ GraphicsPath** 與 **Win32 API LineDDA** 繪製高效能、可自訂顏色/速度的螞蟻線動畫。
PathPoints、PathTypes 取得頂點與段落類型。using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public partial class AntLineForm : Form
{
#region P/Invoke
private delegate void LineDdaProc(int x, int y, IntPtr lpData);
[DllImport("gdi32.dll")]
private static extern int LineDDA(int xStart, int yStart, int xEnd, int yEnd,
LineDdaProc lpLineFunc, IntPtr lpData);
[DllImport("gdi32.dll")]
private static extern int SetPixel(IntPtr hdc, int x, int y, int crColor);
#endregion
#region Constants
private const byte PT_CLOSEFIGURE = 1;
private const byte PT_LINETO = 2;
private const byte PT_BEZIERTO = 4;
private const byte PT_MOVETO = 6;
#endregion
#region Fields
private readonly GraphicsPath _path = new GraphicsPath();
private IntPtr _hdc = IntPtr.Zero;
private int _colorOffset = 0;
private readonly Timer _timer = new Timer { Interval = 100 };
#endregion
public AntLineForm()
{
InitializeComponent();
_timer.Tick += OnTimerTick;
}
private void btnStart_Click(object sender, EventArgs e)
{
// 1. 定義路徑 (這裡示範矩形)
_path.Reset();
_path.AddRectangle(new Rectangle(10, 10, 200, 150));
// 2. 取得裝置內容 (DC)
_hdc = CreateGraphics().GetHdc();
// 3. 啟動動畫
_colorOffset = 0;
_timer.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
if (_hdc == IntPtr.Zero) return;
// 遍歷路徑每一段,呼叫 LineDDA 繪製
for (int i = 0; i < _path.PathPoints.Length; i++)
{
PointF pt1 = _path.PathPoints[i];
PointF pt2;
byte type = _path.PathTypes[i];
if ((type & PT_CLOSEFIGURE) != 0) // 閉合段:連回 MoveTo 點
{
// 找到對應的 MOVETO 點
for (int j = i; j >= 0; j--)
{
if (_path.PathTypes[j] == PT_MOVETO)
{
pt2 = _path.PathPoints[j];
DrawLineDDA((int)pt1.X, (int)pt1.Y, (int)pt2.X, (int)pt2.Y);
break;
}
}
}
else if (i == _path.PathPoints.Length - 1) // 最後一點 (非閉合)
{
pt2 = _path.PathPoints[0];
DrawLineDDA((int)pt1.X, (int)pt1.Y, (int)pt2.X, (int)pt2.Y);
}
else // 一般線段
{
pt2 = _path.PathPoints[i + 1];
DrawLineDDA((int)pt1.X, (int)pt1.Y, (int)pt2.X, (int)pt2.Y);
}
}
}
private void DrawLineDDA(int x1, int y1, int x2, int y2)
{
LineDDA(x1, y1, x2, y2, LineDdaCallback, IntPtr.Zero);
}
private void LineDdaCallback(int x, int y, IntPtr lpData)
{
// 三色循環:白 -> 紅 -> 藍 (模擬螞蟻線移動)
_colorOffset = (_colorOffset + 1) % 15;
Color color = _colorOffset < 5 ? Color.White :
_colorOffset < 12 ? Color.Red : Color.Blue;
// SetPixel 需 BGR 格式 (0x00BBGGRR)
int bgr = color.R | (color.G << 8) | (color.B << 16);
SetPixel(_hdc, x, y, bgr);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
_timer.Stop();
if (_hdc != IntPtr.Zero)
{
ReleaseHdc(_hdc);
_hdc = IntPtr.Zero;
}
_path.Dispose();
base.OnFormClosing(e);
}
#region Helpers
[DllImport("user32.dll")]
private static extern int ReleaseHdc(IntPtr hdc);
#endregion
}
必須符合 void Func(int x, int y, IntPtr data)。委派實例 **_lineDdaProc 必須保持存活** (設為欄位),否則 GC 回收會導致 Crash。
PT_MOVETO (6):起始點。PT_LINETO (2):直線終點。PT_CLOSEFIGURE (1):標記該點需連回本子路徑起點。實際座標仍是線段終點,需自行找回 MOVETO 點畫閉合線。Timer.Interval = 100 (10 FPS) 可改為 50~200ms 視需求調整。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| LineDDA + SetPixel (本文) | 無需雙緩衝、像素級控制、經典做法 | SetPixel 極慢、大路徑會卡頓、高 DPI 模糊 | 教學、簡單工具、小路徑 |
GDI+ Pen.DashStyle = DashStyle.Dash + DashOffset 動畫 | 硬體加速、向量級、高 DPI 完美、程式碼極簡 | 虛線樣式固定、難做多色循環 | **現代專案首選** |
WPF DashArray + DoubleAnimation | 聲明式、GPU 加速、豐富樣式 | 需 WPF 專案 | WPF 應用 |
private readonly Pen _antPen = new Pen(Color.Red, 1) { DashStyle = DashStyle.Dash, DashPattern = new float[] { 4, 2 } };
private float _dashOffset = 0;
private void ModernTimer_Tick(object sender, EventArgs e)
{
_dashOffset += 1f;
if (_dashOffset > 6) _dashOffset = 0; // 4+2=6
_antPen.DashOffset = _dashOffset;
Invalidate(); // 觸發 OnPaint
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(_antPen, 10, 10, 200, 150);
}
**優點**:程式碼 < 10 行、無 P/Invoke、高 DPI 自動縮放、CPU 佔用極低。**缺點**:單色虛線、難做「白紅藍」三色循環。若不需花俏配色,**請直接用此法**。
LineDDA 是 Windows 早期 GDI 遺留的像素級繪圖工具,理解其原理有助於掌握光柵化基礎。但在現代高 DPI、硬體加速環境下,**GDI+ Pen.DashOffset 動畫**或 **WPF 動畫**才是正解。本文提供的 LineDDA 完整範例適合教學、維護舊專案或特殊像素特效需求;新專案請優先採用現代向量繪圖 API。