2010/05/06(Thu) 13:16:17 編集(投稿者)
2010/05/06(Thu) 11:40:06 編集(投稿者)
■No49422 (kinton さん) に返信
> マウスを動かす速さによって線を太さを変えた場合(筆のように)、
> サイズが1から5など飛んだ数字になるとなめらかに太くならず。ガタガタの線になってしまいます。
1の線を引いて、5の線を引いたらガタガタになりますね。
1の線は既に引かれているので、5の線を引くときに、既に引いた1の線の半分くらいの部分に対して、
2の線、3の線、4の線と上書きで滑らかになるように間を補完する線を引く必要があるのではないでしょうか。
> C++ならMoveToEx()とLineTo()を使うとうまく行くのですが。
理論的にはこちらでもうまく行かないのでは?と思うのですが、
そうでもないのですかねぇ。
(MoveToExやLineToは使ったことがないので詳しくは知りませんが)
> C#の場合綺麗な線を引くにはどうすればいいのでしょうか。
提示のコードをもとに実験してみました。
実験してみた限りではそこそこ綺麗に書けていると思うので、
以下に変更点を記載します。
※考え方としては、数個分の点の履歴を保管しておき、
過去に遡って新しいペンのサイズで連続した線として描画しなおしています。
※新しいサイズで過去に遡って描画しなおしているだけの簡易的なものなので、
前回のペンサイズとの間のペンサイズを使って補完するような方法なら、
もっと滑らかになるのではないかなと思います。
> public partial class Form1 : Form
> {
> private Image image;
> private Color color;
> Point pic_old, pic_new;
> Brush brush;
> int size = 1;
> int move_x;
> int move_y;
> private Size es;
Queue<Point> oldPoints; // 点の履歴
> protected override void OnPaint(PaintEventArgs e)
> {
> base.OnPaint(e);
>
> if (image != null)
> {
> e.Graphics.DrawImage(image, 0, 0);
> }
> }
>
> private void PenSize()
> {
> // ペンのサイズ
> // 筆ペン
> move_x = Math.Abs(pic_new.X - pic_old.X);
> move_y = Math.Abs(pic_new.Y - pic_old.Y);
> size = move_x + move_y;
> es = new Size(size, size);
> }
>
> protected override void OnMouseMove(MouseEventArgs e)
> {
> base.OnMouseMove(e);
> if (e.Button == MouseButtons.Left)
> {
> pic_old = pic_new;
> pic_new.X = e.X;
> pic_new.Y = e.Y;
> // ペンのサイズを決定
> PenSize();
>
> Pen m_pen = new Pen(color, es.Width);
// 線の両端や結合点を丸く描画するための設定
m_pen.StartCap = LineCap.Round;
m_pen.EndCap = LineCap.Round;
m_pen.LineJoin = LineJoin.Round;
>
> Graphics g = Graphics.FromImage(image);
> g.SmoothingMode = SmoothingMode.AntiAlias;
> Brush brush = new SolidBrush(color);
>
> // 線の描画
> g.FillEllipse(
> brush, e.X - (es.Width / 2), e.Y - (es.Height / 2),
> es.Width, es.Height
> );
>
> g.DrawLine(m_pen, pic_old, pic_new);
// 上のFillEllipseやDrawLineの呼び出し箇所はコメントアウトしてください。
oldPoints.Enqueue(pic_new);
if (oldPoints.Count > 10) // 10の部分を適当に調整してください。
{
oldPoints.Dequeue();
}
g.DrawLines(m_pen, oldPoints.ToArray());
> }
> Invalidate();
> }
>
> public Form1()
> {
> InitializeComponent();
>
> this.SetStyle(
> ControlStyles.DoubleBuffer | // 描画をバッファで実行する
> ControlStyles.UserPaint | // 描画は(OSでなく)独自に行う
> ControlStyles.AllPaintingInWmPaint, // WM_ERASEBKGND を無視する
> true // 指定したスタイルを適用「する」
> );
> color = Color.Black;
> image = new Bitmap(700, 300);
>
> Graphics g = Graphics.FromImage(image);
> brush = new SolidBrush(Color.White);
> g.FillRectangle(brush, 0, 0, image.Width, image.Height);
> brush = new SolidBrush(Color.Black);
oldPoints = new Queue<Point>();
> }
>
> private void Form1_MouseDown(object sender, MouseEventArgs e)
> {
> pic_old.X = pic_new.X = e.X;
> pic_old.Y = pic_new.Y = e.Y;
oldPoints.Enqueue(pic_new);
> }
>
> private void Form1_MouseUp(object sender, MouseEventArgs e)
> {
> pic_old.X = pic_new.X = e.X;
> pic_old.Y = pic_new.Y = e.Y;
// 一度筆を置いたら、履歴はクリア
oldPoints.Clear();
> }
>
> }