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

TestProject.zip
0.02MB

▶ Cell.cs

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

namespace TestProject
{
    /// <summary>
    /// 셀
    /// </summary>
    public class Cell
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// 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 포인트 포함 여부 구하기 - ContainsPoint(point)

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

        #endregion
        #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);
            }

            graphics.DrawRectangle
            (
                pen,
                BoundRectangle.X,
                BoundRectangle.Y,
                BoundRectangle.Width,
                BoundRectangle.Height
            );

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

            graphics.DrawPath(pen, path);
        }

        #endregion

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

        #region 둥근 사각형 경로 구하기 - GetRoundedRectanglePath(rectangle, radiusX, radiusY, roundLeftUpper, roundRightUpper, roundRightLower, roundLeftLower)

        /// <summary>
        /// 둥근 사각형 경로 구하기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="roundLeftUpper">좌상단 라운드 여부</param>
        /// <param name="roundRightUpper">우상단 라운드 여부</param>
        /// <param name="roundRightLower">우하단 라운드 여부</param>
        /// <param name="roundLeftLower">좌하단 라운드 여부</param>
        /// <returns>둥근 사각형 경로</returns>
        private GraphicsPath GetRoundedRectanglePath
        (
            RectangleF rectangle,
            float      radiusX,
            float      radiusY,
            bool       roundLeftUpper,
            bool       roundRightUpper,
            bool       roundRightLower,
            bool       roundLeftLower
        )
        {
            PointF point1;
            PointF point2;

            GraphicsPath graphicsPath = new GraphicsPath();

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

                graphicsPath.AddArc(cornerRectangle, 180, 90);

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

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

            graphicsPath.AddLine(point1, point2);

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

                graphicsPath.AddArc(cornerRectangle, 270, 90);

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

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

            graphicsPath.AddLine(point1, point2);

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

                graphicsPath.AddArc(cornerRectangle, 0, 90);

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

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

            graphicsPath.AddLine(point1, point2);

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

                graphicsPath.AddArc(cornerRectangle, 90, 90);

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

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

            graphicsPath.AddLine(point1, point2);

            graphicsPath.CloseFigure();

            return graphicsPath;
        }

        #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 cellAngle;
        
        /// <summary>
        /// 구분자 너비
        /// </summary>
        private float dividerWidth;

        /// <summary>
        /// 변환
        /// </summary>
        private Matrix transform = null;

        /// <summary>
        /// 역변환
        /// </summary>
        private Matrix inverseTransform = null;

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

        /// <summary>
        /// 문서 업데이트 여부
        /// </summary>
        private bool documentUpdated = 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.dividerWidthTextBox.TextChanged += dividerWidthTextBox_TextChanged;
            this.dividerColorValueLabel.Click    += dividerColorValueLabel_Click;
            this.backgroundColorValueLabel.Click += backgroundColorValueLabel_Click;
            this.createButton.Click              += createButton_Click;
            this.pictureBox.Paint                += pictureBox_Paint;
            this.pictureBox.MouseClick           += pictureBox_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 = !CheckDocumentUpdated();
        }

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

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

        #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 구분자 너비 텍스트 박스 텍스트 변경시 처리하기 - 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.pictureBox.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.pictureBox.Refresh();
            }
        }

        #endregion
        #region 배경 색상 값 레이블 클릭시 처리하기 - backgroundColorValueLabel_Click(sender, e)

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

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

                this.pictureBox.Refresh();
            }
        }

        #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(!CheckDocumentUpdated())
            {
                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.cellAngle    = float.Parse(this.cellAngleTextBox.Text   );
                this.dividerWidth = float.Parse(this.dividerWidthTextBox.Text);

                this.transform = new Matrix();

                this.transform.Rotate(this.cellAngle);

                this.inverseTransform = new Matrix();

                this.inverseTransform.Rotate(-this.cellAngle);

                MakeCells();

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

                this.pictureBox.Visible = true;

                this.saveAsMenuItem.Enabled = true;

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

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

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

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

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

                    this.inverseTransform.TransformPoints(pointArray);

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

                            this.documentUpdated = true;

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

                this.pictureBox.Refresh();
            }
        }

        #endregion

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

        #region 문서 업데이트 여부 체크하기 - CheckDocumentUpdated()

        /// <summary>
        /// 문서 업데이트 여부 체크하기
        /// </summary>
        /// <returns>문서 업데이트 여부</returns>
        private bool CheckDocumentUpdated()
        {
            if(!this.documentUpdated)
            {
                return true;
            }

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

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

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

            SaveBitmap();

            return !this.documentUpdated;
        }

        #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 비트맵 저장하기 - SaveBitmap()

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

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

                this.documentUpdated = false;
            }
        }

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

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

            this.inverseTransform.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 graphicsPath = new GraphicsPath();

            graphicsPath.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(graphicsPath);

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

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

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

                    region.Intersect(cellRectangle);

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

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

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

            graphics.Clear(this.backgroundColorValueLabel.BackColor);

            graphics.Transform = this.transform;

            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

댓글을 달아 주세요