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

TestProject.zip
다운로드

▶ Cell.cs

using System.Drawing;
using System.Drawing.Drawing2D;

namespace TestProject
{
    /// <summary>
    /// 셀
    /// </summary>
    public class Cell
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 경계 사각형
        /// </summary>
        public RectangleF BoundRectangle;

        /// <summary>
        /// 비트맵
        /// </summary>
        public Bitmap Bitmap = null;

        #endregion

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

        #region 생성자 - Cell(boundRectangle)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="boundRectangle">경계 사각형</param>
        public Cell(RectangleF boundRectangle)
        {
            BoundRectangle = boundRectangle;
        }

        #endregion

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

        #region 그리기 - Draw(graphics, pen, cellWidth, cellHeight)

        /// <summary>
        /// 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="pen">펜</param>
        /// <param name="cellWidth">셀 너비</param>
        /// <param name="cellHeight">셀 높이</param>
        public void Draw(Graphics graphics, Pen pen, float cellWidth, float cellHeight)
        {
            if(Bitmap != null)
            {
                float bitmapWidth  = Bitmap.Width;
                float bitmapHeight = Bitmap.Height;

                float centerX = bitmapWidth  / 2f;
                float centerY = bitmapHeight / 2f;

                if(bitmapWidth / bitmapHeight > BoundRectangle.Width / BoundRectangle.Height)
                {
                    bitmapWidth = BoundRectangle.Width / BoundRectangle.Height * bitmapHeight;
                }
                else
                {
                    bitmapHeight = bitmapWidth / (BoundRectangle.Width / BoundRectangle.Height);
                }

                RectangleF sourceRectangle = new RectangleF
                (
                    centerX - bitmapWidth  / 2f,
                    centerY - bitmapHeight / 2f,
                    bitmapWidth,
                    bitmapHeight
                );

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

                graphics.DrawImage(Bitmap, targetPointArray, sourceRectangle, GraphicsUnit.Pixel);
            }

            GraphicsPath graphicsPath = GetRoundedRectanglePath
            (
                BoundRectangle,
                2 * pen.Width,
                2 * pen.Width,
                true,
                true,
                true,
                true
            );

            graphics.DrawPath(pen, graphicsPath);
        }

        #endregion
        #region 포인트 포함 여부 구하기 - ContainsPoint(point)

        /// <summary>
        /// 포인트 포함 여부 구하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <returns>포인트 포함 여부</returns>
        public bool ContainsPoint(PointF point)
        {
            return BoundRectangle.Contains(point);
        }

        #endregion

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

        #region 라운드 사각형 패스 구하기 - GetRoundedRectanglePath(rectangle, radiusX, radiusY, roundUpperLeft, roundUpperRight, roundLowerRight, roundLowerLeft)

        /// <summary>
        /// 라운드 사각형 패스 구하기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="roundUpperLeft">좌상단 라운드 여부</param>
        /// <param name="roundUpperRight">우상단 라운드 여부</param>
        /// <param name="roundLowerRight">우하단 라운드 여부</param>
        /// <param name="roundLowerLeft">좌하단 라운드 여부</param>
        /// <returns>라운드 사각형 패스</returns>
        private GraphicsPath GetRoundedRectanglePath
        (
            RectangleF rectangle,
            float      radiusX,
            float      radiusY,
            bool       roundUpperLeft,
            bool       roundUpperRight,
            bool       roundLowerRight,
            bool       roundLowerLeft
        )
        {
            PointF point1;
            PointF point2;

            GraphicsPath path = new GraphicsPath();

            if(roundUpperLeft)
            {
                RectangleF cornerRectangle = new RectangleF
                (
                    rectangle.X,
                    rectangle.Y,
                    2 * radiusX,
                    2 * radiusY
                );

                path.AddArc(cornerRectangle, 180, 90);

                point1 = new PointF(rectangle.X + radiusX, rectangle.Y);
            }
            else
            {
                point1 = new PointF(rectangle.X, rectangle.Y);
            }

            if(roundUpperRight)
            {
                point2 = new PointF(rectangle.Right - radiusX, rectangle.Y);
            }
            else
            {
                point2 = new PointF(rectangle.Right, rectangle.Y);
            }

            path.AddLine(point1, point2);

            if(roundUpperRight)
            {
                RectangleF cornerRectangle = new RectangleF
                (
                    rectangle.Right - 2 * radiusX,
                    rectangle.Y,
                    2 * radiusX,
                    2 * radiusY
                );

                path.AddArc(cornerRectangle, 270, 90);

                point1 = new PointF(rectangle.Right, rectangle.Y + radiusY);
            }
            else
            {
                point1 = new PointF(rectangle.Right, rectangle.Y);
            }

            if(roundLowerRight)
            {
                point2 = new PointF(rectangle.Right, rectangle.Bottom - radiusY);
            }
            else
            {
                point2 = new PointF(rectangle.Right, rectangle.Bottom);
            }

            path.AddLine(point1, point2);

            if(roundLowerRight)
            {
                RectangleF cornerRectangle = new RectangleF
                (
                    rectangle.Right  - 2 * radiusX,
                    rectangle.Bottom - 2 * radiusY,
                    2 * radiusX,
                    2 * radiusY
                );

                path.AddArc(cornerRectangle, 0, 90);

                point1 = new PointF(rectangle.Right - radiusX, rectangle.Bottom);
            }
            else
            {
                point1 = new PointF(rectangle.Right, rectangle.Bottom);
            }

            if(roundLowerLeft)
            {
                point2 = new PointF(rectangle.X + radiusX, rectangle.Bottom);
            }
            else
            {
                point2 = new PointF(rectangle.X, rectangle.Bottom);
            }

            path.AddLine(point1, point2);

            if(roundLowerLeft)
            {
                RectangleF cornerRectangle = new RectangleF
                (
                    rectangle.X,
                    rectangle.Bottom - 2 * radiusY,
                    2 * radiusX,
                    2 * radiusY
                );

                path.AddArc(cornerRectangle, 90, 90);

                point1 = new PointF(rectangle.X, rectangle.Bottom - radiusY);
            }
            else
            {
                point1 = new PointF(rectangle.X, rectangle.Bottom);
            }

            if(roundUpperLeft)
            {
                point2 = new PointF(rectangle.X, rectangle.Y + radiusY);
            }
            else
            {
                point2 = new PointF(rectangle.X, rectangle.Y);
            }

            path.AddLine(point1, point2);

            path.CloseFigure();

            return path;
        }

        #endregion
    }
}

 

728x90

 

▶ MainForm.cs

using System;
using System.Collections.Generic;
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
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 이미지 너비
        /// </summary>
        private int imageWidth;
        
        /// <summary>
        /// 이미지 높이
        /// </summary>
        private int imageHeight;

        /// <summary>
        /// 셀 너비
        /// </summary>
        private float cellWidth;
        
        /// <summary>
        /// 셀 높이
        /// </summary>
        private float cellHeight;
        
        /// <summary>
        /// 각도
        /// </summary>
        private float angle;
        
        /// <summary>
        /// 분리자 너비
        /// </summary>
        private float dividerWidth;

        /// <summary>
        /// 분리자 색상
        /// </summary>
        private Color dividerColor;

        /// <summary>
        /// 변환 매트릭스
        /// </summary>
        private Matrix transformMatrix = null;
        
        /// <summary>
        /// 역변환 매트릭스
        /// </summary>
        private Matrix inverseTransformMatrix = null;

        /// <summary>
        /// 셀 리스트
        /// </summary>
        private List<Cell> cellList = null;

        /// <summary>
        /// 문서 수정 여부
        /// </summary>
        private bool documentModified = false;

        #endregion

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

        #region 생성자 - MainForm()

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

            FormClosing                          += Form_FormClosing;
            this.saveAsMenuItem.Click            += saveAsMenuItem_Click;
            this.exitMenuItem.Click              += exitMenuItem_Click;
            this.createButton.Click              += createButton_Click;
            this.dividerWidthTextBox.TextChanged += dividerWidthTextBox_TextChanged;
            this.dividerColorValueLabel.Click    += dividerColorValueLabel_Click;
            this.canvasPictureBox.Paint          += canvasPictureBox_Paint;
            this.canvasPictureBox.MouseClick     += canvasPictureBox_MouseClick;
        }

        #endregion

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

        #region 폼을 닫을 경우 처리하기 - Form_FormClosing(sender, e)

        /// <summary>
        /// 폼을 닫을 경우 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = !IsDocumentSafe();
        }

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

        /// <summary>
        /// 다른 이름으로 저장 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void saveAsMenuItem_Click(object sender, EventArgs e)
        {
            Save();
        }

        #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 생성 버튼 클릭시 처리하기 - createButton_Click(sender, e)

        /// <summary>
        /// 생성 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void createButton_Click(object sender, EventArgs e)
        {
            if(!IsDocumentSafe())
            {
                return;
            }

            try
            {
                this.imageWidth   = int.Parse(this.imageWidthTextBox.Text);
                this.imageHeight  = int.Parse(this.imageHeightTextBox.Text);
                this.cellWidth    = int.Parse(this.cellWidthTextBox.Text);
                this.cellHeight   = int.Parse(this.cellHeightTextBox.Text);
                this.angle        = float.Parse(this.cellAngleTextBox.Text);
                this.dividerWidth = float.Parse(this.dividerWidthTextBox.Text);
                this.dividerColor = this.dividerColorValueLabel.BackColor;

                this.transformMatrix = new Matrix();

                this.transformMatrix.Rotate(this.angle);

                this.inverseTransformMatrix = new Matrix();

                this.inverseTransformMatrix.Rotate(-this.angle);

                MakeCell();

                this.canvasPictureBox.ClientSize = new Size(this.imageWidth, this.imageHeight);

                this.canvasPictureBox.Visible = true;

                this.saveAsMenuItem.Enabled = true;

                this.canvasPictureBox.Refresh();
            }
            catch(Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        #endregion
        #region 분리자 너비 텍스트 박스 텍스트 변경시 처리하기 - dividerWidthTextBox_TextChanged(sender, e)

        /// <summary>
        /// 분리자 너비 텍스트 박스 텍스트 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void dividerWidthTextBox_TextChanged(object sender, EventArgs e)
        {
            float width;

            if(float.TryParse(this.dividerWidthTextBox.Text, out width))
            {
                this.dividerWidth = width;

                this.canvasPictureBox.Refresh();
            }
        }

        #endregion
        #region 분리자 색상 값 레이블 클릭시 처리하기 - dividerColorValueLabel_Click(sender, e)

        /// <summary>
        /// 분리자 색상 값 레이블 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void dividerColorValueLabel_Click(object sender, EventArgs e)
        {
            this.colorDialog.Color = this.dividerColorValueLabel.BackColor;

            if(this.colorDialog.ShowDialog() == DialogResult.OK)
            {
                this.dividerColorValueLabel.BackColor = this.colorDialog.Color;

                this.canvasPictureBox.Refresh();
            }
        }

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

        /// <summary>
        /// 캔버스 픽처 박스 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvasPictureBox_Paint(object sender, PaintEventArgs e)
        {
            DrawCell(e.Graphics);
        }

        #endregion
        #region 캔버스 픽처 박스 마우스 클릭시 처리하기 - canvasPictureBox_MouseClick(sender, e)

        /// <summary>
        /// 캔버스 픽처 박스 마우스 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvasPictureBox_MouseClick(object sender, MouseEventArgs e)
        {
            if(this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    PointF[] pointArray = { e.Location };

                    this.inverseTransformMatrix.TransformPoints(pointArray);

                    foreach(Cell cell in this.cellList)
                    {
                        if(cell.ContainsPoint(pointArray[0]))
                        {
                            cell.Bitmap = new Bitmap(this.openFileDialog.FileName);

                            this.documentModified = true;

                            break;
                        }
                    }
                }
                catch(Exception exception)
                {
                    MessageBox.Show(exception.Message);
                }

                this.canvasPictureBox.Refresh();
            }
        }

        #endregion

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

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

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

            switch(extension.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("알 수 없는 파일 확장자 : " + extension);
            }
        }

        #endregion
        #region 저장하기 - Save()

        /// <summary>
        /// 저장하기
        /// </summary>
        private void Save()
        {
            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                using(Bitmap bitmap = new Bitmap(this.imageWidth, this.imageHeight))
                {
                    using(Graphics graphics = Graphics.FromImage(bitmap))
                    {
                        DrawCell(graphics);
                    }

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

                this.documentModified = false;
            }
        }

        #endregion
        #region 문서 안전 여부 구하기 - IsDocumentSafe()

        /// <summary>
        /// 문서 안전 여부 구하기
        /// </summary>
        /// <returns>문서 안전 여부</returns>
        private bool IsDocumentSafe()
        {
            if(!this.documentModified)
            {
                return true;
            }

            DialogResult result = MessageBox.Show
            (
                "변경 사항을 저장하시겠습니까?",
                "확인",
                MessageBoxButtons.YesNoCancel
            );

            if(result == DialogResult.No)
            {
                return true;
            }

            if(result == DialogResult.Cancel)
            {
                return false;
            }

            Save();

            return !documentModified;
        }

        #endregion
        #region 셀 만들기 - MakeCell()

        /// <summary>
        /// 셀 만들기
        /// </summary>
        private void MakeCell()
        {
            PointF[] pointArray =
            {
                new PointF(0              , 0               ),
                new PointF(0              , this.imageHeight),
                new PointF(this.imageWidth, this.imageHeight),
                new PointF(this.imageWidth, 0               )
            };

            this.inverseTransformMatrix.TransformPoints(pointArray);

            float minimumX = pointArray[0].X;
            float minimumY = pointArray[0].Y;
            float maximumX = minimumX;
            float maximumY = minimumY;

            for(int i = 1; i < pointArray.Length; i++)
            {
                if(minimumX > pointArray[i].X) minimumX = pointArray[i].X;
                if(maximumX < pointArray[i].X) maximumX = pointArray[i].X;
                if(minimumY > pointArray[i].Y) minimumY = pointArray[i].Y;
                if(maximumY < pointArray[i].Y) maximumY = pointArray[i].Y;
            }

            int minimumRow    = (int)(minimumY / this.cellHeight) - 1;
            int maximumRow    = (int)(maximumY / this.cellHeight) + 1;
            int minimumColumn = (int)(minimumX / this.cellWidth ) - 1;
            int maximumColumn = (int)(maximumX / this.cellWidth ) + 1;

            GraphicsPath imagePath = new GraphicsPath();

            imagePath.AddPolygon(pointArray);

            Graphics graphics = CreateGraphics();

            this.cellList = new List<Cell>();

            for(int row = minimumRow; row <= maximumRow; row++)
            {
                for(int column = minimumColumn; column <= maximumColumn; column++)
                {
                    Region region = new Region(imagePath);

                    float x = column * cellWidth;
                    float y = row * cellHeight;

                    if(Math.Abs(column % 2) == 1)
                    {
                        y += cellHeight / 2f;
                    }

                    RectangleF cellRectangle = new RectangleF
                    (
                        x,
                        y,
                        cellWidth,
                        cellHeight
                    );

                    region.Intersect(cellRectangle);

                    if(!region.IsEmpty(graphics))
                    {
                        this.cellList.Add(new Cell(cellRectangle));
                    }
                }
            }
        }

        #endregion
        #region 셀 그리기 - DrawCell(graphics)

        /// <summary>
        /// 셀 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        private void DrawCell(Graphics graphics)
        {
            graphics.SmoothingMode     = SmoothingMode.AntiAlias;
            graphics.InterpolationMode = InterpolationMode.High;

            graphics.Clear(this.canvasPictureBox.BackColor);

            graphics.Transform = this.transformMatrix;

            using(Pen pen = new Pen(this.dividerColorValueLabel.BackColor, this.dividerWidth))
            {
                foreach(Cell cell in this.cellList)
                {
                    cell.Draw(graphics, pen, this.cellWidth, this.cellHeight);
                }
            }
        }

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

댓글을 달아 주세요