【C#】【Form】PictureBox [5] ~ PictureBox 内に画像を描画する ~

■ はじめに

 * PictureBox 内に画像を描画する方法を調べてみた

PictureBox 内に画像を描画するには...

 * DrawImage() で描画する

■ サンプル

 * 以下のサンプルを載せる

 【1】PictureBoxの画像内をクリックした点に、画像を配置
 【2】 【1】の発展版・2点間を指定し距離を描画
 【おまけ】黒い画像内画像を描画し、PictureBox内に表示する

【1】PictureBoxの画像内をクリックした点に、画像を配置

 * ボタン押下で、クリアされる
コード
using SampleForm.Properties;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    private Image sourceImage;
    private List<Point> targetPoints = new List<Point>();

    public Form1()
    {
      InitializeComponent();
      this.sourceImage = this.pictureBox1.Image;
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Left)
      {
        return;
      }

      var pictureBox = sender as PictureBox;
      var image = Resources.Mark;
      var targetPoint = new Point(e.X - (image.Width / 2), e.Y - (image.Height / 2));
      this.targetPoints.Add(targetPoint);
      this.PaintImageOnPictureBox(pictureBox, image, targetPoint);
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
      var pictureBox = sender as PictureBox;
      foreach (var targetPoint in this.targetPoints)
      {
        this.PaintImageOnPictureBox(pictureBox, Resources.Mark, targetPoint);
      }
    }

    private void PaintImageOnPictureBox(PictureBox pictureBox, Bitmap image, Point point)
    {
      using (var graphics = pictureBox.CreateGraphics())
      {
        graphics.DrawImage(image, point);
      }
    }

    // クリア処理
    private void button1_Click(object sender, System.EventArgs e)
    {
      this.targetPoints.Clear();
      this.pictureBox1.Image = this.sourceImage;
    }
  }
}

【2】 【1】の発展版・2点間を指定し距離を描画

コード
using SampleForm.Properties;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    private readonly Pen LinePen = new Pen(Color.AliceBlue, 3);
    private readonly Font LengthFont = new Font("メイリオ", 10);
    private Image sourceImage;
    private List<Point> targetPoints = new List<Point>();

    public Form1()
    {
      InitializeComponent();
      this.sourceImage = this.pictureBox1.Image;
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Left)
      {
        return;
      }

      if (this.targetPoints.Count >= 2)
      {
        this.Clear();
        return;
      }
      else
      {
        var pictureBox = sender as PictureBox;
        var image = Resources.Mark;
        var targetPoint = new Point(e.X, e.Y);
        this.targetPoints.Add(targetPoint);
        if (this.targetPoints.Count == 1)
        {
          this.PaintImageOnPictureBoxForMouseDown(pictureBox, image, targetPoint);
          return;
        }
        else if (this.targetPoints.Count == 2)
        {
          this.PaintImageOnPictureBoxForMouseDown(pictureBox, image, this.targetPoints[0], targetPoint);
          return;
        }
      }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
      var pictureBox = sender as PictureBox;
      if (this.targetPoints.Count == 1)
      {
        var image = Resources.Mark;
        this.PaintImageOnPictureBoxForPaint(pictureBox, image, this.targetPoints[0]);
        return;
      }
      else if (this.targetPoints.Count == 2)
      {
        var image = Resources.Mark;
        this.PaintImageOnPictureBoxForPaint(pictureBox, image, this.targetPoints[0], this.targetPoints[1]);
        return;
      }
    }

    private void PaintImageOnPictureBoxForMouseDown(PictureBox pictureBox, Bitmap image,
      Point startPoint, Point? endPoint = null)
    {
      using (var graphics = pictureBox.CreateGraphics())
      {
        if (endPoint == null)
        {
          // 開始点
          graphics.DrawImage(image, startPoint.X - (image.Width / 2), startPoint.Y - (image.Height / 2));
        }
        else
        {
          var endPointValue = endPoint.Value;

          // 終了点
          graphics.DrawImage(image, 
            endPointValue.X - (image.Width / 2), endPointValue.Y - (image.Height / 2));

          // 開始点と終了点との間の線
          graphics.DrawLine(LinePen, startPoint, endPointValue);

          // 開始点と終了点との間の距離
          var length = this.GetLength(startPoint, endPointValue);
          var lengthPointToDraw = this.CalculatePointToDraw(
            graphics, LengthFont, length, startPoint, endPointValue);
          graphics.DrawString(length, LengthFont, Brushes.Blue, lengthPointToDraw);
        }
      }
    }

    private void PaintImageOnPictureBoxForPaint(PictureBox pictureBox, Bitmap image,
      Point startPoint, Point? endPoint = null)
    {
      using (var graphics = pictureBox.CreateGraphics())
      {
        // 開始点
        graphics.DrawImage(image, startPoint.X - (image.Width / 2), startPoint.Y - (image.Height / 2));


        if (endPoint != null)
        {
          var endPointValue = endPoint.Value;

          // 終了点
          graphics.DrawImage(image,
            endPointValue.X - (image.Width / 2), endPointValue.Y - (image.Height / 2));

          // 開始点と終了点との間の線
          graphics.DrawLine(LinePen, startPoint, endPointValue);

          // 開始点と終了点との間の距離
          var length = this.GetLength(startPoint, endPointValue);
          var lengthPointToDraw = this.CalculatePointToDraw(
            graphics, LengthFont, length, startPoint, endPointValue);
          graphics.DrawString(length, LengthFont, Brushes.Blue, lengthPointToDraw);
        }
      }
    }
    /// <summary>
    /// 文字列描画のための座標の計算
    /// </summary>
    /// <param name="graphics">Graphicsオブジェクト</param>
    /// <param name="text">文字列</param>
    /// <param name="start">開始点</param>
    /// <param name="end">終了点</param>
    /// <returns>文字列描画のための座標</returns>
    /// <remarks>文字列を描画したときの大きさを計測する</remarks>
    /// <see cref="">https://dobon.net/vb/dotnet/graphics/measurestring.html"/>
    private Point CalculatePointToDraw(Graphics graphics, Font font, String text, Point start, Point end)
    {
      var middlePoint = this.CalculateMiddlePoint(start, end);

      var slope = this.CalculateSlope(start, end);
      if (slope >= 0)
      {
        const int Offset = 2;
        var textSize = graphics.MeasureString(text, font);
        return new Point((int)(middlePoint.X + Offset), (int)(middlePoint.Y - textSize.Height));
      }
      else
      {
        const int Offset = 2;
        return new Point(middlePoint.X + Offset, middlePoint.Y + Offset);
      }
    }

    /// <summary>
    /// 2点か間の傾き計算
    /// </summary>
    /// <param name="start">開始点</param>
    /// <param name="end">終了点</param>
    /// <returns>2点間の傾き</returns>
    private double CalculateSlope(Point start, Point end)
    {

      var xDiff = (double)end.X - start.X;
      if (xDiff == 0)
      {
        return 0;
      }

      return (end.Y - start.Y) / xDiff;
    }

    /// <summary>
    /// 2点か間の中点計算
    /// </summary>
    /// <param name="start">開始点</param>
    /// <param name="end">終了点</param>
    /// <returns>2点間の中点</returns>
    private Point CalculateMiddlePoint(Point start, Point end)
    {
      return new Point((end.X + start.X) / 2, (end.Y + start.Y) / 2);
    }

    /// <summary>
    /// 2点か間の距離計算
    /// </summary>
    /// <param name="start">開始点</param>
    /// <param name="end">終了点</param>
    /// <returns>2点間の距離</returns>
    private double CalculateLength(Point start, Point end)
    {
      return Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
    }

    /// <summary>
    /// 2点か間の距離
    /// </summary>
    /// <param name="start">開始点</param>
    /// <param name="end">終了点</param>
    /// <returns>
    /// 2点間の距離 (小数点第一位で出力。小数点第二位から四捨五入。
    /// ex1:123456.14 => 123456.1, ex2::123456.15 => 123456.2)
    /// </returns>
    private String GetLength(Point start, Point end)
    {
      var length = this.CalculateLength(start, end);
      return string.Format("{0:f1}", length);
    }

    // クリア処理
    private void button1_Click(object sender, System.EventArgs e)
    {
      this.Clear();
    }

    private void Clear()
    {
      this.targetPoints.Clear();
      this.pictureBox1.Image = this.sourceImage;
    }
  }
}

【おまけ】黒い画像内画像を描画し、PictureBox内に表示する

using SampleForm.Properties;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form4 : Form
  {
    public Form4()
    {
      InitializeComponent();
    }

    private void Form4_Load(object sender, EventArgs e)
    {
      var canvas = new Bitmap(400, 400);

      using (var graphics = Graphics.FromImage(canvas))
      {
        // 全体を黒で塗りつぶす
        graphics.FillRectangle(Brushes.Black, graphics.VisibleClipBounds);

        // その中に画像を突っ込む
        var innerImage = Resources._1;
        graphics.DrawImage(innerImage, new Point(100, 130));
      }

      // 作成した画像を表示する
      this.pictureBox1.Image = canvas;
    }
  }
}


関連記事

Windows Form ~ 目次 ~

https://blogs.yahoo.co.jp/dk521123/8054245.html

Graphics ~ さまざまな描画 ~

https://blogs.yahoo.co.jp/dk521123/32877749.html

PictureBox

PictureBox [1] ~ 画像を表示する ~
https://blogs.yahoo.co.jp/dk521123/23504075.html
PictureBox [2] ~ PictureBox を マウスで移動する ~
https://blogs.yahoo.co.jp/dk521123/37861699.html
PictureBox [3] ~ マウスホイール で画像の拡大・縮小する ~
https://blogs.yahoo.co.jp/dk521123/37866101.html
PictureBox [4] ~ PictureBox 内に文字列を描画する ~
https://blogs.yahoo.co.jp/dk521123/37890831.html