LoginSignup
3
1

More than 1 year has passed since last update.

windows form 画像を丸く切り抜く

Last updated at Posted at 2020-02-10

完成形

trim_image.gif

このフォームの機能

・左の写真はドラッグで移動
・TrackBarで左の写真の拡縮(写真の中心を軸に)
・Dpi対応をいれてある(解像度の違いでずれないよう)
・画質の劣化を防ぐため、原寸大の画像を裏で持っておき、そこから切り出して右に表示している

各コントロールの用意

画面上にpictureBoxを2つとTrackBarを一つ置きます。

デザイナー上でTrackBarのMaximamSizeを200くらいにしておきます。

ソースコード

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

namespace WindowsFormsApp
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// 切り出す画像サイズ
        /// </summary>
        private Size clipSize = new Size(200, 200);

        /// <summary>
        /// 表示する画像
        /// </summary>
        private Bitmap currentImage;

        /// <summary>
        /// 倍率,位置変更後の画像のサイズと位置
        /// </summary>
        private Rectangle drawRectangle;

        /// <summary>
        /// 画像劣化を防ぐため、出力用の原寸大の画像のサイズと位置
        /// </summary>
        private Rectangle drawDpiRectangle;

        /// <summary>
        /// 画像を移動中か
        /// </summary>
        private bool isDraging = false;

        /// <summary>
        /// 移動前の位置
        /// </summary>
        private Point? diffPoint = null;

        private int dpi = 96;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.pictureBox1.AllowDrop = true;
            this.dpi = (int)this.CreateGraphics().DpiX / 96;
        }

        private void pictureBox1_DragDrop(object sender, DragEventArgs e)
        {
            string fileName = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];

            // 画像の設定
            this.currentImage = new Bitmap(fileName);

            //中央に設定
            this.drawRectangle = GetPictureBoxCenterRect(this.currentImage.Size);
            this.drawDpiRectangle = GetPictureBoxCenterRect(this.CalcDpiSize(this.currentImage.Size, true));

            // 初期拡大率の設定
            var barCenterValue = (int)((this.trackBar1.Maximum - this.trackBar1.Minimum) / 2);
            this.trackBar1.Value = (int)(barCenterValue * this.GetInitialSizePer());
            this.ChangeTrackBar();

            // Invalidate実行後のOnPaintイベント後に1度だけトリミング処理
            PaintEventHandler handler = null;
            handler = (sender2, e2) =>
            {
                TrimPicture();
                this.pictureBox1.Paint -= handler;
            };
            this.pictureBox1.Paint += handler;
            this.pictureBox1.Invalidate();
        }

        /// <summary>
        /// 画像をトリムします
        /// </summary>
        private void TrimPicture()
        {
            if (this.currentImage == null)
            {
                return;
            }

            Bitmap canvas = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height);
            Graphics g = Graphics.FromImage(canvas);

            // dpiの関係で元画像の200*200から画像を切り出さないとぼやけるためcurrentImage
            g.DrawImage(this.currentImage, this.drawDpiRectangle);
            g.Dispose();

            // 切り取る位置を真ん中にする
            var clipRect = this.GetPictureBoxCenterRect(this.clipSize);

            var resultImage = this.TrimToBitmap(canvas, this.clipSize, clipRect);

            // プレビューに表示
            this.DrawCustomerRoundImage(this.pictureBox2, resultImage);
        }

        /// <summary>
        /// 画像の一部を切り取った画像を返す
        /// </summary>
        /// <param name="image">元画像</param>
        /// <param name="size">描画サイズ</param>
        /// <param name="rect">切り取り範囲</param>
        /// <returns描画サイズの画像></returns>
        public Bitmap TrimToBitmap(Image image, Size size, Rectangle rect)
        {
            //描画先とするImageオブジェクトを作成する
            Bitmap canvas = new Bitmap(size.Width, size.Height);
            //ImageオブジェクトのGraphicsオブジェクトを作成する
            Graphics g = Graphics.FromImage(canvas);

            //切り取る部分の範囲を決定する
            Rectangle srcRect = rect;

            //描画する部分の範囲を決定する
            Rectangle desRect = new Rectangle(0, 0, size.Width, size.Height);

            //画像の一部を描画する
            g.DrawImage(image, desRect, srcRect, GraphicsUnit.Pixel);

            //Graphicsオブジェクトのリソースを解放する
            g.Dispose();

            return canvas;
        }

        /// <summary>
        /// 円形の画像を表示する
        /// </summary>
        /// <param name="pictureBox">ピクチャーボックス</param>
        /// <param name="image">画像</param>
        public void DrawCustomerRoundImage(PictureBox pictureBox, Image image)
        {
            if (image == null)
            {
                pictureBox.Image = null;
                return;
            }
            //描画先とするImageオブジェクトを作成する
            Bitmap canvas = new Bitmap(pictureBox.Width, pictureBox.Height);
            //ImageオブジェクトのGraphicsオブジェクトを作成する
            Graphics g = Graphics.FromImage(canvas);

            //楕円の領域を追加する
            GraphicsPath gp = new GraphicsPath();
            gp.AddEllipse(g.VisibleClipBounds);

            //Regionを作成する
            Region rgn = new Region(gp);

            //クリッピング領域を変更する
            g.Clip = rgn;

            //画像を表示する
            g.DrawImage(image, g.VisibleClipBounds);

            //リソースを解放する
            g.Dispose();

            //PictureBox2に表示する
            pictureBox.Image = canvas;
        }

        private void ChangeTrackBar()
        {
            if (this.currentImage == null)
            {
                return;
            }
            var zoomRatio = this.GetZoomRatio();

            //倍率変更後の画像のサイズと位置を計算する
            // 中心を軸に拡縮する
            var diffWidth = (int)Math.Round(this.currentImage.Width * zoomRatio) - this.drawRectangle.Width;
            this.drawRectangle.Width += diffWidth;
            this.drawRectangle.X -= diffWidth / 2;

            var diffHeight = (int)Math.Round(this.currentImage.Height * zoomRatio) - this.drawRectangle.Height;
            this.drawRectangle.Height += diffHeight;
            this.drawRectangle.Y -= diffHeight / 2;


            // 原寸大の画像のRect
            diffWidth = this.CalcDpiSize(diffWidth, true);
            this.drawDpiRectangle.Width += diffWidth;
            this.drawDpiRectangle.X -= diffWidth / 2;

            diffHeight = this.CalcDpiSize(diffHeight, true);
            this.drawDpiRectangle.Height += diffHeight;
            this.drawDpiRectangle.Y -= diffHeight / 2;

            //画像を表示する
            this.pictureBox1.Invalidate();
        }

        /// <summary>
        /// TrackBarの値に対応する拡大率を取得
        /// </summary>
        /// <returns>拡大率</returns>
        private double GetZoomRatio()
        {
            // barの中心で1になるよう2倍
            return 2 * (double)(this.trackBar1.Value) / (double)(this.trackBar1.Maximum - this.trackBar1.Minimum);
        }

        /// <summary>
        /// 画像の初期表示サイズ率を取得します
        /// </summary>
        /// <returns>画像の初期表示サイズ率</returns>
        private double GetInitialSizePer()
        {
            if (this.currentImage.Width < this.pictureBox1.Width
                && this.currentImage.Height < this.pictureBox1.Height)
            {
                return 1;
            }

            // 縦横、サイズが大きいほうの割合に合わせる
            if (this.currentImage.Width > this.currentImage.Height)
            {
                return (double)this.pictureBox1.Width / (double)this.currentImage.Width;
            }
            else
            {
                return (double)this.pictureBox1.Height / (double)this.currentImage.Height;
            }
        }

        /// <summary>
        /// pictureBox1のセンターRectを取得
        /// </summary>
        /// <returns>初期画像Rect</returns>
        private Rectangle GetPictureBoxCenterRect(Size size)
        {
            var x = (int)(((double)this.pictureBox1.Width / 2) - ((double)size.Width / 2));
            var y = (int)(((double)this.pictureBox1.Height / 2) - ((double)size.Height / 2));
            return new Rectangle(x, y, size.Width, size.Height);
        }

        public int CalcDpiSize(int value, bool IsDivision)
        {
            if (IsDivision)
            {
                return (int)(value / this.dpi);
            }
            else
            {
                return (int)(value * this.dpi);
            }
        }

        public Size CalcDpiSize(Size value, bool IsDivision)
        {
            if (IsDivision)
            {
                return new Size((int)(value.Width / this.dpi), (int)(value.Height / this.dpi));
            }
            else
            {
                return new Size((int)(value.Width * this.dpi), (int)(value.Height * this.dpi));
            }
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            this.ChangeTrackBar();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (this.currentImage == null)
            {
                return;
            }

            // PictureBoxサイズの画像を作成
            Bitmap canvas = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height);
            Graphics g = Graphics.FromImage(canvas);

            //画像を指定された位置、サイズでイメージを保持する
            g.DrawImage(this.currentImage, this.drawRectangle);
            g.Dispose();

            //画像を指定された位置、サイズで描画する
            e.Graphics.DrawImage(this.currentImage, this.drawRectangle);

            GraphicsPath path = new GraphicsPath();
            // 切り出す範囲を四角を取得
            Rectangle srcRect = this.GetPictureBoxCenterRect(this.clipSize);

            // 四角に沿った円形を追加
            path.AddEllipse(srcRect);

            // 円に沿ってpenで描画
            Pen pen = new Pen(Color.FromArgb(70, 255, 0, 0), 5);
            e.Graphics.DrawPath(pen, path);
        }

        private void pictureBox1_DragEnter(object sender, DragEventArgs e)
        {
            // ドラッグアイコンの変更
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            Cursor.Current = Cursors.Hand;
            this.isDraging = true;
            this.diffPoint = e.Location;
        }

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

            this.drawRectangle.X += e.X - this.diffPoint.Value.X;
            this.drawRectangle.Y += e.Y - this.diffPoint.Value.Y;

            // 原寸大の画像の座標
            this.drawDpiRectangle.X += this.CalcDpiSize(e.X - this.diffPoint.Value.X, true);
            this.drawDpiRectangle.Y += this.CalcDpiSize(e.Y - this.diffPoint.Value.Y, true);

            this.diffPoint = new Point(e.X, e.Y);
            this.pictureBox1.Invalidate();
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            Cursor.Current = Cursors.Default;
            this.isDraging = false;
            TrimPicture();
        }

        private void trackBar1_MouseUp(object sender, MouseEventArgs e)
        {
            TrimPicture();
        }
    }

ソース
https://github.com/shinnosukekubo/WindowsForms_ImageTrim

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1