Column > 【C#,VB】フリーハンドで太い滑らかな線の描画
2016/5/10 【C#,VB】フリーハンドで太い滑らかな線の描画

よくあるペイントソフトみたいな感じで、マウスで線を引けるようなものが作りたいなあと思って作っていたのですが、そのプログラムを実行してみるとなんだか汚い感じになってしまいました。

そのプログラムはこんな感じです。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testCSharp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        bool mouseDrug = false;
        int prevX;
        int prevY;

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            mouseDrug = true;
            prevX = e.Location.X;
            prevY = e.Location.Y;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            mouseDrug = false;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (mouseDrug)
            {
                Pen objPen = new Pen(System.Drawing.Color.Black, 10);
                Graphics objGrp = this.CreateGraphics();
                objGrp.DrawLine(objPen, prevX, prevY, e.Location.X, e.Location.Y);
                prevX = e.Location.X;
                prevY = e.Location.Y;

                objPen.Dispose();
                objGrp.Dispose();
            }
        }
    }
}
				

Public Class Form1

    Private mouseDrug As Boolean = False
    Private prevX As Integer
    Private prevY As Integer

    Private Sub Form1_MouseDown(sender As Object, e As MouseEventArgs) Handles MyBase.MouseDown
        mouseDrug = True
        prevX = e.Location.X
        prevY = e.Location.Y
    End Sub

    Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles MyBase.MouseUp
        mouseDrug = False
    End Sub

    Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove
        If mouseDrug Then
            Dim objPen As New Pen(Color.White, 10)
            Dim objGrp As Graphics = Me.CreateGraphics()
            objGrp.DrawLine(objPen, prevX, prevY, e.Location.X, e.Location.Y)
            prevX = e.Location.X
            prevY = e.Location.Y

            objPen.Dispose()
            objGrp.Dispose()
        End If
    End Sub
    
End Class
				

フォーム上でマウスがクリックされてから、マウスが移動するたびに線を引きつつ前の座標を記録させるというやり方でフリーハンドでの描画が出来るようにしています。

そうして出来たプログラムを動かした結果が下のような動画なのですが、見て分かるように汚い線になってしまいました。



本当は滑らかな線を引きたかったんですが、線に切り込みが入ったようになって見栄えが悪くなってしまいました。

特にカーブをする時に切り込みが入っているように見えます。

線を引いているのでアンチエイリアスをかければ綺麗になるかな?と思ってやってみたんですが、アンチエイリアスは一本の線や図形を描画する際にそのエッジ部分を滑らかにするというものなので、マウスを移動させるたびに何本もの線を引いていく今回のプログラムには特に効果がありませんでした。

この現象が起こる原因は、フリーハンドで太い直線を引いていく場合は下の図のような感じで
線と線の間に隙間が出来るからです。



そのため、下の図の緑色の部分のように線と線の間を補間してあげる必要があります。



今回作るプログラムではマウスの前の座標を中心として、引いている線の太さ
(objPen.width)の直径
を持つ円を描画することで補完することにしました。


そうして直したプログラムが下のような感じです。

上に挙げたプログラムと比べて
c#の場合は43行目、VisualBasicの場合は22行目の一文だけを追加しました。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testCSharp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        bool mouseDrug = false;
        int prevX;
        int prevY;

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            mouseDrug = true;
            prevX = e.Location.X;
            prevY = e.Location.Y;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            mouseDrug = false;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (mouseDrug)
            {
                Pen objPen = new Pen(System.Drawing.Color.Black, 10);
                Graphics objGrp = this.CreateGraphics();
                objGrp.DrawLine(objPen, prevX, prevY, e.Location.X, e.Location.Y);
                objGrp.FillEllipse(Brushes.Black, prevX - objPen.Width / 2, prevY - objPen.Width / 2, objPen.Width, objPen.Width);
                prevX = e.Location.X;
                prevY = e.Location.Y;

                objPen.Dispose();
                objGrp.Dispose();
            }
        }
    }
}
				

Public Class Form1

    Private mouseDrug As Boolean = False
    Private prevX As Integer
    Private prevY As Integer

    Private Sub Form1_MouseDown(sender As Object, e As MouseEventArgs) Handles MyBase.MouseDown
        mouseDrug = True
        prevX = e.Location.X
        prevY = e.Location.Y
    End Sub

    Private Sub Form1_MouseUp(sender As Object, e As MouseEventArgs) Handles MyBase.MouseUp
        mouseDrug = False
    End Sub

    Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove
        If mouseDrug Then
            Dim objPen As New Pen(Color.White, 10)
            Dim objGrp As Graphics = Me.CreateGraphics()
            objGrp.DrawLine(objPen, prevX, prevY, e.Location.X, e.Location.Y)
            objGrp.FillEllipse(Brushes.White, prevX - objPen.Width / 2, prevY - objPen.Width / 2, objPen.Width, objPen.Width)
            prevX = e.Location.X
            prevY = e.Location.Y

            objPen.Dispose()
            objGrp.Dispose()
        End If
    End Sub
    
End Class
				

プログラムの実行結果は下の動画のような感じです。

直す前と比べると、かなり自然な感じの線が引けるようになったかなと思います。





こんな感じで今回のプログラムは完成しましたが、http://blog.hiros-dot.net/?page_id=3771
こんなサイトを見つけました。

DefaultDrawingAttributesというのはよく分からないのですが、
もっと綺麗な線を描く方法もあるのかもしれませんね。



2017/6/28 追記

よりペイントソフトっぽくするために「進む」「戻る」機能をつけてみました
    Please
    Share!
  • feedly
  • facebook
  • twitter
  • hatena bookmark
  • pocket
  • Google plus


inserted by FC2 system