첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.

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.Windows.Forms;

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

        #region Field

        /// <summary>
        /// 오프셋
        /// </summary>
        private const int OFFSET = 10;

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

        /// <summary>
        /// 일반 투명 비트맵
        /// </summary>
        private Bitmap normalTransparentBitmap = null;

        /// <summary>
        /// 자연스러운 투명 비트맵
        /// </summary>
        private Bitmap naturalTransparentBitmap = null;

        #endregion

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

        #region 생성자 - MainForm()

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

            this.openMenuItem.Click          += openMenuItem_Click;
            this.saveMenuItem.Click          += saveMenuItem_Click;
            this.resetMenuItem.Click         += resetMenuItem_Click;
            this.expandButton.Click           += expandButton_Click;
            this.sourcePictureBox.MouseClick += sourcePictureBox_MouseClick;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Function

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

                this.normalTransparentBitmap  = new Bitmap(this.sourceBitmap);
                this.naturalTransparentBitmap = new Bitmap(this.sourceBitmap);

                ShowSampleBitmap();

                this.expandButton.Enabled = true;
            }
        }

        #endregion
        #region 저장 메뉴 항목 클릭시 처리하기 - saveMenuItem_Click(sender, e)

        /// <summary>
        /// 저장 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void saveMenuItem_Click(object sender, EventArgs e)
        {
            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.naturalTransparentBitmap.Save(this.saveFileDialog.FileName, ImageFormat.Png);
            }
        }

        #endregion
        #region 재설정 메뉴 항목 클릭시 처리하기 - resetMenuItem_Click(sender, e)

        /// <summary>
        /// 재설정 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void resetMenuItem_Click(object sender, EventArgs e)
        {
            if(MessageBox.Show("Discard changes?", "Discard changes?", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                this.normalTransparentBitmap  = new Bitmap(this.sourceBitmap);
                this.naturalTransparentBitmap = new Bitmap(this.sourceBitmap);

                ShowSampleBitmap();
            }
        }

        #endregion
        #region 투명성 확장하기 버튼 클릭시 처리하기 - applyButton_Click(sender, e)

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

            float maximumDistance = float.Parse(this.maximumDistanceTextBox.Text);

            this.naturalTransparentBitmap = ExpandTransparency(this.naturalTransparentBitmap, maximumDistance);

            ShowSampleBitmap();

            Cursor = Cursors.Default;
        }

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

        /// <summary>
        /// 소스 픽처 박스 마우스 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void sourcePictureBox_MouseClick(object sender, MouseEventArgs e)
        {
            int x = e.X - OFFSET;
            int y = e.Y - OFFSET;

            Color color = this.sourceBitmap.GetPixel(x, y);

            this.normalTransparentBitmap.MakeTransparent(color);

            int deltaRed   = int.Parse(this.deltaRedTextBox.Text  );
            int deltaGreen = int.Parse(this.deltaGreenTextBox.Text);
            int deltaBlue  = int.Parse(this.deltaBlueTextBox.Text );

            this.naturalTransparentBitmap = Transparentify(this.naturalTransparentBitmap, x, y, deltaRed, deltaGreen, deltaBlue);

            ShowSampleBitmap();
        }

        #endregion

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

        #region 잠금없이 비트맵 로드하기 - LoadBitmapUnlocked(filePath)

        /// <summary>
        /// 잠금없이 비트맵 로드하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>비트맵</returns>
        private Bitmap LoadBitmapUnlocked(string filePath)
        {
            using(Bitmap bitmap = new Bitmap(filePath))
            {
                return new Bitmap(bitmap);
            }
        }

        #endregion
        #region 배경 설정하기 - SetBackground()

        /// <summary>
        /// 배경 설정하기
        /// </summary>
        /// <returns>비트맵</returns>
        private Bitmap SetBackground()
        {
            int width  = this.sourceBitmap.Width  + 2 * OFFSET;
            int height = this.sourceBitmap.Height + 2 * OFFSET;

            Bitmap bitmap = new Bitmap(width, height);

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                using
                (
                    LinearGradientBrush brush = new LinearGradientBrush
                    (
                        new Point(0, 0),
                        new Point(64, 64),
                        Color.Blue,
                        Color.Yellow
                    )
                )
                {
                    graphics.FillRectangle(brush, 0, 0, width, height);
                }
            }

            return bitmap;
        }

        #endregion
        #region 샘플 비트맵 표시하기 - ShowSampleBitmap()

        /// <summary>
        /// 샘플 비트맵 표시하기
        /// </summary>
        private void ShowSampleBitmap()
        {
            Bitmap bitmap;

            bitmap = SetBackground();

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.DrawImage(this.sourceBitmap, OFFSET, OFFSET);
            }

            this.sourcePictureBox.Image = bitmap;

            bitmap = SetBackground();

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.DrawImage(this.normalTransparentBitmap, OFFSET, OFFSET);
            }

            this.normalPictureBox.Image = bitmap;

            bitmap = SetBackground();

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.DrawImage(this.naturalTransparentBitmap, OFFSET, OFFSET);
            }

            this.naturalPictureBox.Image = bitmap;

            this.sourcePictureBox.Visible  = true;
            this.normalPictureBox.Visible  = true;
            this.naturalPictureBox.Visible = true;
        }

        #endregion
        #region 투명하게 만들기 - Transparentify(sourceBitmap, x, y, deltaRed, deltaGreen, deltaBlue)

        /// <summary>
        /// 투명하게 만들기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="deltaRed">적색 증분</param>
        /// <param name="deltaGreen">녹색 증분</param>
        /// <param name="deltaBlue">파란색 증분</param>
        /// <returns>비트맵</returns>
        private Bitmap Transparentify(Bitmap sourceBitmap, int x, int y, int deltaRed, int deltaGreen, int deltaBlue)
        {
            Color targetColor = sourceBitmap.GetPixel(x, y);

            byte red   = targetColor.R;
            byte green = targetColor.G;
            byte blue  = targetColor.B;

            Bitmap bitmap = new Bitmap(sourceBitmap);

            Stack<Point> pointStack = new Stack<Point>();

            int width  = sourceBitmap.Width;
            int height = sourceBitmap.Height;

            bool[,] addedStackArray = new bool[width, height];

            pointStack.Push(new Point(x, y));

            addedStackArray[x, y] = true;

            bitmap.SetPixel(x, y, Color.Transparent);

            while(pointStack.Count > 0)
            {
                Point point = pointStack.Pop();

                for(int i = point.X - 1; i <= point.X + 1; i++)
                {
                    for(int j = point.Y - 1; j <= point.Y + 1; j++)
                    {
                        if((i < 0) || (i >= width) || (j < 0) || (j >= height))
                        {
                            continue;
                        }

                        if(addedStackArray[i, j])
                        {
                            continue;
                        }

                        Color color = sourceBitmap.GetPixel(i, j);

                        if(Math.Abs(red - color.R) > deltaRed)
                        {
                            continue;
                        }

                        if(Math.Abs(green - color.G) > deltaGreen)
                        {
                            continue;
                        }

                        if(Math.Abs(blue - color.B) > deltaBlue)
                        {
                            continue;
                        }

                        pointStack.Push(new Point(i, j));

                        addedStackArray[i, j] = true;

                        bitmap.SetPixel(i, j, Color.Transparent);
                    }
                }
            }

            return bitmap;
        }

        #endregion
        #region 거리 배열 구하기 - GetDistanceArray(sourceBitmap, maximumDistance)

        /// <summary>
        /// 거리 배열 구하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="maximumDistance">최대 거리</param>
        /// <returns>거리 배열</returns>
        private float[,] GetDistanceArray(Bitmap sourceBitmap, float maximumDistance)
        {
            int width  = sourceBitmap.Width;
            int height = sourceBitmap.Height;

            float[,] distanceArray = new float[width, height];

            for(int x = 0; x < width; x++)
            {
                for(int y = 0; y < height; y++)
                {
                    distanceArray[x, y] = float.PositiveInfinity;
                }
            }

            int maximumDistanceInteger = (int)maximumDistance;

            if(maximumDistanceInteger < maximumDistance)
            {
                maximumDistanceInteger++;
            }

            for(int x = 0; x < width; x++)
            {
                for(int y = 0; y < height; y++)
                {
                    if(sourceBitmap.GetPixel(x, y).A == 0)
                    {
                        for(int deltaX = -maximumDistanceInteger; deltaX <= maximumDistanceInteger; deltaX++)
                        {
                            int indexX = x + deltaX;

                            if((indexX < 0) || (indexX >= width))
                            {
                                continue;
                            }

                            for(int deltaY = -maximumDistanceInteger; deltaY <= maximumDistanceInteger; deltaY++)
                            {
                                int indexY = y + deltaY;

                                if((indexY < 0) || (indexY >= height))
                                {
                                    continue;
                                }

                                float distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

                                if(distanceArray[indexX, indexY] > distance)
                                {
                                    distanceArray[indexX, indexY] = distance;
                                }
                            }
                        }
                    }
                }
            }

            return distanceArray;
        }

        #endregion
        #region 투명성 확장하기 - ExpandTransparency(sourceBitmap, maximumDistance)

        /// <summary>
        /// 투명성 확장하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="maximumDistance">최대 거리</param>
        /// <returns>비트맵</returns>
        private Bitmap ExpandTransparency(Bitmap sourceBitmap, float maximumDistance)
        {
            Bitmap targetBitmap = new Bitmap(sourceBitmap);

            float[,] distanceArray = GetDistanceArray(sourceBitmap, maximumDistance);
            
            int width  = sourceBitmap.Width;
            int height = sourceBitmap.Height;

            for(int x = 0; x < width; x++)
            {
                for(int y = 0; y < height; y++)
                {
                    if(sourceBitmap.GetPixel(x, y).A == 0)
                    {
                        continue;
                    }

                    float distance = distanceArray[x, y];

                    if(distance > maximumDistance)
                    {
                        continue;
                    }

                    float scale = distance / maximumDistance;

                    Color color = sourceBitmap.GetPixel(x, y);

                    int red   = color.R;
                    int green = color.G;
                    int blue  = color.B;

                    int alpha = (int)(255 * scale);

                    color = Color.FromArgb(alpha, red, green, blue);

                    targetBitmap.SetPixel(x, y, color);
                }
            }

            return targetBitmap;
        }

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

댓글을 달아 주세요