• 북마크
  • 추가메뉴
어디로 앱에서 쉽고 간편하게!
애플 중고 거래 전문 플랫폼
오늘 하루 보지 않기
KMUG 케이머그

소프트웨어

[App 개발] Xcode 2.2 에서 카본 프로그램 만들자 (3)

본문

이번에는 본격적으로 Quartz 의 Core Graphics 를 이용해서 그림을 그려 보겠습니다.

참고 자료로는 애플 홈페이지에서 다운로드 받으실 수 있는 Quartz 2D Programming Guide 입니다.

프로그램의 기본 구조는 이전 소스와 거의 비슷합니다만, MyDraw() 함수의 전달 인수에 하나가 더 추가되었습니다.

OSStatus MyDraw( EventRef event, WindowRef window )

event 인수를 전달받는 이유는 이것으로 Core Graphics 컨텍스트를 얻어내기 위한 것입니다. 컨텍스트 (context) 란, 화면에 원하는 그림을 그리기 위하여 그래픽 시스템으로부터 얻어와야 하는 일종의 권한 같은 것입니다. 화면에 내 마음대로 그림을 그렸다가는 남의 윈도우에다가 마구 그림을 그려댈 수도 있기 때문에, 여러 개의 윈도우가 동시에 한 디스플레이를 공유하기 위해서 서로 질서를 잡아주는 역할을 하는 것입니다. 퀵드로우에서는 SetPort 를 사용했지만, 이제 Quartz 에서는 CGContext 를 사용하는 것이 약간 다른 점이겠습니다.

거두 절미하고 소스부터 먼저 보시지요.

--- MyDraw.h ---

#include <Carbon/Carbon.h>

void InstallMyDraw( WindowRef window ) ;
OSStatus MyDrawEventHandler( EventHandlerCallRef myHandler,
                                                         EventRef event,
                                                         void *userData ) ;

OSStatus MyDraw( EventRef event, WindowRef window ) ;


--- MyDraw.c ---

#include "MyDraw.h"

#include "MyDraw.h"

HIViewRef myHIView ;

void InstallMyDraw( WindowRef window )
{
        
        HIViewID myHIViewID = { 'mVue', 130 } ;
        EventTypeSpec        myHIViewSpec[] = {
        { kEventClassControl, kEventControlDraw },
        } ;
        OSStatus err ;
        
        HIViewFindByID( HIViewGetRoot( window ), myHIViewID, &myHIView ) ;
        
        err = InstallEventHandler( GetControlEventTarget( myHIView ),
                                                           NewEventHandlerUPP( MyDrawEventHandler ),
                                                           GetEventTypeCount( myHIViewSpec ),
                                                           myHIViewSpec,
                                                           (void *)myHIView,
                                                           NULL ) ;
        
}



OSStatus MyDrawEventHandler( EventHandlerCallRef myHandler,
                                                         EventRef event,
                                                         void *userData )
{
        
        OSStatus err = eventNotHandledErr ;
        
        switch( GetEventClass( event ) ) {
                case kEventClassControl :
                {
                        switch( GetEventKind( event ) )
                        {
                                case kEventControlDraw:
                                        err = MyDraw( event, (WindowRef)userData ) ;
                                        break ;
                        }
                        break ;
                }
                        
                default:
                        break ;
        }
        
        return err ;
        
}


OSStatus MyDraw( EventRef event, WindowRef window )
{
        
        OSStatus status = noErr ;
        CGContextRef myContext ;
        HIRect bounds ;
        int i, count = 0 ;
        
        CGMutablePathRef thePath ;
        
        status = GetEventParameter( event,
                                                                kEventParamCGContextRef,
                                                                typeCGContextRef,
                                                                NULL,
                                                                sizeof( CGContextRef ),
                                                                NULL,
                                                                &myContext ) ;
        require_noerr( status, CantGetGraphicsContext ) ;
        HIViewGetBounds( (HIViewRef) window, &bounds ) ;
        require_noerr( status, CantGetBoundingRectangle ) ;
        
        /////

        thePath = CGPathCreateMutable() ;
        
        CGContextBeginPath( myContext ) ;
        CGPathMoveToPoint( thePath, NULL, 0, 0 ) ;
        CGPathAddQuadCurveToPoint( thePath, NULL, 50, 100, 0, 200 ) ;
        CGPathAddQuadCurveToPoint( thePath, NULL, -50, 100, 0, 0 ) ;
        CGPathCloseSubpath( thePath ) ;

        CGContextTranslateCTM( myContext, bounds.size.width / 2, bounds.size.height / 2 ) ;
        for( i = 0 ; i < 360 ; i += 10 ) {
                if( count++ % 2 == 0 ) {
                        CGContextSetRGBFillColor( myContext, 0, 1, 0, 0.5 ) ;
                } else {
                        CGContextSetRGBFillColor( myContext, 0, 0, 1, 0.5 ) ;
                }
                CGContextRotateCTM( myContext, 10.0 * M_PI / 180.0 ) ;
                CGContextAddPath( myContext, thePath ) ;
                CGContextDrawPath( myContext, kCGPathFillStroke ) ;
        }
        
CantGetGraphicsContext:
CantGetBoundingRectangle:
                
        return status ;

}


많이 달라진 부분은 MyDraw() 함수입니다. 처음 부분은 CGContext 를 얻어내는 과정입니다. 그림을 그리기 위해서는 매번 반복되어야 하는 루틴이므로 미리 잘 저장해 두었다가 복사해서 붙여넣으면 되겠습니다.

        thePath = CGPathCreateMutable() ;
        
        CGContextBeginPath( myContext ) ;
        CGPathMoveToPoint( thePath, NULL, 0, 0 ) ;
        CGPathAddQuadCurveToPoint( thePath, NULL, 50, 100, 0, 200 ) ;
        CGPathAddQuadCurveToPoint( thePath, NULL, -50, 100, 0, 0 ) ;
        CGPathCloseSubpath( thePath ) ;


myContext 에 그대로 그림을 그릴 수도 있으나, 프로그램을 좀 더 재미있게 하기 위해서 여기서는 path 라는 것을 만들어 보겠습니다. path 는 그림을 그리는 경로, 혹은 과정이라고 할 수 있겠습니다. 어떤 도구를 이용해서 어떤 순서로 그림을 그릴 것인지를 순서대로 기록하는 것을 path 라고 보면 좋겠습니다. 이런 개념으로는 아도베 일러스트레이터를 많이 써보신 분들이 훨씬 더 센스있게 프로그램을 만드실 수 있을 것 같습니다.

thePath 에는 잎사귀 하나의 모양만을 갖고 있습니다. 잎사귀의 곡선은 베지어 툴을 이용하여 그렸습니다. Quartz 에서 곡선을 그리는 베지어 툴은 컨트롤 포인트를 한 개, 혹은 두 개를 쓸 수 있습니다. 매뉴얼을 살펴보시거나, 일러스트레이터 잘 하시는 분을 붙잡고 물어보세요. ㅎㅎㅎ

잎사귀 모양을 다 그리고 나면 thePath 를 저장하고, 이제는 이것을 화면에 출력하는 작업이 필요하겠지요.

        CGContextTranslateCTM( myContext, bounds.size.width / 2, bounds.size.height / 2 ) ;
        for( i = 0 ; i < 360 ; i += 10 ) {
                if( count++ % 2 == 0 ) {
                        CGContextSetRGBFillColor( myContext, 0, 1, 0, 0.5 ) ;
                } else {
                        CGContextSetRGBFillColor( myContext, 0, 0, 1, 0.5 ) ;
                }
                CGContextRotateCTM( myContext, 10.0 * M_PI / 180.0 ) ;
                CGContextAddPath( myContext, thePath ) ;
                CGContextDrawPath( myContext, kCGPathFillStroke ) ;
        }

CGContextTranslateCTM() 함수는 그림그리기 도구의 시작 위치가 어디인지를 지정합니다. 여기서는 매번 마찬가지로 화면의 정 중앙을 잡았습니다. 이제 그림 그리는 축을 10도씩 기울여 나가면서 잎사귀를 하나씩 그려 나갑니다. count 값이 홀수일 때에는 파란색, 짝수일 때에는 녹색으로 path 내부를 칠합니다. 알파값을 0.5 로 하여 둘이 서로 겹치는 부분에서 색상이 혼합되는 느낌을 주게 하였습니다. 0.5 이므로 반투명이 되지요.

프로그램을 직접 수행해 보면서 여러 가지로 프로그램을 조금씩 바꿔 나가면서 어떻게 변경되는지 확인해 보는것도 좋은 공부가 될 것입니다.
0 0
로그인 후 추천 또는 비추천하실 수 있습니다.
포인트 228,692
가입일 :
2003-02-18 14:12:30
서명 :
미입력
자기소개 :
미입력

최신글이 없습니다.

최신글이 없습니다.

댓글목록 2

향기님의 댓글

향기 221.♡.107.120 2005.12.21 00:09

완료~ ^^

악동시니님의 댓글