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

TestProject.zip
다운로드

▶ StripInfo.cs

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

namespace TestProject
{
    /// <summary>
    /// 스트립 정보
    /// </summary>
    [Serializable]
    public class StripInfo
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 스트립 배열
        /// </summary>
        public int[][] StripArray { get; private set; }

        /// <summary>
        /// 너비
        /// </summary>
        public int Width { get; private set; }

        /// <summary>
        /// 높이
        /// </summary>
        public int Height { get; private set; }

        /// <summary>
        /// 스케일
        /// </summary>
        public float Scale { get; private set; }

        /// <summary>
        /// 소스 너비
        /// </summary>
        public int SourceWidth { get; private set; }

        /// <summary>
        /// 소스 높이
        /// </summary>
        public int SourceHeight { get; private set; }

        /// <summary>
        /// 수직 여부
        /// </summary>
        public bool IsVertical { get; private set; }

        /// <summary>
        /// 최대 높이
        /// </summary>
        public const int MAXIMUM_HEIGHT = 600;

        #endregion

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

        #region 생성자 - StripInfo(sourceBitmap, stripCount, isVertical)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="stripCount">스트립 카운트</param>
        /// <param name="isVertical">수직 여부</param>
        public StripInfo(Bitmap sourceBitmap, int stripCount, bool isVertical = false)
        {
            SourceWidth  = sourceBitmap.Width;
            SourceHeight = sourceBitmap.Height;
            IsVertical   = isVertical;

            Scale = SourceHeight > MAXIMUM_HEIGHT ? 1f * MAXIMUM_HEIGHT / SourceHeight : 1f;

            Width  = stripCount;
            Height = (int)((IsVertical ? SourceWidth : SourceHeight) * Scale);

            int targetWidth  = isVertical ? Height : Width;
            int targetHeight = isVertical ? Width  : Height;

            using(Bitmap targetBitmap = new Bitmap(targetWidth, targetHeight))
            {
                using(Graphics targetGraphics = Graphics.FromImage(targetBitmap))
                {
                    targetGraphics.InterpolationMode = InterpolationMode.Low;

                    targetGraphics.DrawImage(sourceBitmap, 0, 0, targetWidth, targetHeight);

                    using(BitmapWrapper bitmapWrapper = new BitmapWrapper(targetBitmap, true))
                    {
                        StripArray = new int[Width][];

                        for(int x = 0; x < StripArray.Length; x++)
                        {
                            int[] stripHeightArray = StripArray[x] = new int[Height];

                            if(isVertical)
                            {
                                for(int y = 0; y < stripHeightArray.Length; y++)
                                {
                                    stripHeightArray[y] = bitmapWrapper[y, x].G;
                                }
                            }
                            else
                            {
                                for(int y = 0; y < stripHeightArray.Length; y++)
                                {
                                    stripHeightArray[y] = bitmapWrapper[x, y].G;
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion
    }
}

 

728x90

 

▶ BitmapWrapper.cs

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

namespace TestProject
{
    /// <summary>
    /// 비트맵 래퍼
    /// </summary>
    public class BitmapWrapper : IDisposable, IEnumerable<Point>
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

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

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

        /// <summary>
        /// 소스 바이트 배열
        /// </summary>
        private byte[] sourceByteArray;

        /// <summary>
        /// 복사 바이트 배열
        /// </summary>
        private byte[] copyByteArray;

        /// <summary>
        /// 스트라이드
        /// </summary>
        private int stride;

        #endregion

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

        #region 비트맵 너비 - BitmapWidth

        /// <summary>
        /// 비트맵 너비
        /// </summary>
        public int BitmapWidth { get; private set; }

        #endregion
        #region 비트맵 높이 - BitmapHeight

        /// <summary>
        /// 비트맵 높이
        /// </summary>
        public int BitmapHeight { get; private set; }

        #endregion
        #region 디폴트 색상 - DefaultColor

        /// <summary>
        /// 디폴트 색상
        /// </summary>
        public Color DefaultColor { get; set; }

        #endregion

        #region 인덱서 - this[x, y]

        /// <summary>
        /// 인덱서
        /// </summary>
        /// <param name="x">X 좌표</param>
        /// <param name="y">Y 좌표</param>
        /// <returns>색상</returns>
        public Color this[int x, int y]
        {
            get
            {
                int i = GetIndex(x, y);

                return i < 0 ? DefaultColor : Color.FromArgb(sourceByteArray[i + 3], sourceByteArray[i + 2], sourceByteArray[i + 1], sourceByteArray[i]);
            }
            set
            {
                int i = GetIndex(x, y);

                if(i >= 0)
                {
                    copyByteArray[i    ] = value.B;
                    copyByteArray[i + 1] = value.G;
                    copyByteArray[i + 2] = value.R;
                    copyByteArray[i + 3] = value.A;
                };
            }
        }

        #endregion
        #region 인덱서 - this[point]

        /// <summary>
        /// 인덱서
        /// </summary>
        /// <param name="point">포인트</param>
        /// <returns>색상</returns>
        public Color this[Point point]
        {
            get
            {
                return this[point.X, point.Y];
            }
            set
            {
                this[point.X, point.Y] = value;
            }
        }

        #endregion

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

        #region 생성자 - BitmapWrapper(sourceBitmap, copySourceBitmap)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="copySourceBitmap">소스 비트맵 복사 여부</param>
        public BitmapWrapper(Bitmap sourceBitmap, bool copySourceBitmap = false)
        {
            BitmapWidth  = sourceBitmap.Width;
            BitmapHeight = sourceBitmap.Height;

            this.sourceBitmap = sourceBitmap;

            this.sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadWrite,
                PixelFormat.Format32bppArgb
            );

            this.stride = this.sourceBitmapData.Stride;

            this.sourceByteArray = new byte[this.stride * BitmapHeight];

            Marshal.Copy(this.sourceBitmapData.Scan0, this.sourceByteArray, 0, this.sourceByteArray.Length);

            this.copyByteArray = copySourceBitmap ? (byte[])sourceByteArray.Clone() : new byte[this.stride * BitmapHeight];
        }

        #endregion

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

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

        /// <summary>
        /// 픽셀 설정하기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <param name="red">빨간색</param>
        /// <param name="green">녹색</param>
        /// <param name="blue">파란색</param>
        public void SetPixel(Point point, double red, double green, double blue)
        {
            if(red < 0)
            {
                red = 0;
            }

            if(red >= 256)
            {
                red = 255;
            }

            if(green < 0)
            {
                green = 0;
            }

            if(green >= 256)
            {
                green = 255;
            }

            if(blue < 0)
            {
                blue = 0;
            }

            if(blue >= 256)
            {
                blue = 255;
            }

            this[point.X, point.Y] = Color.FromArgb((int)red, (int)green, (int)blue);
        }

        #endregion
        #region 교환하기 - Swap()

        /// <summary>
        /// 교환하기
        /// </summary>
        public void Swap()
        {
            byte[] temporaryByteArray = sourceByteArray;

            sourceByteArray = copyByteArray;

            copyByteArray = temporaryByteArray;
        }

        #endregion
        #region 열거자 구하기 - GetEnumerator()

        /// <summary>
        /// 열거자 구하기
        /// </summary>
        /// <returns>포인트 열거자</returns>
        public IEnumerator<Point> GetEnumerator()
        {
            for(int y = 0; y < BitmapHeight; y++)
            {
                for(int x = 0; x < BitmapWidth; x++)
                {
                    yield return new Point(x, y);
                }
            }
        }

        #endregion
        #region (IEnumerable) 열거자 구하기 - GetEnumerator()

        /// <summary>
        /// 열거자 구하기
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
        #region 리소스 해제하기 - Dispose()

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        public void Dispose()
        {
            Marshal.Copy(copyByteArray, 0, sourceBitmapData.Scan0, copyByteArray.Length);

            sourceBitmap.UnlockBits(sourceBitmapData);
        }

        #endregion

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

        #region 인덱스 구하기 - GetIndex(x, y)

        /// <summary>
        /// 인덱스 구하기
        /// </summary>
        /// <param name="x">X 좌표</param>
        /// <param name="y">Y 좌표</param>
        /// <returns></returns>
        private int GetIndex(int x, int y)
        {
            return (x < 0 || x >= BitmapWidth || y < 0 || y >= BitmapHeight) ? -1 : x * 4 + y * stride;
        }

        #endregion
    }
}

 

300x250

 

▶ BitmapHelper.cs

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

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

        #region 비트맵 회전하기 - RotateBitmap(sourceBitmap, angle)

        /// <summary>
        /// 비트맵 회전하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="angle">각도</param>
        /// <returns>회전 비트맵</returns>
        public static Bitmap RotateBitmap(Bitmap sourceBitmap, double angle)
        {
            Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);

            using(Graphics graphics = Graphics.FromImage(targetBitmap))
            {
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

                graphics.TranslateTransform(sourceBitmap.Width / 2, sourceBitmap.Height / 2);

                graphics.RotateTransform(-(float)angle);

                graphics.DrawImage
                (
                    sourceBitmap,
                    -sourceBitmap.Width  / 2,
                    -sourceBitmap.Height / 2,
                    sourceBitmap.Width,
                    sourceBitmap.Height
                );
            }

            return targetBitmap;
        }

        #endregion
    }
}

 

▶ SkewCalculator.cs

using System;

namespace TestProject
{
    /// <summary>
    /// SKEW 계산기
    /// </summary>
    public static class SkewCalculator
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 회전 각도 구하기 - GetRotateAngle(stripInfo, stripX1, stripX2, maximumSkew)

        /// <summary>
        /// 회전 각도 구하기
        /// </summary>
        /// <param name="stripInfo">스트립 정보</param>
        /// <param name="stripX1">스트립 X1</param>
        /// <param name="stripX2">스트립 X2</param>
        /// <param name="maximumSkew">최대 SKEW</param>
        /// <returns></returns>
        public static double GetRotateAngle(StripInfo stripInfo, int stripX1, int stripX2, int maximumSkew = 70)
        {
            double shift  = 0d;
            int    shift1 = FindBestShift(stripInfo, stripX1    , stripX2    , -maximumSkew, maximumSkew);
            int    shift2 = FindBestShift(stripInfo, stripX1 + 1, stripX2 + 1, -maximumSkew, maximumSkew);

            if(Math.Abs(shift1 - shift2) < 2)
            {
                shift = (shift1 + shift2) / 2d;
            }
            else
            {
                if(Math.Abs(shift1) < Math.Abs(shift2))
                {
                    shift = shift1;
                }
                else
                {
                    shift = shift2;
                }
            }

            return Math.Atan2
            (
                shift / stripInfo.Scale,
                (stripX2 - stripX1) * stripInfo.SourceWidth / stripInfo.Width
            );
        }

        #endregion

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

        #region 정정 값 계산하기 - CalculateCorrection(stripInfo, x1, x2, shift)

        /// <summary>
        /// 정정 값 계산하기
        /// </summary>
        /// <param name="stripInfo">스트립 정보</param>
        /// <param name="x1">X1</param>
        /// <param name="x2">X2</param>
        /// <param name="shift">시프트</param>
        /// <returns>정정 값</returns>
        private static double CalculateCorrection(StripInfo stripInfo, int x1, int x2, int shift)
        {
            int startIndex = shift < 0 ? -shift : 0;
            int endIndex   = shift > 0 ? stripInfo.Height - shift - 1 : stripInfo.Height - 1;

            int[] stripArray1 = stripInfo.StripArray[x1];
            int[] stripArray2 = stripInfo.StripArray[x2];

            int res = 0;

            for(int i = startIndex; i <= endIndex; i++)
            {
                res += -Math.Abs(stripArray1[i] - stripArray2[i + shift]);
            }

            return 1d * stripArray1.Length * res / (stripArray1.Length - Math.Abs(shift));
        }

        #endregion
        #region 최적 시프트 찾기 - FindBestShift(stripInfo, x1, x2, startShift, endShift)

        /// <summary>
        /// 최적 시프트 찾기
        /// </summary>
        /// <param name="stripInfo">스트립 정보</param>
        /// <param name="x1">X1</param>
        /// <param name="x2">X2</param>
        /// <param name="startShift">시작 시프트</param>
        /// <param name="endShift">종료 시프트</param>
        /// <returns>최적 시프트</returns>
        private static int FindBestShift(StripInfo stripInfo, int x1, int x2, int startShift, int endShift)
        {
            double bestCorrection = double.MinValue;

            int bestShift = 0;

            for(int shift = startShift; shift <= endShift; shift++)
            {
                double correction = CalculateCorrection(stripInfo, x1, x2, shift);

                if(correction > bestCorrection)
                {
                    bestCorrection = correction;

                    bestShift = shift;
                }
            }

            return bestShift;
        }

        #endregion
    }
}

 

▶ ZoomPictureBox.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 확대/축소 픽처 박스
    /// </summary>
    public class ZoomPictureBox : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 표시 중심점 변경시 이벤트 - VisibleCenterPointChanged

        /// <summary>
        /// 표시 중심점 변경시 이벤트
        /// </summary>
        public event EventHandler VisibleCenterPointChanged;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 마우스 상태 - MouseState

        /// <summary>
        /// 마우스 상태
        /// </summary>
        private enum MouseState
        {
            /// <summary>
            /// NONE
            /// </summary>
            None,
            
            /// <summary>
            /// DRAG
            /// </summary>
            Drag
        }

        #endregion

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

        #region Field

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

        /// <summary>
        /// 표시 중심점
        /// </summary>
        private PointF visibleCenterPoint;

        /// <summary>
        /// 확대/축소
        /// </summary>
        private float zoom = 1f;

        /// <summary>
        /// 마우스 상태
        /// </summary>
        private MouseState mouseState;

        /// <summary>
        /// 드래그 시작 포인트
        /// </summary>
        private Point dragStartPoint;

        /// <summary>
        /// 드래그 시작 표시 중심점
        /// </summary>
        private PointF dragStartVisibleCenterPoint;

        /// <summary>
        /// 소스 비트맵 너비
        /// </summary>
        private int sourceBitmapWidth;

        /// <summary>
        /// 소스 비트맵 높이
        /// </summary>
        private int sourceBitmapHeight;

        #endregion

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

        #region 축소/확대 델타 - ZoomDelta

        /// <summary>
        /// 축소/확대 델타
        /// </summary>
        [DefaultValue(0.1f)]
        public float ZoomDelta { get; set; }

        #endregion
        #region 사용자 드래그 허용 여부 - AllowUserDrag

        /// <summary>
        /// 사용자 드래그 허용 여부
        /// </summary>
        [DefaultValue(true)]
        public bool AllowUserDrag { get; set; }

        #endregion
        #region 사용자 확대/축소 허용 여부 - AllowUserZoom

        /// <summary>
        /// 사용자 확대/축소 허용 여부
        /// </summary>
        [DefaultValue(true)]
        public bool AllowUserZoom { get; set; }

        #endregion
        #region 보간 모드 - InterpolationMode

        /// <summary>
        /// 보간 모드
        /// </summary>
        public InterpolationMode InterpolationMode { get; set; }

        #endregion
        #region 픽셀 오프셋 모드 - PixelOffsetMode

        /// <summary>
        /// 픽셀 오프셋 모드
        /// </summary>
        public PixelOffsetMode PixelOffsetMode { get; set; }

        #endregion
        #region 소스 비트맵 - SourceBitmap

        /// <summary>
        /// 소스 비트맵
        /// </summary>
        [DefaultValue(null)]
        public Bitmap SourceBitmap
        {
            get
            {
                return this.sourceBitmap;
            }
            set
            {
                this.sourceBitmap = value;

                if(value == null)
                {
                    this.sourceBitmapWidth  = 0;
                    this.sourceBitmapHeight = 0;

                    VisibleCenterPoint = new PointF(0, 0);
                }
                else
                {
                    this.sourceBitmapWidth  = value.Width;
                    this.sourceBitmapHeight = value.Height;

                    VisibleCenterPoint = new PointF(this.sourceBitmapWidth / 2f, this.sourceBitmapHeight / 2f);
                }

                Invalidate();
            }
        }

        #endregion
        #region 확대/축소 - Zoom

        /// <summary>
        /// 확대/축소
        /// </summary>
        [DefaultValue(1f)]
        public float Zoom
        {
            get
            {
                return this.zoom;
            }

            set
            {
                if(Math.Abs(value) <= float.Epsilon)
                {
                    throw new Exception("Zoom must be more then 0");
                }

                this.zoom = value;

                Invalidate();
            }
        }

        #endregion
        #region 표시 중심 포인트 - VisibleCenterPoint

        /// <summary>
        /// 표시 중심 포인트
        /// </summary>
        public PointF VisibleCenterPoint
        {
            get
            {
                return this.visibleCenterPoint;
            }

            set
            {
                this.visibleCenterPoint = value;

                OnVisibleCenterPointChanged();
            }
        }

        #endregion

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

        #region 생성자 - ZoomPictureBox()

        /// <summary>
        /// 생성자
        /// </summary>
        public ZoomPictureBox()
        {
            SetStyle
            (
                ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw,
                true
            );

            ZoomDelta = 0.1f;

            AllowUserDrag = true;

            AllowUserZoom = true;

            InterpolationMode = InterpolationMode.Bicubic;
            PixelOffsetMode   = PixelOffsetMode.HighQuality;
        }

        #endregion

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

        #region 표시 중심 포인트 변경시 처리하기 - OnVisibleCenterPointChanged()

        /// <summary>
        /// 표시 중심 포인트 변경시 처리하기
        /// </summary>
        public virtual void OnVisibleCenterPointChanged()
        {
            VisibleCenterPointChanged?.Invoke(this, EventArgs.Empty);

            Invalidate();
        }

        #endregion
        #region 비트맵 업데이트 하기 - UpdateBitmap(sourceBitmap)

        /// <summary>
        /// 비트맵 업데이트 하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        public void UpdateBitmap(Bitmap sourceBitmap)
        {
            this.sourceBitmap = sourceBitmap;

            if(sourceBitmap != null)
            {
                this.sourceBitmapWidth  = sourceBitmap.Width;
                this.sourceBitmapHeight = sourceBitmap.Height;
            }

            Invalidate();
        }

        #endregion

        #region 축소하기 - DecreaseZoom()

        /// <summary>
        /// 축소하기
        /// </summary>
        public void DecreaseZoom()
        {
            Zoom = (float)Math.Exp(Math.Log(this.zoom) - ZoomDelta);
        }

        #endregion
        #region 확대하기 - IncreazeZoom()

        /// <summary>
        /// 확대하기
        /// </summary>
        public void IncreazeZoom()
        {
            Zoom = (float)Math.Exp(Math.Log(this.zoom) + ZoomDelta);
        }

        #endregion

        #region 비트맵 포인트 구하기 - GetBitmapPoint(clientPoint)

        /// <summary>
        /// 비트맵 포인트 구하기
        /// </summary>
        /// <param name="clientPoint">클라이언트 포인트</param>
        /// <returns>비트맵 포인트</returns>
        public Point GetBitmapPoint(Point clientPoint)
        {
            float deltaX = (clientPoint.X - ClientSize.Width  / 2f) / Zoom + this.visibleCenterPoint.X;
            float deltaY = (clientPoint.Y - ClientSize.Height / 2f) / Zoom + this.visibleCenterPoint.Y;

            return new Point((int)deltaX, (int)deltaY);
        }

        #endregion
        #region 클라이언트 포인트 구하기 - GetClientPoint(bitmapPoint)

        /// <summary>
        /// 클라이언트 포인트 구하기
        /// </summary>
        /// <param name="bitmapPoint">비트맵 포인트</param>
        /// <returns>클라이언트 포인트</returns>
        public Point GetClientPoint(Point bitmapPoint)
        {
            float deltaX = (bitmapPoint.X - visibleCenterPoint.X) * Zoom + ClientSize.Width  / 2f;
            float deltaY = (bitmapPoint.Y - visibleCenterPoint.Y) * Zoom + ClientSize.Height / 2f;

            return new Point((int)deltaX, (int)deltaY);
        }

        #endregion

        #region 스크린샷 구하기 - GetScreenshot()

        /// <summary>
        /// 스크린샷 구하기
        /// </summary>
        /// <returns>스크린샷</returns>
        public Image GetScreenshot()
        {
            Image targetImage = new Bitmap(ClientSize.Width, ClientSize.Height);

            using(Graphics targetGraphics = Graphics.FromImage(targetImage))
            {
                OnPaint(new PaintEventArgs(targetGraphics, ClientRectangle));
            }

            return targetImage;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 마우스 휠 처리하기 - OnMouseWheel(e)

        /// <summary>
        /// 마우스 휠 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if(!AllowUserZoom)
            {
                return;
            }

            if(e.Delta > 0)
            {
                IncreazeZoom();
            }

            if(e.Delta < 0)
            {
                DecreaseZoom();
            }
        }

        #endregion
        #region 마우스 DOWN 처리하기 - OnMouseDown(e)

        /// <summary>
        /// 마우스 DOWN 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if(AllowUserDrag)
            {
                if(e.Button == MouseButtons.Left)
                {
                    Cursor = Cursors.SizeAll;

                    this.mouseState = MouseState.Drag;
                }
            }

            this.dragStartPoint = e.Location;

            this.dragStartVisibleCenterPoint = VisibleCenterPoint;
        }

        #endregion
        #region 마우스 이동시 처리하기 - OnMouseMove(e)

        /// <summary>
        /// 마우스 이동시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if(mouseState == MouseState.Drag)
            {
                int deltaX = e.Location.X - this.dragStartPoint.X;
                int deltaY = e.Location.Y - this.dragStartPoint.Y;

                VisibleCenterPoint = new PointF
                (
                    this.dragStartVisibleCenterPoint.X - deltaX / this.zoom,
                    this.dragStartVisibleCenterPoint.Y - deltaY / this.zoom
                );
            }
        }

        #endregion
        #region 마우스 UP 처리하기 - OnMouseUp(e)

        /// <summary>
        /// 마우스 UP 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            Cursor = Cursors.Default;

            this.mouseState = MouseState.None;

            Invalidate();
        }

        #endregion

        #region 페인트시 처리하기 - OnPaint(e)

        /// <summary>
        /// 페인트시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if(this.sourceBitmap == null)
            {
                return;
            }

            e.Graphics.ResetTransform();

            e.Graphics.InterpolationMode = InterpolationMode;
            e.Graphics.PixelOffsetMode   = PixelOffsetMode;

            if(this.mouseState == MouseState.Drag || Zoom < 1f)
            {
                e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
            }

            float deltaX = -this.visibleCenterPoint.X * Zoom + ClientSize.Width  / 2f;
            float deltaY = -this.visibleCenterPoint.Y * Zoom + ClientSize.Height / 2f;

            e.Graphics.DrawImage
            (
                this.sourceBitmap,
                deltaX,
                deltaY,
                this.sourceBitmap.Width  * Zoom,
                this.sourceBitmap.Height * Zoom
            );

            base.OnPaint(e);
        }

        #endregion
    }
}

 

▶ MainForm.cs

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// 랴디
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 스트립 카운트
        /// </summary>
        private int STRIP_COUNT = 10;

        #endregion

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

        #region 생성자 - MainForm()

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

            this.sourcePictureBox.Paint += pictureBox_Paint;
            this.targetPictureBox.Paint += pictureBox_Paint;
        }

        #endregion

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

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

        /// <summary>
        /// 파일 열기 메뉴 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void openFileMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "Image|*.png;*.jpeg;*.jpg;*.tif" };

            if(openFileDialog.ShowDialog() == DialogResult.OK)
            {
                ProcessBitmap(openFileDialog.FileName);
            }
        }

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

        /// <summary>
        /// 픽처 박스 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            using(var pen = new Pen(Color.FromArgb(20, Color.Black)))
            {
                for(int i = 0; i < 1000; i += 30)
                {
                    e.Graphics.DrawLine(pen, 0, i, 1000, i);
                }
            }
        }

        #endregion

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

        #region 비트맵 처리하기 - ProcessBitmap(filePath)

        /// <summary>
        /// 비트맵 처리하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        private void ProcessBitmap(string filePath)
        {
            Bitmap sourceBitmap = Image.FromFile(filePath) as Bitmap;

            this.sourcePictureBox.SourceBitmap = sourceBitmap.Clone() as Bitmap;

            StripInfo stripInfo = new StripInfo(sourceBitmap, STRIP_COUNT);

            int stripX1 = 2;
            int stripX2 = 6;

            double angle = SkewCalculator.GetRotateAngle(stripInfo, stripX1, stripX2);

            angle = (angle * 180 / Math.PI);

            Text = "스캔 이미지 왜곡 자동 보정하기 : 각도 = " + angle.ToString("0.00");

            Bitmap targetBitmap = BitmapHelper.RotateBitmap(sourceBitmap, angle);

            this.targetPictureBox.SourceBitmap = targetBitmap;
        }

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

댓글을 달아 주세요