첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ GraphicsExtension.cs

using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 그래픽스 확장
    /// </summary>
    public static class GraphicsExtension
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 사각형 그리기 - DrawRectangle(graphics, pen, rectangle)

        /// <summary>
        /// 사각형 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="pen">펜</param>
        /// <param name="rectangle">사각형</param>
        public static void DrawRectangle(this Graphics graphics, Pen pen, RectangleF rectangle)
        {
            graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
        }

        #endregion
        #region 상자 그리기 - DrawBox(graphics, brush, pen, centerPoint, radius)

        /// <summary>
        /// 상자 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="brush">브러시</param>
        /// <param name="pen">펜</param>
        /// <param name="centerPoint">중심 포인트</param>
        /// <param name="radius">반경</param>
        public static void DrawBox(this Graphics graphics, Brush brush, Pen pen, PointF centerPoint, float radius)
        {
            RectangleF rectangle = new RectangleF
            (
                centerPoint.X - radius,
                centerPoint.Y - radius,
                2 * radius,
                2 * radius
            );

            graphics.FillRectangle(brush, rectangle);

            graphics.DrawRectangle(pen, rectangle);
        }

        #endregion
        #region 이미지 확대/축소하기 - ScaleImage(sourceBitmap, scale)

        /// <summary>
        /// 이미지 확대/축소하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="scale">스케일</param>
        /// <returns>비트맵</returns>
        public static Bitmap ScaleImage(this Bitmap sourceBitmap, float scale)
        {
            if(sourceBitmap == null)
            {
                return null;
            }

            int widthScaled  = (int)(sourceBitmap.Width  * scale);
            int heightScaled = (int)(sourceBitmap.Height * scale);

            Bitmap targetBitmap = new Bitmap(widthScaled, heightScaled);

            using(Graphics graphics = Graphics.FromImage(targetBitmap))
            {
                Point[] targetPointArray =
                {
                    new Point(0              , 0               ),
                    new Point(widthScaled - 1, 0               ),
                    new Point(0              , heightScaled - 1)
                };

                Rectangle targetRectangle = new Rectangle
                (
                    0,
                    0,
                    sourceBitmap.Width  - 1,
                    sourceBitmap.Height - 1
                );

                graphics.DrawImage(sourceBitmap, targetPointArray, targetRectangle, GraphicsUnit.Pixel);
            }

            return targetBitmap;
        }

        #endregion
    }
}

 

728x90

 

▶ BitmapHelper.cs

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// 비트맵 헬퍼
    /// </summary>
    public class BitmapHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 픽셀 비트 수
        /// </summary>
        public const int PIXEL_BIT_COUNT = 32;

        /// <summary>
        /// 이미지 바이트 배열
        /// </summary>
        public byte[] ImageByteArray;

        /// <summary>
        /// 행 바이트 수
        /// </summary>
        public int RowByteCount;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 소스 비트맵
        /// </summary>
        private Bitmap sourceBitmap;

        /// <summary>
        /// 소스 비트맵 데이터
        /// </summary>
        private BitmapData sourceBitmapData;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 너비 - Width

        /// <summary>
        /// 너비
        /// </summary>
        public int Width
        {
            get
            {
                return this.sourceBitmap.Width;
            }
        }

        #endregion
        #region 높이 - Height

        /// <summary>
        /// 높이
        /// </summary>
        public int Height
        {
            get
            {
                return this.sourceBitmap.Height;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - BitmapHelper(sourceBitmap)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        public BitmapHelper(Bitmap sourceBitmap)
        {
            this.sourceBitmap = sourceBitmap;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 픽셀 구하기 - GetPixel(x, y, red, green, blue, alpha)

        /// <summary>
        /// 픽셀 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="red">적색 채널</param>
        /// <param name="green">녹색 채널</param>
        /// <param name="blue">청색 채널</param>
        /// <param name="alpha">알파 채널</param>
        public void GetPixel(int x, int y, out byte red, out byte green, out byte blue, out byte alpha)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            blue  = ImageByteArray[i++];
            green = ImageByteArray[i++];
            red   = ImageByteArray[i++];
            alpha = ImageByteArray[i  ];
        }

        #endregion
        #region 적색 채널 구하기 - GetRed(x, y)

        /// <summary>
        /// 적색 채널 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <returns>적색 채널</returns>
        public byte GetRed(int x, int y)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            return ImageByteArray[i + 2];
        }

        #endregion
        #region 녹색 채널 구하기 - GetGreen(x, y)

        /// <summary>
        /// 녹색 채널 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <returns>녹색 채널</returns>
        public byte GetGreen(int x, int y)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            return ImageByteArray[i + 1];
        }

        #endregion
        #region 청색 채널 구하기 - GetBlue(x, y)

        /// <summary>
        /// 청색 채널 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <returns>청색 채널</returns>
        public byte GetBlue(int x, int y)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            return ImageByteArray[i];
        }

        #endregion
        #region 알파 채널 구하기 - GetAlpha(x, y)

        /// <summary>
        /// 알파 채널 구하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <returns>알파 채널</returns>
        public byte GetAlpha(int x, int y)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            return ImageByteArray[i + 3];
        }

        #endregion

        #region 픽셀 설정하기 - SetPixel(x, y, red, green, blue, alpha)

        /// <summary>
        /// 픽셀 설정하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="red">적색 채널</param>
        /// <param name="green">녹색 채널</param>
        /// <param name="blue">청색 채널</param>
        /// <param name="alpha">알파 채널</param>
        public void SetPixel(int x, int y, byte red, byte green, byte blue, byte alpha)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            ImageByteArray[i++] = blue;
            ImageByteArray[i++] = green;
            ImageByteArray[i++] = red;
            ImageByteArray[i  ] = alpha;
        }

        #endregion
        #region 적색 채널 설정하기 - SetRed(x, y, red)

        /// <summary>
        /// 적색 채널 설정하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="red">적색 채널</param>
        public void SetRed(int x, int y, byte red)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            ImageByteArray[i + 2] = red;
        }

        #endregion
        #region 녹색 채널 설정하기 - SetGreen(x, y, green)

        /// <summary>
        /// 녹색 채널 설정하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="green">녹색 채널</param>
        public void SetGreen(int x, int y, byte green)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            ImageByteArray[i + 1] = green;
        }

        #endregion
        #region 청색 채널 설정하기 - SetBlue(x, y, blue)

        /// <summary>
        /// 청색 채널 설정하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="blue">청색 채널</param>
        public void SetBlue(int x, int y, byte blue)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            ImageByteArray[i] = blue;
        }

        #endregion
        #region 알파 채널 설정하기 - SetAlpha(x, y, alpha)

        /// <summary>
        /// 알파 채널 설정하기
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="alpha">알파 채널</param>
        public void SetAlpha(int x, int y, byte alpha)
        {
            int i = y * this.sourceBitmapData.Stride + x * 4;

            ImageByteArray[i + 3] = alpha;
        }

        #endregion

        #region 비트맵 잠금 설정하기 - LockBitmap()

        /// <summary>
        /// 비트맵 잠금 설정하기
        /// </summary>
        public void LockBitmap()
        {
            Rectangle sourceRectangle = new Rectangle
            (
                0,
                0,
                this.sourceBitmap.Width,
                this.sourceBitmap.Height
            );

            this.sourceBitmapData = this.sourceBitmap.LockBits
            (
                sourceRectangle,
                ImageLockMode.ReadWrite,
                PixelFormat.Format32bppArgb
            );

            RowByteCount = this.sourceBitmapData.Stride;

            int totalByteCount = this.sourceBitmapData.Stride * this.sourceBitmapData.Height;

            ImageByteArray = new byte[totalByteCount];

            Marshal.Copy(this.sourceBitmapData.Scan0, ImageByteArray, 0, totalByteCount);
        }

        #endregion
        #region 비트맵 잠금 해제하기 - UnlockBitmap()

        /// <summary>
        /// 비트맵 잠금 해제하기
        /// </summary>
        public void UnlockBitmap()
        {
            int totalByteCount = this.sourceBitmapData.Stride * this.sourceBitmapData.Height;

            Marshal.Copy(ImageByteArray, 0, this.sourceBitmapData.Scan0, totalByteCount);

            this.sourceBitmap.UnlockBits(this.sourceBitmapData);

            ImageByteArray = null;

            this.sourceBitmapData = null;
        }

        #endregion
    }
}

 

300x250

 

▶ MainForm.cs

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

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 히트 타입 - HitType

        /// <summary>
        /// 히트 타입
        /// </summary>
        private enum HitType
        {
            /// <summary>
            /// 해당 무
            /// </summary>
            None,

            /// <summary>
            /// 몸체
            /// </summary>
            Body,

            /// <summary>
            /// 왼쪽 가장자리
            /// </summary>
            LeftEdge,

            /// <summary>
            /// 오른쪽 가장자리
            /// </summary>
            RightEdge,

            /// <summary>
            /// 위쪽 가장자리
            /// </summary>
            TopEdge,

            /// <summary>
            /// 아래쪽 가장자리
            /// </summary>
            BottomEdge,

            /// <summary>
            /// 상단 왼쪽 모서리
            /// </summary>
            TopLeftCorner,

            /// <summary>
            /// 상단 오른쪽 모서리
            /// </summary>
            TopRightCorner,

            /// <summary>
            /// 하단 왼쪽 모서리
            /// </summary>
            BottomLeftCorner,

            /// <summary>
            /// 하단 오른쪽 모서리
            /// </summary>
            BottomRightCorner,
        };

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 상자 반경
        /// </summary>
        private const int BOX_RADIUS = 4;

        /// <summary>
        /// 배경 비트맵
        /// </summary>
        private Bitmap backgroundBitmap = null;

        /// <summary>
        /// 전경 비트맵
        /// </summary>
        private Bitmap foregroundBitmap = null;

        /// <summary>
        /// 전경 사각형
        /// </summary>
        private RectangleF foregroundRectangle = new RectangleF(-10, -10, 1, 1);

        /// <summary>
        /// 드래그 여부
        /// </summary>
        private bool isDragging = false;

        /// <summary>
        /// 히트 타입
        /// </summary>
        private HitType hitType = HitType.None;

        /// <summary>
        /// 마지막 마우스 포인트
        /// </summary>
        private Point lastMousePoint;

        /// <summary>
        /// 반대 모서리
        /// </summary>
        private PointF oppositeCorner;

        /// <summary>
        /// 이미지 확대/축소
        /// </summary>
        private float imageScale = 1f;

        /// <summary>
        /// 종횡비
        /// </summary>
        private float aspectRatio = 1f;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 배경 이미지 메뉴 항목 클릭시 처리하기 - backgroundImageMenuItem_Click(sender, e)

        /// <summary>
        /// 배경 이미지 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void backgroundImageMenuItem_Click(object sender, EventArgs e)
        {
            if(this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.backgroundBitmap = GetBitmap(this.openFileDialog.FileName);

                this.pictureBox.Image = this.backgroundBitmap.ScaleImage(this.imageScale);

                this.pictureBox.Refresh();
            }
        }

        #endregion
        #region 전경 이미지 메뉴 항목 클릭시 처리하기 - foregroundImageMenuItem_Click(sender, e)

        /// <summary>
        /// 전경 이미지 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void foregroundImageMenuItem_Click(object sender, EventArgs e)
        {
            if(this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.foregroundBitmap = GetBitmap(this.openFileDialog.FileName);

                this.foregroundRectangle = new RectangleF
                (
                    0,
                    0,
                    this.foregroundBitmap.Width,
                    this.foregroundBitmap.Height
                );

                this.aspectRatio = (float)this.foregroundBitmap.Width / (float)this.foregroundBitmap.Height;

                Bitmap ellipseBitmap = new Bitmap(this.foregroundBitmap.Width, this.foregroundBitmap.Height);

                using(Graphics ellipseGraphics = Graphics.FromImage(ellipseBitmap))
                {
                    ellipseGraphics.Clear(Color.Transparent);

                    using(GraphicsPath path = new GraphicsPath())
                    {
                        path.AddEllipse(this.foregroundRectangle);

                        using(PathGradientBrush brush = new PathGradientBrush(path))
                        {
                            Blend blend = new Blend();

                            blend.Positions = new float[] { 0.0f, 0.05f, 1.0f };
                            blend.Factors   = new float[] { 0.0f, 1.0f , 1.0f };

                            brush.CenterPoint = new PointF
                            (
                                this.foregroundBitmap.Width  / 2f,
                                this.foregroundBitmap.Height / 2f
                            );
                            brush.CenterColor    = Color.White;
                            brush.SurroundColors = new Color[] { Color.FromArgb(0, 0, 0, 0) };
                            brush.Blend          = blend;

                            ellipseGraphics.FillPath(brush, path);
                        }
                    }
                }

                CopyAlpha(this.foregroundBitmap, ellipseBitmap);

                this.pictureBox.Refresh();
            }
        }

        #endregion
        #region 다른 이름으로 저장 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)

        /// <summary>
        /// 다른 이름으로 저장 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void saveAsMenuItem_Click(object sender, EventArgs e)
        {
            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                Bitmap targetBitmap = new Bitmap(this.backgroundBitmap);

                if(this.foregroundBitmap != null)
                {
                    using(Graphics targetGraphics = Graphics.FromImage(targetBitmap))
                    {
                        targetGraphics.DrawImage(this.foregroundBitmap, this.foregroundRectangle);
                    }
                }

                SaveImage(targetBitmap, this.saveFileDialog.FileName);
            }
        }

        #endregion
        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

        /// <summary>
        /// 종료 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion
        #region 확대/축소 메뉴 항목 클릭시 처리하기 - scaleMenuItem_Click(sender, e)

        /// <summary>
        /// 확대/축소 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void scaleMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;

            string scaleText = menuItem.Text.Replace("&", "").Replace("%", "");

            this.imageScale = float.Parse(scaleText) / 100f;

            this.pictureBox.Image = this.backgroundBitmap.ScaleImage(this.imageScale);

            this.pictureBox.Refresh();

            this.scaleMenuItem.Text = "확대/축소 (" + menuItem.Text.Replace("&", "") + ")";

            foreach(ToolStripMenuItem childMenuItem in scaleMenuItem.DropDownItems)
            {
                childMenuItem.Checked = (childMenuItem == menuItem);
            }
        }

        #endregion
        #region 픽처 박스 마우스 DOWN 처리하기 - pictureBox_MouseDown(sender, e)

        /// <summary>
        /// 픽처 박스 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            this.hitType = FindHitType(e.Location);

            if(this.hitType == HitType.None)
            {
                return;
            }

            this.lastMousePoint = e.Location;

            switch(this.hitType)
            {
                case HitType.None :

                    break;

                case HitType.TopLeftCorner :
                case HitType.TopEdge       :
                case HitType.LeftEdge      :

                    this.oppositeCorner = new PointF
                    (
                        this.foregroundRectangle.Right,
                        this.foregroundRectangle.Bottom
                    );

                    break;

                case HitType.TopRightCorner :

                    this.oppositeCorner = new PointF
                    (
                        this.foregroundRectangle.Left,
                        this.foregroundRectangle.Bottom
                    );

                    break;

                case HitType.BottomRightCorner :
                case HitType.BottomEdge        :
                case HitType.RightEdge         :

                    this.oppositeCorner = new PointF
                    (
                        this.foregroundRectangle.Left,
                        this.foregroundRectangle.Top
                    );

                    break;

                case HitType.BottomLeftCorner :

                    this.oppositeCorner = new PointF
                    (
                        this.foregroundRectangle.Right,
                        this.foregroundRectangle.Top
                    );

                    break;
            }

            this.isDragging = true;
        }

        #endregion
        #region 픽처 박스 마우스 이동시 처리하기 - pictureBox_MouseMove(sender, e)

        /// <summary>
        /// 픽처 박스 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if(this.isDragging)
            {
                ProcessDraggingMouseMove(e.Location);
            }
            else
            {
                ProcessNotDraggingMouseMove(e.Location);
            }
        }

        #endregion
        #region 픽처 박스 마우스 UP 처리하기 - pictrueBox_MouseUp(sender, e)

        /// <summary>
        /// 픽처 박스 마우스 UP 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictrueBox_MouseUp(object sender, MouseEventArgs e)
        {
            this.isDragging = false;
        }

        #endregion
        #region 픽처 박스 페인트시 처리하기 - pictureBox_Paint(sender, e)

        /// <summary>
        /// 픽처 박스 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode     = SmoothingMode.AntiAlias;
            e.Graphics.InterpolationMode = InterpolationMode.High;

            try
            {
                RectangleF foregoundRectangleScaled = ScaleForegroundRectangle();

                if(this.foregroundBitmap != null)
                {
                    e.Graphics.DrawImage(this.foregroundBitmap, foregoundRectangleScaled);
                }

                using(Pen pen = new Pen(Color.Red, 2))
                {
                    e.Graphics.DrawRectangle(pen, foregoundRectangleScaled);

                    pen.Color       = Color.Yellow;
                    pen.DashPattern = new float[] { 5, 5 };

                    e.Graphics.DrawRectangle(pen, foregoundRectangleScaled);
                }

                PointF[] cornerPointArray =
                {
                    new PointF(foregoundRectangleScaled.Left , foregoundRectangleScaled.Top   ),
                    new PointF(foregoundRectangleScaled.Right, foregoundRectangleScaled.Top   ),
                    new PointF(foregoundRectangleScaled.Left , foregoundRectangleScaled.Bottom),
                    new PointF(foregoundRectangleScaled.Right, foregoundRectangleScaled.Bottom),
                };

                foreach(PointF point in cornerPointArray)
                {
                    e.Graphics.DrawBox(Brushes.White, Pens.Black, point, BOX_RADIUS);
                }
            }
            catch
            {
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 비트맵 구하기 - GetBitmap(filePath)

        /// <summary>
        /// 비트맵 구하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>비트맵</returns>
        private Bitmap GetBitmap(string filePath)
        {
            using(Bitmap bitmap = new Bitmap(filePath))
            {
                return new Bitmap(bitmap);
            }
        }

        #endregion
        #region 이미지 저장하기 - SaveImage(image, filePath)

        /// <summary>
        /// 이미지 저장하기
        /// </summary>
        /// <param name="image">이미지</param>
        /// <param name="filePath">파일 경로</param>
        private void SaveImage(Image image, string filePath)
        {
            string fileExtension = Path.GetExtension(filePath);

            switch(fileExtension.ToLower())
            {
                case ".bmp"  : image.Save(filePath, ImageFormat.Bmp ); break;
                case ".exif" : image.Save(filePath, ImageFormat.Exif); break;
                case ".gif"  : image.Save(filePath, ImageFormat.Gif ); break;
                case ".jpg"  :
                case ".jpeg" : image.Save(filePath, ImageFormat.Jpeg); break;
                case ".png"  : image.Save(filePath, ImageFormat.Png ); break;
                case ".tif"  :
                case ".tiff" : image.Save(filePath, ImageFormat.Tiff); break;
                default      : throw new NotSupportedException("알 수 없는 파일 확장 : " + fileExtension);
            }
        }

        #endregion
        #region 알파 채널 복사하기 - CopyAlpha(targetBitmap, sourceMaskBitmap)

        /// <summary>
        /// 알파 채널 복사하기
        /// </summary>
        /// <param name="targetBitmap">타겟 비트맵</param>
        /// <param name="sourceMaskBitmap">소스 마스크 비트맵</param>
        private void CopyAlpha(Bitmap targetBitmap, Bitmap sourceMaskBitmap)
        {
            BitmapHelper targetBitmapHelper     = new BitmapHelper(targetBitmap    );
            BitmapHelper sourceMaskBitmapHelper = new BitmapHelper(sourceMaskBitmap);

            targetBitmapHelper.LockBitmap();
            sourceMaskBitmapHelper.LockBitmap();

            for(int x = 0; x < targetBitmap.Width; x++)
            {
                for(int y = 0; y < targetBitmap.Height; y++)
                {
                    targetBitmapHelper.SetAlpha(x, y, sourceMaskBitmapHelper.GetAlpha(x, y));
                }
            }

            sourceMaskBitmapHelper.UnlockBitmap();
            targetBitmapHelper.UnlockBitmap();
        }

        #endregion
        #region 전경 이미지 사각형 확대/축소하기 - ScaleForegroundRectangle()

        /// <summary>
        /// 전경 이미지 사각형 확대/축소하기
        /// </summary>
        /// <returns>전경 이미지 사각형 확대/축소하기</returns>
        private RectangleF ScaleForegroundRectangle()
        {
            float x      = this.imageScale * this.foregroundRectangle.X;
            float y      = this.imageScale * this.foregroundRectangle.Y;
            float width  = this.imageScale * this.foregroundRectangle.Width;
            float height = this.imageScale * this.foregroundRectangle.Height;

            return new RectangleF(x, y, width, height);
        }

        #endregion
        #region 히트 타입 찾기 - FindHitType(point)

        /// <summary>
        /// 히트 타입 찾기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <returns>히트 타입</returns>
        private HitType FindHitType(Point point)
        {
            RectangleF rectangle = ScaleForegroundRectangle();

            bool hitLeft;
            bool hitRight;
            bool hitTop;
            bool hitBottom;

            hitLeft   = ((point.X >= rectangle.Left   - BOX_RADIUS) && (point.X <= rectangle.Left   + BOX_RADIUS));
            hitRight  = ((point.X >= rectangle.Right  - BOX_RADIUS) && (point.X <= rectangle.Right  + BOX_RADIUS));
            hitTop    = ((point.Y >= rectangle.Top    - BOX_RADIUS) && (point.Y <= rectangle.Top    + BOX_RADIUS));
            hitBottom = ((point.Y >= rectangle.Bottom - BOX_RADIUS) && (point.Y <= rectangle.Bottom + BOX_RADIUS));

            if(hitLeft && hitTop)
            {
                return HitType.TopLeftCorner;
            }

            if(hitRight && hitTop)
            {
                return HitType.TopRightCorner;
            }

            if(hitLeft && hitBottom)
            {
                return HitType.BottomLeftCorner;
            }

            if(hitRight && hitBottom)
            {
                return HitType.BottomRightCorner;
            }

            if(hitLeft)
            {
                return HitType.LeftEdge;
            }

            if(hitRight)
            {
                return HitType.RightEdge;
            }

            if(hitTop)
            {
                return HitType.TopEdge;
            }

            if(hitBottom)
            {
                return HitType.BottomEdge;
            }

            if((point.X >= rectangle.Left) && (point.X <= rectangle.Right ) &&
               (point.Y >= rectangle.Top ) && (point.Y <= rectangle.Bottom))
            {
                return HitType.Body;
            }

            return HitType.None;
        }

        #endregion
        #region 감소된 크기 구하기 - GetReducedSize(newWidth, newHeight)

        /// <summary>
        /// 감소된 크기 구하기
        /// </summary>
        /// <param name="newWidth">신규 너비</param>
        /// <param name="newHeight">신규 높이</param>
        /// <returns>감소된 크기</returns>
        private SizeF GetReducedSize(float newWidth, float newHeight)
        {
            if(newWidth < 10)
            {
                newWidth = 10;
            }

            if(newHeight < 10)
            {
                newHeight = 10;
            }

            if(newWidth / newHeight > this.aspectRatio)
            {
                newWidth = newHeight * this.aspectRatio;
            }
            else
            {
                newHeight = newWidth / this.aspectRatio;
            }

            return new SizeF(newWidth, newHeight);
        }

        #endregion
        #region 확장된 크기 구하기 - GetEnlargedSize(newWidth, newHeight)

        /// <summary>
        /// 확장된 크기 구하기
        /// </summary>
        /// <param name="newWidth">신규 너비</param>
        /// <param name="newHeight">신규 높이</param>
        /// <returns>확장된 크기</returns>
        private SizeF GetEnlargedSize(float newWidth, float newHeight)
        {
            if(newWidth < 10)
            {
                newWidth = 10;
            }

            if(newHeight < 10)
            {
                newHeight = 10;
            }

            if(newWidth / newHeight > this.aspectRatio)
            {
                newHeight = newWidth / this.aspectRatio;
            }
            else
            {
                newWidth = newHeight * this.aspectRatio;
            }

            return new SizeF(newWidth, newHeight);
        }

        #endregion
        #region 드래그하는 경우 마우스 이동시 처리하기 - ProcessDraggingMouseMove(mousePoint)

        /// <summary>
        /// 드래그하는 경우 마우스 이동시 처리하기
        /// </summary>
        /// <param name="mousePoint">마우스 포인트</param>
        private void ProcessDraggingMouseMove(Point mousePoint)
        {
            float cornerWidth  = Math.Abs(this.oppositeCorner.X - mousePoint.X / this.imageScale);
            float cornerHeight = Math.Abs(this.oppositeCorner.Y - mousePoint.Y / this.imageScale);

            SizeF cornerSize = GetReducedSize(cornerWidth, cornerHeight);

            SizeF edgeSize = new SizeF();

            if((this.hitType == HitType.TopEdge) || (this.hitType == HitType.BottomEdge))
            {
                edgeSize = GetEnlargedSize(0, cornerHeight);
            }
            else if((this.hitType == HitType.LeftEdge) || (this.hitType == HitType.RightEdge))
            {
                edgeSize = GetEnlargedSize(cornerWidth, 0);
            }

            float centerX = this.foregroundRectangle.X + this.foregroundRectangle.Width  / 2f;
            float centerY = this.foregroundRectangle.Y + this.foregroundRectangle.Height / 2f;

            switch(this.hitType)
            {
                case HitType.TopLeftCorner :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.Right  - cornerSize.Width,
                        this.foregroundRectangle.Bottom - cornerSize.Height,
                        cornerSize.Width,
                        cornerSize.Height
                    );

                    break;

                case HitType.TopRightCorner :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.Left,
                        this.foregroundRectangle.Bottom - cornerSize.Height,
                        cornerSize.Width,
                        cornerSize.Height
                    );

                    break;

                case HitType.BottomRightCorner :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.X,
                        this.foregroundRectangle.Y,
                        cornerSize.Width,
                        cornerSize.Height
                    );

                    break;

                case HitType.BottomLeftCorner :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.Right - cornerSize.Width,
                        this.foregroundRectangle.Top,
                        cornerSize.Width,
                        cornerSize.Height
                    );

                    break;

                case HitType.TopEdge :

                    this.foregroundRectangle = new RectangleF
                    (
                        centerX - edgeSize.Width / 2f,
                        this.foregroundRectangle.Bottom - edgeSize.Height,
                        edgeSize.Width,
                        edgeSize.Height
                    );

                    break;

                case HitType.RightEdge :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.Left,
                        centerY - edgeSize.Height / 2f,
                        edgeSize.Width,
                        edgeSize.Height
                    );

                    break;

                case HitType.BottomEdge :

                    this.foregroundRectangle = new RectangleF
                    (
                        centerX - edgeSize.Width / 2f,
                        this.foregroundRectangle.Top,
                        edgeSize.Width,
                        edgeSize.Height
                    );

                    break;

                case HitType.LeftEdge :

                    this.foregroundRectangle = new RectangleF
                    (
                        this.foregroundRectangle.Right - edgeSize.Width,
                        centerY - edgeSize.Height / 2f,
                        edgeSize.Width,
                        edgeSize.Height
                    );

                    break;

                case HitType.Body :

                    int deltaX = (int)((mousePoint.X - this.lastMousePoint.X) / this.imageScale);
                    int deltaY = (int)((mousePoint.Y - this.lastMousePoint.Y) / this.imageScale);

                    this.foregroundRectangle.X += deltaX;
                    this.foregroundRectangle.Y += deltaY;

                    break;
            }

            this.lastMousePoint = mousePoint;

            this.pictureBox.Refresh();
        }

        #endregion
        #region 드래그하지 않는 경우 마우스 이동시 처리하기 - ProcessNotDraggingMouseMove(mousePoint)

        /// <summary>
        /// 드래그하지 않는 경우 마우스 이동시 처리하기
        /// </summary>
        /// <param name="mousePoint">마우스 포인트</param>
        private void ProcessNotDraggingMouseMove(Point mousePoint)
        {
            Cursor newCursor = Cursors.Default;

            this.hitType = FindHitType(mousePoint);

            switch(this.hitType)
            {
                case HitType.TopLeftCorner     :
                case HitType.BottomRightCorner :

                    newCursor = Cursors.SizeNWSE;

                    break;

                case HitType.TopRightCorner   :
                case HitType.BottomLeftCorner :

                    newCursor = Cursors.SizeNESW;

                    break;

                case HitType.LeftEdge  :
                case HitType.RightEdge :

                    newCursor = Cursors.SizeWE;

                    break;

                case HitType.TopEdge    :
                case HitType.BottomEdge :

                    newCursor = Cursors.SizeNS;

                    break;

                case HitType.Body :

                    newCursor = Cursors.SizeAll;

                    break;
            }

            if(this.pictureBox.Cursor != newCursor)
            {
                this.pictureBox.Cursor = newCursor;
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요