티스토리 뷰

프로그래밍

비베 닷넷 그래픽 드로잉

야라바 2015. 10. 28. 16:25


윈도우 폼 기반으로 단순한 데이터베이스 처리 위주로 동작하는 프로그램이나 대부분의 업무용 프로그램에서는 사용 빈도가 많지 않지만 닷넷에서 제공하는 그래픽 기능을 비주얼베이직 닷넷(이하 비베닷넷)을 통해서 활용하는 방법을 알아두면 유용하게 사용할 경우가 있습니다. 비주얼스튜디오에서 제공하는 기본 컨트롤에서 지원하지 않는 인터페이스를 별도로 제작하거나 여러 프로그램에서 사용할 수 있는 공통 라이브러리를 제작하는 등에 여러모로 활용 가능성이 있습니다. 닷넷의 그래픽 기능을 최대한 활용하는 프로그램은 뭐니뭐니해도 그래픽 편집 도구가 아닐까 싶습니다. 대표적인 예로 페인트닷넷을(가볍고 빠른 이미지 편집툴 페인트닷넷 참조) 예로 들수 있습니다. 


페인트 닷넷은 가로, 세로 크기를 지정한 캔버스 위에 다양한 도형을 그리거나 사진이나 이미지를 편집하는 전형적인 그래픽 편집도구인데, 이 프로그램에서 지원하는 대부분의 기능 또한 닷넷에서 제공하는 기능에 기반하고 있습니다. 오늘 다룰 비베 닷넷 그래픽 기능을 활용하면 페인트 닷넷과 같은 이미지 편집 프로그램도 비베 닷넷으로 개발할 수 있을 것입니다.

닷넷에서의 그래픽 처리는 앞서 다룬 인쇄 기능에서(비베 닷넷 인쇄하기 참조) PrintPage 이벤트로 전달되는 Graphics 클래스를 통해서 이미지나 텍스트를 프린터로 출력하는 것과 동일한 원리를 사용합니다. 인쇄나 화면 출력 모두 GDI+를 기반으로 합니다. GDI+(Graphics Device Interface)는 실제 모니터나 프린터의 종류와 관계없이 프로그래밍을 할수 있도록 지원하는데 크게 3가지로 나눌 수 있습니다. 첫째는 원, 사각형, 선등의 2D 도형을 그리는 기능으로 비트맵 이미지와 구분하여 벡터(Vector) 그래픽이라 합니다. 두번째는 Bitmap Image 클래스를 통해서 비트맵 이미지를 다루는 것으로 이미지 변형과 이미지 파일 로딩등 다양한 기능을 지원합니다. GDI+가 지원하는 마지막 기능은 텍스트 출력과 연관되는 것으로 폰트, 크기, 스타일등을 감안한 출력을 지원하며 타이포그래피(Typopraphy)라 합니다.


■ 2D 벡터 그래픽

    Private Sub PrintDocument1_PrintPage(sender As System.Object, e As System.Drawing.Printing.PrintPageEventArgs) _
             Handles PrintDocument1.PrintPage
        e.Graphics.DrawString("인쇄 테스트 입니다. Good test!", _
                              New Font("맑은 고딕", 32, FontStyle.Bold), Brushes.Black, 10, 10)
        e.Graphics.DrawString("1/1 페이지", _
                              New Font("맑은 고딕", 10, FontStyle.Bold), Brushes.Black, 600, 800)
        e.Graphics.FillEllipse(Brushes.Red, 300, 300, 300, 50)
        e.HasMorePages = False
    End Sub

위의 코드를 보면 인쇄를 실제로 수행하는 PrintPage 이벤트에서 파라미터를 통해서 전달된 Graphics 클래스의 오브젝트를(e.Graphics) 사용하여 텍스트를 출력하고 타원을 그리는 과정을 확인할 수 있습니다. Graphics 클래스는 그림이나 텍스트를 그리기 위한 캔버스의 역할을 수행하는데 윈도우 폼이나 컨트롤의 표면에 원하는 내용을 출력하기 위해서는 먼저 폼이나 컨트롤의 표면에 해당하는 Graphics 클래스의 오브젝트가 필요합니다. 윈도우 폼의 경우에는 폼의 CreateGraphics() 메소드를 사용하면 됩니다.

Public Class Form1
    Dim grp As System.Drawing.Graphics
    Dim actbut As String
    Dim ptfrom, ptto As Point
    Dim rect As Rectangle
    Dim ptcnt As Integer = 0
    Dim ptary() As Point
    Dim mypen As Pen

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        grp = Me.CreateGraphics()
        actbut = Button1.Text
        mypen = New Pen(Color.FromArgb(0, 0, 255), 5)
    End Sub

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) _
                     Handles Button1.Click, Button4.Click, Button3.Click, Button2.Click
        actbut = sender.Text
    End Sub

    Private Sub Form1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) _
                     Handles MyBase.MouseDown
        ptfrom = New Point(e.X, e.Y)
        ptcnt = 0
    End Sub

    Private Sub Form1_MouseUp(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) _
                     Handles MyBase.MouseUp
        ptto = New Point(e.X, e.Y)
        rect = New Rectangle(Math.Min(ptto.X, ptfrom.X), Math.Min(ptto.Y, ptfrom.Y),
                            Math.Abs(ptto.X - ptfrom.X), Math.Abs(ptto.Y - ptfrom.Y))
        Select Case actbut
            Case "사각형"
                grp.DrawRectangle(Pens.Red, rect)
            Case "타원"
                grp.DrawEllipse(Pens.Navy, rect)
            Case "직선"
                grp.DrawLine(Pens.Green, ptfrom, ptto)
        End Select
    End Sub

    Private Sub Form1_MouseMove(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) _
                     Handles MyBase.MouseMove
        If actbut = "펜 그리기" And e.Button = Windows.Forms.MouseButtons.Left Then
            Dim pt As New Point(e.X, e.Y)
            ReDim Preserve ptary(ptcnt)
            ptary(ptcnt) = pt
            ptcnt += 1
            If ptcnt >= 2 Then
                grp.DrawLines(mypen, ptary)
            End If
        End If
    End Sub
End Class

폼 로드 시점에 Graphics 오브젝트를 확보하고 마우스 조작에 따라 선택한 도형을 그리는 예제입니다. 그릴 도형의 종류는 버튼으로 선택하도록 했습니다. 마우스 드래그 조작에 따라 도형을 그리기 위해서 마우스 버튼을 클릭한 위치를 시작 위치로, 마우스 버튼은 뗀 위치를 종료 시점으로 도형을 그립니다.


펜그리기의 경우 마우스 버튼을 누르고 움직이는 곳들을 선으로 연결하는 방식으로 구현합니다. 코드를 보면 MouseMove 이벤트에서 움직인 곳들을 배열로 저장하고 배열에 있는 위치 정보를 기반으로 선을 그리도록 DrawLines를 호출하고 있음을 확인할 수 있습니다.

문제는 마우스 버튼을 뗀 시점에 그린 그림들은 창의 변화에 대응을 할 수 없다는 것입니다. 


위의 그림은 예제 프로그램으로 몇가지 도형을 그린 상태에서 폼의 크기를 줄였다가 다시 원상복구시킨 것으로 그린 내용의 일부가 잘라져서 복원되지 않은 것을 확인할 수 있습니다. 이런 경우에 대비하기 위해서는 Paint 이벤트에서 필요한 내용을 그려주어야 창의 포커스가 옮겨졌다 돌아오거나 크기나 위치가 변경된 경우에도 문제없이 동작할 수 없습니다. 인쇄 과정에서 PrintPage 이벤트로 전달된 Graphics 오브젝트로 작업을 하듯이 폼의 Paint 이벤트로 전달된 Graphics 오브젝트로 각종 도형을 그립니다.

    Private Sub Form1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        Select Case actbut
            Case "사각형"
                e.Graphics.DrawRectangle(Pens.Red, rect)
            Case "타원"
                e.Graphics.DrawEllipse(Pens.Navy, rect)
            Case "직선"
                e.Graphics.DrawLine(Pens.Green, ptfrom, ptto)
            Case "펜 그리기"
                If ptcnt >= 2 Then
                    e.Graphics.DrawLines(mypen, ptary)
                End If
        End Select
    End Sub

2D 도형을 그리는 "e.Graphics.DrawRectangle(Pens.Red, rect)"등의 함수에 사용하고 있는 Pens.Red등은 시스템에 정의되어 있는 폭크기 1의 펜(Pen) 오브젝트로 [펜 그리기]에 사용한 펜은 필자가 "mypen = New Pen(Color.FromArgb(0, 0, 255), 5)" 처럼 색상과 폭크기를 지정해서 선언한 것입니다. 개발자가 인위적으로 Paint 이벤트를 발생시키려면 Invalidate() 메소드를 사용하면 됩니다. Graphics 오브젝트로 2D 벡터 그래픽에 사용할 수 있는 메소드들은 아래와 같습니다.

  • DrawArc : 호 그리기
  • DrawBezier, DrawBeziers : 베지어 곡선 그리기
  • DrawClosedCurve : 닫힌 기본 곡선 그리기
  • DrawCurve : 기본 곡선 그리기
  • DrawEllipse : 타원 그리기
  • DrawLine, DrawLines : 직선 그리기
  • DrawPath : 그래픽 패스 그리기
  • DrawPie : 파이 그리기
  • DrawPolygon : 다각형 그리기
  • DrawRectangle, DrawRectangles : 사각형 그리기
  • FillClosedCurve : 닫힌 곡선 채우기
  • FillEllipse : 타원 채우기
  • FillPath : 그래픽 패스 채우기
  • FillPie : 파이 채우기
  • FillPolygon : 다각형 채우기
  • FillRectangle, FillRectangles : 사각형 채우기
  • FillRegion : 영역 채우기
  • Clear : 캔버스 지우기

일러스트레이터나 플래시, 잉크스페이스와 벡터 그래픽 편집 프로그램에서는 선과 면을 구분하는데 Draw... 메소드는 선을 그리고 특정 영역을 채우는 Fill... 메소드들은 면을 그린다고 생각하면 됩니다. 면의 경우 브러시(Brush)를 통해서 채우기 지정하며 시스템에서는 아래와 같은 기본 브러시를 제공하고 있습니다.

  • System.Drawing.SolidBrush
  • System.Drawing.Drawing2D.LinearGradientBrush
  • System.Drawing.Drawing2D.PathGradientBrush
  • System.Drawing.Drawing2D.HatchBrush
  • System.Drawing.TextureBrush

위와 같은 브러시를 Fill...함수에 전달하면 됩니다. 선의 경우에는 앞서 선의 색상과 굵기로 Pen을 생성했는데 이를 비롯하여 선을 종류, 시작/끝 화살표 종류등을 지정할 수 있습니다.

  • PenType : SolidColor, LinearGradient, PathGradient, HatchFill, TextureFill 등의 브러시 타입과 유사한 타입을 Drawing2D.PenType으로 지정합니다.
  • StartCap, EndCap : Flat, Round, Triangle등 선의 시작/끝 모양을 Drawing2D.LineCap으로 지정할 수 있습니다. CustomStartCap, CustomEndCap으로 나름의 모양을 지정할 수도 있습니다.
  • DashStyle, DashCap, DashOffset, DashPattern : 파선의 모양을 지정하는 속성으로 DashStyle의 경우 Solid, Dot, Dash, DashDot 등을 Drawing2D.DashStyle로 지정할 수 있습니다.


■ 이미지 다루기

벡터 그래픽이 아닌 비트맵 이미지를 다루는 부분 또한 폼이나 컨트롤에서 Graphics 오브젝트를 받아서 작업을 합니다. 폼에서 이미지를 출력하는데 가장 많이 사용하는 픽처박스(PictureBox) 컨트롤에 나름의 변형을 주거나 기능을 추가하고 싶다면 폼과 마찬가지로 "PictureBox1.CreateGraphics()"와 같이 픽처박스에서 Graphics 오브젝트를 받아와야 합니다. 연관 메소드는 아래와 같습니다.

  • DrawlconDrawIconUnstretched : 아이콘 출력
  • DrawlmageDrawImageUnscaled, DrawImageUnscaledAndClipped  : 이미지 출력

출력할 이미지는 모두 System.Drawing.Icon 클래스와 System.Drawing.Image 클래스를 기반으로 다뤄집니다. 

Dim icn As New Icon("Sample.ico")

DrawIcon의 경우 Icon 클래스 생성 시점에 *.ICO 파일이나 아이콘 오브젝트를 지정하여 준비한 Icon 오브젝트를 출력합니다. DrawImage의 경우에는 Image 클래스의 FromFile(), FromStream()와 같은 메소드를 활용해서 이미지 파일을 로딩한 Image 클래스의 오브젝트로 이미지를 출력할 수 있습니다. 닷넷의 GDI+는 내부적으로 BMP, GIF, JPEG, PNG, TIFF 파일 형식을 읽고 저장할 수 있다고 합니다.

DrawImage() 메소드는 단순히 이미지를 특정 위치에 출력하는 것부터 이미지의 부분을 지정해서 출력하기, 크기를 조정해서 출력하기등 다양한 오버로드(Overload) 메소드가 있으므로 필요에 따라 적절하게 사용할 수 있습니다.


■ 텍스트 출력

GDI+에서 지원하는 텍스트 출력은 텍스트박스 컨트롤이나 레이블 컨트롤에서 다루는 텍스트와는 완전히 다르게 취급됩니다. 텍스트박스 컨트롤 등에 텍스트를 저장하면 해당 컨트롤의 텍스트를 읽고 쓰는데 문제가 될것이 없지만 GDI+에서 텍스트를 출력하면 해당 출력은 이미지화되기 때문에 이미 출력한 텍스트가 어떤 내용인지 거꾸로 확인할 수 없다는 것입니다.

텍스트 출력 또한 이미지 처럼 Graphics 오브젝트를 받아서 작업을 합니다. 텍스트 출력과 연관된 메소드는 아래와 같습니다.

  • DrawString : 문자열 출력

  • MeasureString : 문자열의 출력 크기 측정

  • MeasureCharacterRanges : 문자열의 출력 영역 측정

DrawString에 대한 여러 오버로드 메소드가 있지만 대표적인 사용법은 "DrawString(문자열, 폰트, 브러시, X, Y)" 형식입니다. 폰트의 종류나 크기, 스타일에 따라 출력에 필요한 영역이 달라지기 때문에 Measure... 메소드도 제공하고 있습니다.

        Dim fnt = New Font("맑은 고딕", 48, FontStyle.Italic Or FontStyle.Bold)
        e.Graphics.DrawString("텍스트 출력 테스트!", fnt, Brushes.SteelBlue, 10, 10)

위의 코드예제는 "맑은 고딕" 폰트를 기반으로 이택릭과 굵은 스타일로 폰트를 설정하고 특정 위치에 SteelBlue 색을 지정하여 텍스트를 출력하고 있습니다. 복합적인 폰트 스타일을 적용하려면 FontStyle을 Or로 묶으면 됩니다. 색 지정은 2D 도형 출력시 사용했던 Pens처럼 Brushes을 사용할 수 있습니다. 아래의 그림의 위의 예제 코드를 수행한 결과입니다.




댓글
댓글쓰기 폼