【C#】【Form】PictureBox [9] ~ 画像を任意の角度で回転させる ~

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37853430.html
の続き。

画像を任意の角度で回転させる方法

画像を任意の角度で回転させるために、以下の方法がある

[1] Graphics.RotateTransform()を使う
 => 以下の「■ サンプル」の「例1」を参照

[2] Matrix(アフィン変換)クラスを利用する
 => 以下の「■ サンプル」の「例2」を参照

[3] アフィン変換などで自作する
 => 以下の関連記事を参照のこと
https://blogs.yahoo.co.jp/dk521123/38093149.html

■ サンプル

例1:Graphics.RotateTransform()を使う方法

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    /// <summary>
    /// オリジナルのビットマップ
    /// </summary>
    private Bitmap originalBitmap = null;
    /// <summary>
    /// 現在の回転角度
    /// </summary>
    private float currentAngle = 0f;

    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      // 画像ファイルのImageオブジェクトを作成する
      this.originalBitmap = new Bitmap(@"20161215052204.gif");
      this.pictureBox1.Image = this.originalBitmap;
    }

    private void button1_Click(object sender, EventArgs e)
    {
      var image = this.pictureBox1.Image;
      if (image != null &&
        this.originalBitmap != null && 
        this.originalBitmap != image)
      {
        Console.WriteLine("オリジナル以外の回転画像をDispose");
        image.Dispose();
      }

      // 角度決める
      if (this.checkBox1.Checked)
      {
        // 逆回転
        this.currentAngle = this.currentAngle - 5.0f;
      }
      else
      {
        // 回転
        this.currentAngle = this.currentAngle + 5.0f;
      }
      
      // 回転する
      this.pictureBox1.Image = this.Rotate(
        this.originalBitmap, this.currentAngle, this.originalBitmap.Width / 2, this.originalBitmap.Height / 2);
    }

    /// <summary>
    /// ビットマップ(Bitmap)を回転する
    /// </summary>
    /// <param name="targetBitmap">ビットマップ</param>
    /// <param name="angle">回転角度</param>
    /// <param name="x">中心点X</param>
    /// <param name="y">中心点Y</param>
    /// <returns></returns>
    public Bitmap Rotate(Bitmap targetBitmap, float angle, int x, int y)
    {
      Bitmap clonedBitmap = new Bitmap(targetBitmap.Width, targetBitmap.Height);
      using (var graphics = Graphics.FromImage(clonedBitmap))
      {
        // まずは背景色を黒くする
        graphics.Clear(Color.Black);

        // ★回転させる★
        graphics.TranslateTransform(-x, -y);
        // ★ここ★
        graphics.RotateTransform(angle, MatrixOrder.Append);
        graphics.TranslateTransform(x, y, MatrixOrder.Append);
	
        // 高品質双三次補間を指定
        graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;

        // 描画(指定された位置に元の物理サイズで描画)
        graphics.DrawImageUnscaled(targetBitmap, 0, 0);
      }

      return clonedBitmap;
    }
  }
}

例2:Matrix(アフィン変換)クラスを利用する

 * 以下の関連記事で行ったサンプルに回転処理を追加する
https://blogs.yahoo.co.jp/dk521123/37866101.html
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace ImageViewer
{
  public partial class Form1 : Form
  {
    // ★アフィン変換 ★
    private Matrix affineTransformation = new Matrix();
    private RectangleF sourceRectangle;
    private PointF[] sourcePoints = new PointF[3];
    private Graphics graphics = null;
    private Image image = null;
    private Bitmap bitmap;
    private Point oldPoint = Point.Empty;

    private bool IsDragging
    {
      get
      {
        return !this.oldPoint.IsEmpty;
      }
    }

    public Form1()
    {
      InitializeComponent();

      // ホイールイベントの追加  
      this.pictureBox1.MouseWheel
          += new MouseEventHandler(this.pictureBox1_MouseWheel);
    }

    private void Form1_Load(object sender, System.EventArgs e)
    {
      this.Resize();
      this.OpenImageFile(@"20161215052204.gif");
    }

    // マウスホイールイベント  
    private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
    {
      this.affineTransformation.Translate(-e.X, -e.Y, MatrixOrder.Append);

      if (e.Delta > 0)
      {
        // 拡大  
        if (this.affineTransformation.Elements[0] < 100) 
        {
          this.affineTransformation = this.GetAffineTransformationToScale(
            this.affineTransformation, 1.5f, e.Location);
        }
      }
      else
      {
        // 縮小  
        if (this.affineTransformation.Elements[0] > 0.01) 
        {
          this.affineTransformation = this.GetAffineTransformationToScale(
            this.affineTransformation, 1.0f / 1.5f, e.Location);
        }
      }

      this.affineTransformation.Translate(e.X, e.Y, MatrixOrder.Append);
    }

    private Matrix GetAffineTransformationToScale(
      Matrix matrix, float scale, Point point)
    {
      // 原点へ移動
      matrix.Translate(-point.X, -point.Y, MatrixOrder.Append);
      // 拡大縮小
      matrix.Scale(scale, scale, MatrixOrder.Append);
      // 元へ戻す
      matrix.Translate(point.X, point.Y, MatrixOrder.Append);

      return matrix;
    }

    private void OpenImageFile(string fileName)
    {
      if (string.IsNullOrEmpty(fileName))
      {
        return;
      }

      if (this.image != null)
      {
        this.image.Dispose();
      }
      if (this.bitmap != null)
      {
        this.bitmap.Dispose();
      }
      this.image = Image.FromFile(fileName);
      this.bitmap = new Bitmap(this.image);

      this.sourceRectangle = 
        new RectangleF(-0.5f, -0.5f, this.image.Width, this.image.Height);
      this.sourcePoints[0] =
        new PointF(this.sourceRectangle.Left, this.sourceRectangle.Top);
      this.sourcePoints[1] =
        new PointF(this.sourceRectangle.Right, this.sourceRectangle.Top);
      this.sourcePoints[2] =
        new PointF(this.sourceRectangle.Left, this.sourceRectangle.Bottom);

      this.DrawImage();
    }

    private void Resize()
    {
      if ((this.pictureBox1.Width == 0) || 
          (this.pictureBox1.Height == 0))
      {
        return;
      }

      var bmpPicBox = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height);
      this.pictureBox1.Image = bmpPicBox;

      this.graphics = Graphics.FromImage(this.pictureBox1.Image);
      this.graphics.InterpolationMode = InterpolationMode.NearestNeighbor;

      this.DrawImage();
    }

    // ビットマップの描画
    private void DrawImage()
    {
      if (this.image == null)
      {
        return;
      }
      if (this.bitmap == null)
      {
        return;
      }

      this.graphics.Clear(this.pictureBox1.BackColor);

      var destinationPoints = (PointF[])this.sourcePoints.Clone();
      this.affineTransformation.TransformPoints(destinationPoints);

      this.graphics.DrawImage(this.bitmap,
          destinationPoints, this.sourceRectangle, GraphicsUnit.Pixel);

      this.pictureBox1.Refresh();
    }

    private void pictureBox1_Resize(object sender, System.EventArgs e)
    {
      this.Resize();
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
      this.pictureBox1.Focus();

      this.oldPoint.X = e.X;
      this.oldPoint.Y = e.Y;
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
      if (!this.IsDragging)
      {
        return;
      }

      // 移動
      this.affineTransformation.Translate(
        e.X - this.oldPoint.X, e.Y - this.oldPoint.Y, MatrixOrder.Append);

      this.DrawImage();

      this.oldPoint.X = e.X;
      this.oldPoint.Y = e.Y;
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
      this.oldPoint = Point.Empty;
    }

    // ★ここを追加★
    private void button1_Click(object sender, System.EventArgs e)
    {
      this.graphics.RotateTransform(
        float.Parse(this.textBox1.Text), MatrixOrder.Append);
      this.DrawImage();
    }
  }
}

■ 補足:画像回転ではみ出さないように補正するには...

 * 以下の関連記事を参照。
https://blogs.yahoo.co.jp/dk521123/38061211.html


関連記事

Windows Form

Windows Form ~ 目次 ~
https://blogs.yahoo.co.jp/dk521123/8054245.html
PictureBox [3] ~ マウスホイール で画像の拡大・縮小する ~
https://blogs.yahoo.co.jp/dk521123/37866101.html
PictureBox [10] ~ 画像をレイヤー構造で扱う ~
https://blogs.yahoo.co.jp/dk521123/38055633.html

画像処理

画像処理 ~ 回転 ~
https://blogs.yahoo.co.jp/dk521123/37853430.html
画像処理 ~ アフィン変換・Matrixクラス ~
https://blogs.yahoo.co.jp/dk521123/38061211.html
画像処理 ~ アフィン変換で任意角度の回転を自作する ~
https://blogs.yahoo.co.jp/dk521123/38093149.html