[C#]GDI+在鼠标移动(MouseMove)时画平滑曲线

时间:2023-01-28 20:38:08

关于平滑曲线,相信很多人在网上提过这个问题,如何画出平滑曲线?我也很想知道答案,究竟如何才能在MouseMove事件里画出真正平滑又柔和的曲线?注意本文讨论的重点是在MouseMove事件里,并非相对固定的几个点
针对这个问题,网上给出了很多种方案。看到最多的是用类似gdi的moveto,lineto实现,gdi+则是drawline,但这个方案有一个很明显的漏洞:在MouseMove事件里,当用户快速移动鼠标时,我们可以很明显的看到画出的曲线是由直线段拼接而成,一点都不圆滑,这和我们的主题相差太远,所以否决这种方案。
当然网上也给出了另一种方案:DrawCurve
第一次看到这个方法,我就像发现新大陆一样,因为网上给出的效果图确实非常圆滑。
但很可惜,实践证明,用DrawCurve方法在相对固定的几个点下画曲线确实平滑,但一旦移到MouseMove事件里却出现了各种各样的问题,比如锯齿,“毛刺”等,下图就非常直观的呈现出了这些特征

[C#]GDI+在鼠标移动(MouseMove)时画平滑曲线

有问题理应要有相应的解决方案,很荣幸,我们找到了Pen的线帽(StartCap/EndCap)和联接样式(LineJoin)两个属性,这两个恰恰是刚刚提到那两个问题的“克星”,线帽用来对付锯齿,而联接样式则是用来对付“毛刺”,只要分别设置这三个属性为Round即可,缺一不可哈:)

[C#]GDI+在鼠标移动(MouseMove)时画平滑曲线

未设置线帽的效果

 

[C#]GDI+在鼠标移动(MouseMove)时画平滑曲线

未设置联接样式的效果

 

[C#]GDI+在鼠标移动(MouseMove)时画平滑曲线

只有三个属性设全了,才能画出“真正”完美的平滑曲线

 

附上测试代码,有兴趣的读者不妨一试

    public partial class Form1 : Form
    {
        private bool initial = true, startDraw;
        List<Point> pList = new List<Point>();

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (initial) //窗体刚加载时不重绘
            {
                return;
            }
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            using (Pen p = new Pen(Color.Black, 15))
            {
                //设置起止点线帽
                p.StartCap = LineCap.Round;
                p.EndCap = LineCap.Round;

                //设置连续两段的联接样式
                p.LineJoin = LineJoin.Round;

                g.DrawCurve(p, pList.ToArray()); //画平滑曲线
            }
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                initial = false;
                startDraw = true;
                pList.Add(e.Location);
            }
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && startDraw)
            {
                pList.Add(e.Location);
                this.Refresh();
            }
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                startDraw = false;
            }
        }
    }