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

TestProject.zip
다운로드

▶ 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 Bitmap sourceBitmap = Properties.Resources.cat;

        /// <summary>
        /// 배경 이미지
        /// </summary>
        private Bitmap backgroundImage = null;

        /// <summary>
        /// 배경 색상
        /// </summary>
        private Color backgroundColor = Color.White;

        #endregion

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

        #region 생성자 - MainForm()

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

            this.openMenuItem.Click            += openMenuItem_Click;
            this.backgroundColorMenuItem.Click += backgroundColorMenuItem_Click;
            this.backgroundImageMenuItem.Click += backgroundImageMenuItem_Click;
            this.saveAsMenuItem.Click          += saveAsMenuItem_Click;
            this.exitMenuItem.Click            += exitMenuItem_Click;
            this.drawButton.Click              += drawButton_Click;
        }

        #endregion

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

        #region 파일 열기 메뉴 항목 클릭시 처리하기 - openMenuItem_Click(sender, e)

        /// <summary>
        /// 파일 열기 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void openMenuItem_Click(object sender, EventArgs e)
        {
            if(this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.sourceBitmap = GetBitmap(this.openFileDialog.FileName);
            }
        }

        #endregion
        #region 배경 색상 메뉴 항목 클릭시 처리하기 - backgroundColorMenuItem_Click(sender, e)

        /// <summary>
        /// 배경 색상 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void backgroundColorMenuItem_Click(object sender, EventArgs e)
        {
            this.colorDialog.Color = this.backgroundColor;

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

                Bitmap bitmap = new Bitmap(16, 16);

                using(Graphics graphics = Graphics.FromImage(bitmap))
                {
                    graphics.Clear(backgroundColor);

                    graphics.DrawRectangle(Pens.Black, 0, 0, 15, 15);
                }

                this.backgroundColorMenuItem.Image = bitmap;

                this.backgroundImage = null;
            }
        }

        #endregion
        #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.backgroundImage = GetBitmap(this.openFileDialog.FileName);

                this.witdhTextBox.Text  = this.backgroundImage.Width.ToString();
                this.heightTextBox.Text = this.backgroundImage.Height.ToString();
            }
        }

        #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)
            {
                try
                {
                    SaveImage(this.resultPictureBox.Image, this.saveFileDialog.FileName);
                }
                catch(Exception exception)
                {
                    MessageBox.Show(exception.Message);
                }
            }
        }

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

        /// <summary>
        /// 그리기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void drawButton_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;

            DrawSpiral();

            Cursor = Cursors.Default;
        }

        #endregion

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

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

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

                return null;
            }
        }

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

        /// <summary>
        /// 이미지 저장하기
        /// </summary>
        /// <param name="image">이미지</param>
        /// <param name="filePath">파일 경로</param>
        private void SaveImage(Image image, string filePath)
        {
            try
            {
                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("Unknown file extension " + extension);
                }
            }
            catch(Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        #endregion
        #region 매개 변수 검증하기 - ValidateParameter(width, height, scale, a, b, deltaTheta, spiralCount)

        /// <summary>
        /// 매개 변수 검증하기
        /// </summary>
        /// <param name="width">너비</param>
        /// <param name="height">높이</param>
        /// <param name="scale">스케일</param>
        /// <param name="a">A</param>
        /// <param name="b">B</param>
        /// <param name="deltaTheta">델타 세타</param>
        /// <param name="spiralCount">나선 수</param>
        /// <returns>검증 결과</returns>
        private bool ValidateParameter
        (
            out int   width,
            out int   height,
            out float scale,
            out float a,
            out float b,
            out float deltaTheta,
            out int   spiralCount
        )
        {
            width       = 0;
            height      = 0;
            scale       = 0;
            a           = 0;
            b           = 0;
            deltaTheta  = 0;
            spiralCount = 0;

            if(!int.TryParse(this.witdhTextBox.Text, out width) || (width <= 0))
            {
                MessageBox.Show("Please enter a width greater than zero.");

                this.witdhTextBox.Focus();

                return true;
            }

            if(!int.TryParse(this.heightTextBox.Text, out height) || (height <= 0))
            {
                MessageBox.Show("Please enter a height greater than zero.");

                this.heightTextBox.Focus();

                return true;
            }

            if(!float.TryParse(this.scaleTextBox.Text, out scale) || (Math.Abs(scale) <= 0.01))
            {
                MessageBox.Show("Please enter a non-zero scale.");

                this.scaleTextBox.Focus();

                return true;
            }

            if(this.sourceBitmap == null)
            {
                MessageBox.Show("Please select a base image.");

                return true;
            }

            if(!float.TryParse(this.aTextBox.Text, out a))
            {
                MessageBox.Show("Please enter parameter A.");

                return true;
            }

            if(!float.TryParse(this.bTextBox.Text, out b))
            {
                MessageBox.Show("Please enter parameter B.");

                return true;
            }

            if(!float.TryParse(this.deltaThetaTextBox.Text, out deltaTheta))
            {
                MessageBox.Show("Please enter parameter DTheta.");

                return true;
            }

            deltaTheta *= (float)(Math.PI / 180);

            if(!int.TryParse(this.spiralCountTextBox.Text, out spiralCount) || spiralCount <= 0)
            {
                MessageBox.Show("Please enter a number if spirals greater than zero.");

                return true;
            }

            return false;
        }

        #endregion
        #region 극 좌표를 직교 좌표로 변환하기 - ConvertPolarToCartesian(radius, theta, x, y)

        /// <summary>
        /// 극 좌표를 직교 좌표로 변환하기
        /// </summary>
        /// <param name="radius">반경</param>
        /// <param name="theta">세타</param>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        private void ConvertPolarToCartesian(float radius, float theta, out float x, out float y)
        {
            x = (float)(radius * Math.Cos(theta));
            y = (float)(radius * Math.Sin(theta));
        }

        #endregion
        #region 나선 포인트 리스트 구하기 - GetSpiralPointList(centerPoint, a, b, offsetAngle, deltaTheta, maximumRadius)

        /// <summary>
        /// 나선 포인트 리스트 구하기
        /// </summary>
        /// <param name="centerPoint">중심점</param>
        /// <param name="a">A</param>
        /// <param name="b">B</param>
        /// <param name="offsetAngle">오프셋 각도</param>
        /// <param name="deltaTheta">델타 세타</param>
        /// <param name="maximumRadius">최대 반경</param>
        /// <returns>나선 포인트 리스트</returns>
        private List<PointF> GetSpiralPointList
        (
            PointF centerPoint,
            float  a,
            float  b,
            float  offsetAngle,
            float  deltaTheta,
            float  maximumRadius
        )
        {
            List<PointF> pointList = new List<PointF>();

            float minimumTheta = (float)(Math.Log(0.1 / a) / b);

            for(float theta = minimumTheta; ; theta += deltaTheta)
            {
                float radius = (float)(a * Math.Exp(b * theta));

                float x, y;

                ConvertPolarToCartesian(radius, theta + offsetAngle, out x, out y);

                x += centerPoint.X;
                y += centerPoint.Y;

                pointList.Add(new PointF((float)x, (float)y));

                if(radius > maximumRadius)
                {
                    break;
                }
            }

            return pointList;
        }

        #endregion
        #region 거리 구하기 - GetDistance(point1, point2)

        /// <summary>
        /// 거리 구하기
        /// </summary>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <returns>거리</returns>
        private float GetDistance(PointF point1, PointF point2)
        {
            float deltaX = point1.X - point2.X;
            float deltaY = point1.Y - point2.Y;

            return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
        }

        #endregion
        #region 비트맵 밝기 조정하기 - AdjustBitmapBrightness(sourceImage, brightness)

        /// <summary>
        /// 비트맵 밝기 조정하기
        /// </summary>
        /// <param name="sourceImage">소스 이미지</param>
        /// <param name="brightness">밝기</param>
        /// <returns>비트맵</returns>
        private Bitmap AdjustBitmapBrightness(Image sourceImage, float brightness)
        {
            ColorMatrix colorMatrix = new ColorMatrix
            (
                new float[][]
                {
                    new float[] {brightness, 0         , 0         , 0, 0},
                    new float[] {0         , brightness, 0         , 0, 0},
                    new float[] {0         , 0         , brightness, 0, 0},
                    new float[] {0         , 0         , 0         , 1, 0},
                    new float[] {0         , 0         , 0         , 0, 1}
                }
            );

            ImageAttributes imageAttributes = new ImageAttributes();

            imageAttributes.SetColorMatrix(colorMatrix);

            Point[] pointArray =
            {
                new Point(0                , 0                 ),
                new Point(sourceImage.Width, 0                 ),
                new Point(0                , sourceImage.Height)
            };

            Rectangle rectangle = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);

            Bitmap targetBitmap = new Bitmap(sourceImage.Width, sourceImage.Height);

            using(Graphics graphics = Graphics.FromImage(targetBitmap))
            {
                graphics.DrawImage(sourceImage, pointArray, rectangle, GraphicsUnit.Pixel, imageAttributes);
            }

            return targetBitmap;
        }

        #endregion
        #region 이미지 그리기 - DrawImageAt(graphics, centerPoint, point, radius, imageScale)

        /// <summary>
        /// 이미지 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="centerPoint">중심점</param>
        /// <param name="point">포인트</param>
        /// <param name="radius">반경</param>
        /// <param name="imageScale">이미지 스케일</param>
        private void DrawImageAt(Graphics graphics, PointF centerPoint, PointF point, float radius, float imageScale)
        {
            float deltaX = point.X - centerPoint.X;
            float deltaY = point.Y - centerPoint.Y;

            float angle = (float)(Math.Atan2(deltaY, deltaX) * 180 / Math.PI) + 90;

            float scaleX = radius / this.sourceBitmap.Width * imageScale;
            float scaleY = radius / this.sourceBitmap.Height * imageScale;
            float scale  = Math.Min(scaleX, scaleY);

            GraphicsState state = graphics.Save();

            graphics.ResetTransform();
            graphics.ScaleTransform(scale, scale);
            graphics.RotateTransform(angle, MatrixOrder.Append);
            graphics.TranslateTransform(point.X, point.Y, MatrixOrder.Append);

            PointF[] targetPointArray =
            {
                new PointF(-this.sourceBitmap.Width / 2, -this.sourceBitmap.Height / 2),
                new PointF( this.sourceBitmap.Width / 2, -this.sourceBitmap.Height / 2),
                new PointF(-this.sourceBitmap.Width / 2,  this.sourceBitmap.Height / 2)
            };

            RectangleF sourceRectangle = new RectangleF(0, 0, this.sourceBitmap.Width, this.sourceBitmap.Height);

            if(this.darkenCheckBox.Checked)
            {
                float darkenRadius = Math.Min(centerPoint.X, centerPoint.Y) / 3f;
                float darkenScale  = radius / darkenRadius + 0.2f;

                if(darkenScale > 1)
                {
                    darkenScale = 1;
                }

                Bitmap bitmap = AdjustBitmapBrightness(this.sourceBitmap, darkenScale);

                graphics.DrawImage(bitmap, targetPointArray, sourceRectangle, GraphicsUnit.Pixel);
            }
            else
            {
                graphics.DrawImage(sourceBitmap, targetPointArray, sourceRectangle, GraphicsUnit.Pixel);
            }

            graphics.Restore(state);
        }

        #endregion
        #region 나선 이미지 그리기 - DrawSpiralImage(graphics, bitmap, width, height, scale, a, b, deltaTheta, spiralCount)

        /// <summary>
        /// 나선 이미지 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="bitmap">비트맵</param>
        /// <param name="width">너비</param>
        /// <param name="height">높이</param>
        /// <param name="scale">스케일</param>
        /// <param name="a">A</param>
        /// <param name="b">B</param>
        /// <param name="deltaTheta">델타 세타</param>
        /// <param name="spiralCount">나선 수</param>
        private void DrawSpiralImage
        (
            Graphics graphics,
            Bitmap   bitmap,
            int      width,
            int      height,
            float    scale,
            float    a,
            float    b,
            float    deltaTheta,
            int      spiralCount
        )
        {
            PointF centerPoint = new PointF(width / 2, height / 2);

            float maximumRadius = (float)Math.Sqrt(centerPoint.X * centerPoint.X + centerPoint.Y * centerPoint.Y) + this.sourceBitmap.Height;

            float startAngle = 0;
            float deltaAngle = (float)(2 * Math.PI / spiralCount);

            for(int i = 0; i < spiralCount; i++)
            {
                List<PointF> pointList = GetSpiralPointList(centerPoint, a, b, startAngle, deltaTheta, maximumRadius);

                foreach(PointF point in pointList)
                {
                    float distance = GetDistance(centerPoint, point);

                    float radius = distance / 2;

                    DrawImageAt(graphics, centerPoint, point, radius, scale);
                }

                startAngle += deltaAngle;
            }
        }

        #endregion
        #region 나선 그리기 - DrawSpiral()

        /// <summary>
        /// 나선 그리기
        /// </summary>
        private void DrawSpiral()
        {
            int   width;
            int   height;
            int   spiralCount;
            float scale;
            float a;
            float b;
            float deltaTheta;

            if(ValidateParameter(out width, out height, out scale, out a, out b, out deltaTheta, out spiralCount))
            {
                return;
            }

            this.resultPictureBox.ClientSize = new Size(width, height);

            int clientWidth  = Math.Min(this.containerPanel.Left + this.resultPictureBox.Width + this.containerPanel.Left, 800);
            int clientHeight = Math.Min(this.containerPanel.Top  + this.resultPictureBox.Width + this.containerPanel.Left, 800);

            if(clientWidth < ClientSize.Width)
            {
                clientWidth = ClientSize.Width;
            }

            if(clientHeight < ClientSize.Height)
            {
                clientHeight = ClientSize.Height;
            }

            ClientSize = new Size(clientWidth, clientHeight);

            Bitmap bitmap = new Bitmap(width, height);

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.SmoothingMode = SmoothingMode.AntiAlias;

                if(backgroundImage != null)
                {
                    using(Brush brush = new TextureBrush(backgroundImage))
                    {
                        graphics.FillRectangle(brush, 0, 0, width, height);
                    }
                }
                else
                {
                    graphics.Clear(this.backgroundColor);
                }

                DrawSpiralImage(graphics, sourceBitmap, width, height, scale, a, b, deltaTheta, spiralCount);
            }

            this.resultPictureBox.Image = bitmap;
        }

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

댓글을 달아 주세요