그래픽, 멀티미디어어플리케이션제작 (Creating Graphic and Multimedia Application) 델파이를이용해서어플리케이션에그래픽기능을추가하거나, 멀티미디어를지원하게하는것은그다지어려운일이아니다. 간단하게미리그려진그림을디자인시에추가할수도있고, 여러가지그래픽컨트롤을추가하거나런타임에서동적으로그리는등의방법이모두가능하다. 또한, 델파이 4 에서멀티미디어컴포넌트를사용하면어플리케이션에서동영상이나각종사운드등을지원하게할수있다. 이번장에서는각종어플리케이션을제작할때화려함을더해줄수있는그래픽과멀티미디어를지원하게하는방법에대해서알아보도록한다. 그래픽프로그래밍개괄 VCL 그래픽컴포넌트는윈도우의 GDI(Graphics Device Interface) 를캡슐화한다. 델파이어플리케이션에서그림을그리려면, 객체의캔버스를이용한다. 캔버스는객체의프로퍼티로제공되지만, 그자체가객체이다. 캔버스객체의가장큰장점은리소스를효과적으로사용하며, 디바이스컨텍스트를관리하기때문에스크린, 프린터나비트맵, 메타파일등의종류에관계없이같은방법으로사용할수있다는것이다. 캔버스는런타임에만사용할수있다. 또한, TCanvas 객체가윈도우의디바이스컨텍스트의 wrapper 이므로, 캔버스에대해윈도우 GDI 함수를사용할때에는캔버스객체의 Handle 프로퍼티를이용해서디바이스컨텍스트핸들을얻어야한다. 디바이스컨텍스트의이해 윈도우의그래픽에대해배울때가장먼저알아야할것이디바이스컨텍스트이다. 모든윈도우응용프로그램들이실제장치대신에가상화면과가상프린터를사용한다. 윈도우는본질적으로실제디스플레이하기전에, 내부적으로그릴것을그리게되는데이때사용하는것이디바이스컨텍스트이다. 따라서화면이나프린터에그리기를원하는어떤것과디바이스컨텍스트를분리할필요가있다. 디바이스컨텍스트는단순한메모리라기보다는객체이다. 이등록정보를변경하면다양한특수효과를줄수도있다. 윈도우는디바이스컨텍스트를가지고직접작업하는것을허용하지않는다. 이를처리하기위해서는 API 함수를호출해야한다. 이때컨텍스트핸들에대한변수로 hdc 를사용
한다. 델파이는미리정의된디바이스컨텍스트의 wrapper 를제공한다. 이것이바로캔버스이다. 윈도우의디바이스컨텍스트의유형에는다음과같은 4 가지종류가있다. 1. 디스플레이 (Display) 보통윈도우나 TMemo 컨트롤과같이그릴수있는다른영역에부착되어있다. 이컨텍 스트에그리면항상정보가화면에전달된다. 디스플레이디바이스컨텍스트에는 Class, Common, Private 형의 3 가지종류가있다. 32 비트응용프로그램에서는항상 private 형을사용하면된다. 만약빨리그래프나차트를출력하고자할때에는 common 형을사 용할수있다. Common 디바이스컨텍스트를얻을때에는 GetDC 나 GetDCEx, BeginPaint 함수등을사용하면된다. 물론이때받은핸들은빨리사용하고반납해야한 다. Private 디바이스컨텍스트는실제로응용프로그램이소유하고있는컨텍스트이다. 이것의장점은윈도우에돌려주기전에마음대로그릴수있다는점이다. 캐드나그래픽 응용프로그램의경우에는이유형의디스플레이디바이스컨텍스트를사용한다. 2. 프린터 (Printer) 디스플레이디바이스컨텍스트와같이프린터디바이스컨텍스트에보내는것도당장인쇄 된다. 3. 메모리 (Memory) 디스플레이나프린터디바이스컨텍스트에보낼수있는것은메모리에도보낼수있다. 메모리디바이스컨텍스트는 CreateCompatibleDC 함수를사용하여만든다. 인쇄나지금디스플레이에나타낼때, 또는나중에출력할때메모리디바이스컨텍스트가사용되고있으며, 비트맵과같은것에도이디바이스컨텍스트유형을사용할수있다. 4. 정보 (Information) 이디바이스컨텍스트는디스플레이의경우에는그다지문제가되지않지만, 프린터의경우에는중요한역할을한다. 예를들어프린터에인쇄할컬러문서가있는경우, 프린터가컬러출력을지원하는지알면도움이될것이다. 만약흑백프린터의경우에는특별한디더링루틴을추가하는것이필요하다. 정보디바이스컨텍스트는 CreateIC 함수를이용하여만든다. 디바이스컨텍스트를만들었으면, GetCurrentObject 나 GetObject 함수를이용하여특정객체에대한정보를얻을수있다.
윈도우의그래픽객체 윈도우는 7 개의서로다른그래픽객체를가지고있다. 만들때사용되는기본객체이다. 이들객체에대해서알아보자. 이들은윈도우의다른객체들을 1. 비트맵 (Bitmap) 비트맵은특정유형의래스터그림이다. 예를들어아이콘과 PCX 파일은물론 BMP 파일도이범주에속한다. 비트맵객체는바이트단위의크기, 픽셀단위의차원, 컬러포맷, 압축스키마등의정보를가진다. 비트맵객체는자신의특정유형에기초한다른속성들도다향하게가지고있다. 2. 브러쉬 (Brush) 브러쉬색상을적용할때사용된다. 벽에칠을할때붓을사용하는것과같이윈도우는다각형이나패스와같은다른그래픽객체의내부를칠할때브러쉬를사용하고있다. 특정패턴으로내부를채우고자할때에는비트맵을브러쉬로사용할수도있다. 3. 팔레트 (Palette) 윈도우는풍부한색상을정의하여사용하는색상의집합인팔레트를사용한다. 그런데, 일부비디오카드는한번에 256 색상만나타낼수있다. 이것이하나이상의팔레트를사용하는이유로서로다른 256 색상이필요할때마다서로다른팔레트를사용하면깨끗하고견고한색상을화면에나타낼수있다. 4. 폰트 (Font) 글자를나타내는폰트역시마찬가지로그래픽객체이다. 야좋은어플리케이션을만들수있다. 적절한크기와서체를사용하여 5. 패스 (Path) 다각형이나호, 선, 그리고타원과같은그려진객체의특정유형을의미한다. 윈도우는수 많은서로다른패스에대한함수를가지고있으며, 이들은모두서로다른속성들을가지 고있다.
6. 펜 (Pen) 선을그리거나, 도형을그릴떄사용하는객체로라인의두께와스타일과같은것들을설정 하게된다. 7. 지역 (Region) 지역은디바이스컨텍스트에있는한영역의위치를나타낸다. 때때로캔버스전체가아니라일정영역에대해서만윈도우가작업하도록요청해야할때가있다. 지역객체는이와같은일을할수있게해주는역할을한다. 윈도우의그래픽모드 디바이스컨텍스트를사용할때에, 디바이스컨텍스트의유형을명시하면그래픽객체를담을컨테이너를정의하는셈이다. 그러면, 이객체들이어떻게작용할것인지를결정해야하는데, 이를결정하는것이그래픽모드이다. 그래픽모드는윈도우에게디바이스컨텍스트의컨테이너에있는객체들이서로어떻게동작할것인지를지정한다. 이들이동작하는방법은대개특정유형의함수를사용할때어떤종류의화면효과를얻을것인가를결정한다. 이는브러쉬를사용하여특정영역을채울때영향이크다. 윈도우의그래픽모드에는다음의 5 가지가있으며, 이들을동시에사용한다. 1. 배경 (Background) 윈도우가배경색을섞는방법을정의한다. 윈도우가한색상을다른색상을대치해야하는지, 또는두색상을합해서새로운색상을만들어야하는지를나타낸다. 텍스트와비트맵동작에있어서보통이모드를사용하게된다. 2. 드로잉 (Drawing) 이모드는전경색 (foreground color) 을같이섞는방법을윈도우에게알려주는역할을한다. 이모드는보통펜이나브러쉬, 텍스트, 그리고비트맵동작을사용할때사용된다. 3. 매핑 (Mapping) 매핑은윈도우에게크기조정을어떻게해결할것인지를알려주는역할을한다. 예를들어,
이론적으로가능한커다란공간을그래픽으로할때, 시스템에있는메모리공간에의해이를모두나타내게할수없을것이다. 프린터가디스플레이에이를어떻게보여줄것인가를결정하는역할을하는것이매핑이다. 4. 다각형채우기 (Polygon-Fill) 이모드는윈도우가다각형과다른도형을채우는방법을정의한다. 그릴때사용할도형의모양과브러쉬의크기를결정하는역할을한다. 본질적으로윈도우가 5. 스트레칭 (Stretching) 이미지를압축하면, 이미지의세세한내용의일부를잃어버리게된다. 이미지의질을가능 한많이유지하려면윈도우가색상들을섞는방법을알아야한다. 캔버스의공통적인프로퍼티와메소드 캔버스객체에서공통적으로사용하는프로퍼티를나열하면다음과같다. 프로퍼티 설 명 Font 이미지에텍스트를기록할때사용할폰트를지정한다. TFont 객체를설정한다. Brush 그래픽모양과배경을채울색깔과패턴을결정한다. TBrush 객체를설정한다. Pen 캔버스가그림을그릴때사용할라인의펜종류를결정한다. TPen 객체를설정한다. PenPos 펜의현재의위치를지정한다. Pixels 현재의 ClipRect 내의픽셀영역의색깔을지정한다. 캔버스객체에서사용하는메소드에는다음과같은것들이있다. 이들에대해서모두자세하게설명할수는없으므로, 간단히설명한다. 이중에서 CopyRect, Ellipase, RectAngle, MoveTo, TextOut, TextWidth, TextHeight 는이미 9 장에서사용한바있다. 메소드 설 명 Arc 지정된사각영역의타원을따라서호를그린다. Chord 타원이라인으로절단된닫힌그림을그린다. CopyRect 다른캔버스의영역에서이미지를복사한다. Draw 캔버스의 Graphic 파라미터로지정된그래픽객체를지정한위치에그린다. Ellipse 지정된사각영역을따라서타원을그린다. FillRect 지정된사각영역을현재의브러쉬로채운다.
FloodFill 캔버스전체영역을현재의브러쉬로채운다. FrameRect 캔버스의브러쉬를이용하여사각형을그린다. LineTo PenPos 의위치에서지정된위치까지라인을그린다. MoveTo 현재의위치를지정된위치로옮긴다. Pie 타원에서지정된사각영역으로경계된부분을파이형태로그린다. Polygon 파라미터로넘긴점들을연결하여다각형을만든다. 다. 처음점과마지막점이연결된 PolyLine 현재의펜으로 Points 에넘어온점들을서로연결한다. Rectangle 좌측상단점과우측하단점을대각선으로하는사각형을펜을이용해서그리고, 내부 를브러쉬로채운다. RoundRect 코너가둥근사각형을그린다. StretchDraw 캔버스에그린그래픽을지정된사각영역에맞춘다. 경우에따라서확대되거나상하 좌우비율이변경된다. TextHeight, TextWidth 각각현재폰트로문자열을쓸때의높이와폭을반환한다. 백이포함된다. 높이에는줄사이의여 TextOut 문자열을지정된위치에첫자부터출력한다. 끝부분의위치로업데이트된다. 그리고, PenPos 프로퍼티는문자열의 TextRect 영역안에문자열을기록한다. 영역바깥으로나가는문자열은보이지않게된다. 그래픽작업을할때에는드로잉 (drawing) 과페인팅 (painting) 이란용어를구별해서사용해야한다. 드로잉은특정그래픽요소를생성하는것이다. 예를들어, 라인이나특정형태를코드를이용해서그리는것이다. 보통앞에서설명한캔버스의드로잉메소드를호출해서사용한다. 이미지가저장되지않기때문에, 그내용의전부또는일부를잃을수있으며, 출력도이미지가저장되지않고, 어플리케이션은어떻게다시그리는지를알지못하므로변할수있다. 그에비해페인팅은객체전체의형태를생성하는것으로, 드로잉을포함한다. 즉, 어떤상황에서든어플리케이션이그전체화면을다시칠할수있도록하는것이다. 사실사용자가마우스버튼을누르거나다른어떤동작을취하면그위치와다른요소들을저장해야하고, 페인팅메소드에서이정보를실제로해당이미지를페인팅하기위해사용한다.. 스크린의리프레쉬 윈도우는스크린위의객체의형태가변경되거나, 리프레쉬할필요가있을때에는 WM_PAINT 메시지를생성해낸다. 이메시지는 VCL 에서 OnPaint 이벤트로표현된다. 그러므로, VCL 객체가 Refersh 메소드를호출하면언제나 OnPaint 이벤트가발생한다.
폼에서 Refresh 메소드를사용하면지정된형태로그래픽을다시그리게된다. 그러므로, 예를들어폼의 OnResize 이벤트핸들러에서폼의형태를변경하는코드를집어넣은경우에는 Refresh 메소드를호출해서변경된사항을반영하도록해야한다. 일부운영체제에서는윈도우의클라이언트영역의일부가무효화 (invalidated) 된경우자동으로그부분을그려주지만, 윈도우는그렇지않다. 윈도우운영체제에서는일단스크린에그려진것은영구적이다. 그러므로, 폼이나컨트롤이드래그등의작업에의해잠시가려지는경우폼이나컨트롤은반드시불명확해진영역을다시페인트해야한다. TImage 컨트롤을사용한다면, TImage 내부에있는그래픽의페인팅과 refreshing 은 VCL 에의해자동으로관리된다. 또한 TImage 에드로잉을한경우에는이것이지속적인 (persistent) 이미지이기때문에, 포함된이미지를다시그려줄필요도없다. 이와는반대로 TPaintBox 의캔버스는스크린디바이스에직접연결되어있기때문에, PaintBox 에그려진모든것은임시로저장된다. 대부분의경우 TPaintBox 와거의동일하다. 그러므로, TPaintBox 에그리거나페인팅을한경우에는 OnPaint 이벤트핸들러에클라이언트영역이무효화될때마다이를다시그려주는코드를추가할필요가있다. 그래픽이미지가어플리케이션에나타나는형태는그리는방법에따라틀리다. 만약 TBitmap 캔버스처럼오프스크린이미지를그리는경우에는컨트롤이컨트롤의캔버스에비트맵을복사하기전에는나타나지않는다. 그러므로, 비트맵을그리고이것을이미지컨트롤에대입하려면, 이미지는컨트롤이 OnPaint 메시지를처리할기회가있을때에보여줄수있다. 화면의 Refresh 와연관된메소드에는 Invalidate, Update, Refresh(Repaint) 의 3 가지가있다. 이들의특징은각각다음과같다. 1. Invalidate 메소드 이메소드는윈도우에게폼의전체표면이페인팅되어야한다는것을알려준다. 그런데, Invalidate 는페인팅동작을즉각강제하지않고윈도우가이요구사항을저장하고나서, 현재프로시저가완전히수행되고시스템에다른이벤트가남아있지않을경우에여기에반응하게된다. 때때로이지연시간때문에, 페인팅작업에여러변경이일어난후에야비로소폼이페인팅되는수가있다. 그렇기때문에, 느린페인트메소드가여러번호출되서수행속도를더욱느리게하는것을막을수있다. 2. Update 메소드 윈도우에게폼의내용을갱신할것인지를물어보아서그것을곧장페인팅하는메소드이다. 이메소드는무효영역 (invalidated area) 이있을경우에만동작한다. 그러므로, 이동작은 Invalidate 메소드가막호출되었을때일어나거나아니면사용자에의한동작의결과로서
일어날수있다. 만약무효영역이없다면아무런동작을하지않으므로, 보통 Invalidate 를호출한뒤에바로 Update 메소드를호출한다. 3. Refresh(Repaint) 메소드 Invalidate 와 Update 메소드를차례로호출한다. 를즉각동작시킨다. 그결과로이메소드는 OnPaint 이벤트 이메소드들은잘사용해야한다. 컨트롤을반복페이팅을요청해야할경우에는 Invalidate 를호출하는것이좋다. 이는윈도우가화면을갱신하는데너무많은시간을소비하면이호출들을한데모아서한번에처리할수도있기때문이다. 윈도우의 WM_PAINT 메시지는낮은우선순위메시지이기때문에, 이것이가능하고더효과적이다. 반면에, Refresh 를여러번호출했을경우에는윈도우가매번다른메시지만처리할수는없으니화면이다시그려져야하고, 페인팅작업이느리기때문에어플리케이션전체의성능을떨어뜨릴수있다. 그렇지만, 가능한빠르게화면을다시그려야하는때가있는데이런경우에는 Refresh 를호출하는것이좋다. 그래픽객체의종류 VCL 은다음과같은그래픽객체를제공한다. 객체설명 Picture Bitmap Clipboard 그래픽이미지를담을수있다. 만약에추가적인그래픽파일포맷을추가하려면 RegisterFileFormat 메소드를사용한다. 그래픽파일을보여준다. 이미지를생성, 관리, 저장하는데사용되는강력한그래픽객체어플리케이션에 cut, copy, paste 를지원하게될텍스트나그래픽에대한컨테이너를나타낸다. 적절한포맷관리, 참조계수관리등을한다. Icon 윈도우아이콘파일을다룬다. 이미지를다룰때실제픽셀로서구성되는것이아니라, 이미지를구성하기 Metafile 위한여러작업들을기록하는것이다. 메타파일은비트맵에비해메모리도 적게차지하고, 이미지손상이적은장점이있으나, 처리속도가느리다. 캔버스객체의프로퍼티활용 캔버스객체에서제공하는여러가지프로퍼티를설정하여사용하는방법에대해서알아보자. 펜을이용하여라인을그리고, 브러쉬를이용하여내부를채우며, 적절한폰트를골라서텍
스트를기록하는등의작업이캔버스객체의프로퍼티를활용하여이루어진다. 펜의활용 캔버스의 Pen 프로퍼티는라인의형태를결정한다. 선을긋는작업을궁극적으로생각해보면두개의점사이에있는픽셀의그룹을바꾸는것이다. 펜에대해서연상할때가장쉽게생각할수있는것은포토샵이나페이트샵프로와같은그래픽프로그램의도구상자에서여러가지모양의붓을선택해서선이나도형을그릴수있다는것이다. 펜을선택한다는것은이런그래픽프로그램에서붓을선택하는것과같은것이다. 펜자체에는변경해서사용할수있는프로퍼티가 4 가지가있다. Color, Width, Style, Mode 가그것인데이들프로퍼티의값들이펜의형태를결정하게된다. 디폴트로모든펜은검정색, 두께는 1 픽셀이고 solid 형을가지며, 모드는캔버스에있는어떤것이든덧씌워그리는 copy 모드로설정되어있다. 1. 펜색상의변경 펜의색상을변경하는것은다른여러가지컴포넌트의 Color 프로퍼티를런타임에서변경하는것과똑같다. 펜의색상은그려지는라인의색상을결정하게된다. 사용자에게펜에대한새로운색상을결정하게하려면, 보통컬러그리드를띄워서선택하게한다. 컬러그리드는 foreground 색상과 background 색상을결정할수있게구성되어있다. 이때 foreground 색상은보통펜의색상을나타내며, background 색상은브러쉬의색상을나타낸다고생각하면된다. 2. 펜의두께변경 펜의두께는그려지는라인의두께를픽셀단위로결정하는것이다. 펜의두께가 1 보다크면, 펜의 Style 프로퍼티값에관계없이윈도우 95 는언제나 solid 라인을그리게된다. 펜의두께를변경하려면펜의 Width 프로퍼티에정수값을대입하면된다. 3. 펜의스타일변경 펜의 Style 프로퍼티는선의종류를일반적인선 (solid), 점선 (dotted line), 대쉬 (dashed line) 등이있다. 모두 6 개의스타일이존재하는데이들은각각 pssolid, psdash, psdot, psdashdot, psdashdotdot, psclear 이다. 앞에서도설명했지만, 펜의두께가 1 픽셀을넘을경우이프로퍼티의값은무시된다.
4. 펜의모드변경 펜의모드프로퍼티는펜의색상과캔버스의색상을결합하는방법을결정하는것이다. 예 를들어, 펜은언제나검정색이게할수도있고, 캔버스의배경색의보색으로보이게할수 도있고, 펜색상의보색으로보이게할수도있다. 5. 펜의위치얻기 펜이다음라인을그릴때시작점이되는위치를펜의위치 (position) 라고한다. 캔버스는펜의위치를 PenPos 프로퍼티에저장한다. 펜의위치는라인을그릴때에만영향을미친다. 그러므로, 다른도형이나텍스트를그리는경우에는그리려는위치를좌표로전달해야한다. 펜의위치를설정할때에는 MoveTo 메소드를사용한다. 예를들어, 다음의코드는펜의위치를캔버스의좌측상단으로이동한다. Canvas.MoveTo(0, 0); LineTo 메소드로라인을그릴때, 라인의끝점으로현재의위치를이동시킨다. 브러쉬의활용 캔버스컨트롤의 Brush 프로퍼티는도형의내부를채우는방법을결정한다. 브러쉬객체에는 Color, Style, Bitmap 의 3 개의프로퍼티를이용해서여러가지작업을하게된다. 이들프로퍼티의디폴트값은백색, solid 스타일, 패턴은없는것이다. 1. 브러쉬색상의변경 브러쉬의 Color 프로퍼티는영역의내부를채울색상을결정한다. 보통브러쉬는배경색을 결정하는것으로이해하면된다. 2. 브러쉬스타일의변경 브러쉬스타일은캔버스에채우는방법을결정한다. 스타일에따라브러쉬색상과캔버스의색상을결합하는방법을결정한다. 미리지정된스타일에는 solid, clear 와여러가지라인과해치패턴등을결정할수있다. Style 프로퍼티에는 bssolid, bsclear, bshorizontal, bsvertical, bsfdiagonal, bsbdiagonal, bscross, bsdiagcross 등의값들이있다.
3. 브러쉬비트맵프로퍼티의설정 브러쉬의 Bitmap 프로퍼티는브러쉬가여기에지정된비트맵을패턴으로사용하여영역을 채우게된다. 다음의코드는비트맵을파일에서로드하여, 이를브러쉬의패턴으로사용하 는예이다. var Bitmap: TBitmap; Bitmap := TBitmap.Create; try Bitmap.LoadFromFile('MyBitmap.bmp'); Form1.Canvas.Brush.Bitmap := Bitmap; Form1.Canvas.FillRect(Rect(0, 0, 100, 100)); finally Form1.Canvas.Brush.Bitmap := nil; Bitmap.Free; 참고 : 브러쉬비트맵 브러쉬의비트맵은 8x8 픽셀의비트맵패턴이어야한다. 브러쉬에비트맵을사용하기위해서는우선비트맵을생성하고, 이것을지정하여사용한후, 모든작업이완료되었으면이를해제시켜주어야한다. 그래픽객체그리기 라인그리기 캔버스에서그릴수있는라인은일반적인라인과폴리라인 (polyline) 이있다. 라인은두개의점을이어주는픽셀들이다. 폴리라인은라인이서로연결된것으로, 이들을그릴때에캔버스는펜을이용하게된다. 1. 라인그리기
캔버스에서라인을그리려면, LineTo 메소드를사용한다. LineTo 메소드는현재의위치에서지정한좌표까지현재의펜으로라인을그린다. 그리고, 현재위치가끝점으로이동하게된다. 예를들어, 다음의코드는폼이그려질때폼에라인으로 X 자를그리게된다. procedure TForm1.FormPaint(Sender: TObject); with Canvas do MoveTo(0, 0); LineTo(ClientWidth, ClientHeight); MoveTo(0, ClientHeight); LineTo(ClientWidth, 0); 2. 폴리라인그리기 폴리라인을캔버스에드리려면 PolyLine 메소드를사용한다. PolyLine 메소드에서사용하는파라미터는 Point 의배열이다. PolyLine 은기능적으로는 MoveTo 와 LineTo 메소드를계속해서호출하는것과같지만호출에따른오버헤드가없기때문에수행속도가빠르다. 도형그리기 캔버스에는여러가지종류의도형을그리는메소드가있다. 으로그리고, 내부는브러쉬로채운다. 캔버스는도형의외곽선은펜 1. 사각형과타원그리기 캔버스에사각형과타원을그리기위해서는 Rectangle, Ellipse 메소드를사용하면된다. 이두메소드모두좌표로는경계가되는 4 개의좌표를사용한다. Rectangle 메소드는넘어온 4 개의좌표를연결한사각형을그리며, Ellipse 메소드는사각형내를채우는타원을그린다. 다음의코드는좌측상단의 1/4 영역에사각형을그리고, 내부에타원을그린다.
procedure TForm1.FormPaint(Sender: TObject); Canvas.Rectangle(0, 0, ClientWidth div 2, ClientHeight div 2); Canvas.Ellipse(0, 0, ClientWidth div 2, ClientHeight div 2); 이코드를실행하면다음과같은실행화면을볼수있다. 2. 끝이둥근사각형그리기 끝이둥근사각형을그릴때에는 RoundRect 메소드를사용한다. RoundRect 메소드의 4 개의파라미터는다른메소드와마찬가지로사각형의좌표를나타내며, 이밖에도둥근코너를어떻게그릴지를나타내는 2 개의파라미터를추가로가진다. 다음의메소드는폼의좌측상단 1/4 영역에 10 픽셀의지름의원형코너를가진끝이둥근사각형을그리게된다. procedure TForm1.FormPaint(Sender: TObject); Canvas.RoundRect(0, 0, ClientWidth div 2, ClientHeight div 2, 10, 10); 3. 다각형그리기 다각형을그리기위해서는 Polygon 메소드를사용하면된다. 의배열을사용하며, 이들의점이꼭지점이된다. 다각형은파라미터로 Point
그래픽컨트롤사용하기 어플리케이션에그래픽객체를다루기위해서특별히다른컴포넌트를사용할필요는없다. 그렇지만, 사실상폼에직접드로잉을하는경우는거의없다. 그보다는보통 VCL 이미지컨트롤을이용해서그래픽을보여주는경우가많다. 이미지컨트롤은비트맵객체를디스플레이할수있는컨테이너컴포넌트이다. 이렇게이미지컨트롤을사용하면인쇄, 클립보드, 그래픽객체읽기와저장등의편리한기능을이용할수있다. 그래픽객체는비트맵파일, 메타파일, 아이콘등의여러가지그래픽클래스일수있다. 스크롤가능한그래픽만들기 그래픽의크기는폼과같은크기일필요는없다. 그래픽의종류에따라서는폼보다더크거나작을수있다. 이럴때스크롤박스컨트롤 (TScrollBox) 을폼에추가하고, 그래픽이미지를그안에위치시키면그래픽이폼보다훨씬커도 ( 스크린전체보다커도 ) 스크롤박스의기능을통해전체를볼수있다. 초기비트맵크기설정 이미지컨트롤을폼위에올려놓으면, 처음에는단순한컨테이너이다. 실제로그래픽을사용하려면 Picture 프로퍼티를설정해주어야한다. 그런데, 처음에비트맵을보여주지않더라도, 자리를차지한컨트롤이보이지않으면보기가무척싫을것이다. 이를피하기위해서는폼의 OnCreate 이벤트핸들러에비트맵객체를생성해서 Picture.Graphic 프로퍼티에대입하면된다. 다음은어플리케이션의폼에초기비트맵의크기를설정해서대입하는코드이다. procedure TForm1.FormCreate(Sender: TObject); var Bitmap: TBitmap; Bitmap := TBitmap.Create; Bitmap.Width := Image1.Width; Bitmap.Height := Image1.Height; Image1.Picture.Graphic := Bitmap;
비트맵을 Picture.Graphic 프로퍼티에대입하면, Picture 객체가비트맵의 Owner 가된다. 그러므로, Picture 객체가파괴되면, 비트맵객체도자동을파괴된다. 어플리케이션을실행 하면폼의클라이언트영역에비트맵이하얀색으로자리를차지하고있을것이다. 그래픽파일의읽기와쓰기 그래픽이미지는어플리케이션이실행되는동안에만존재한다. 그렇기때문에, 이를파일로저장하고, 불러오는것은필수적인부분이다. 그래픽이미지컨트롤도일반적인다른 VCL 컴포넌트와마찬가지로이미지를파일로저장하고, 읽어올수있다. 또한, 이미지컨트롤은설치가능한그래픽클래스를지원한다. 파일에서그림읽어오기 이미지컨트롤에그래픽을로드하려면 Picture 객체의 LoadFromFile 메소드를이용하면된다. 9 장의그래픽인쇄루틴에서이미한번소개한바있으므로이해가어렵지는않을것이다. 다음의코드는 OpenDialog 컴포넌트를이용해서그림파일을이미지컨트롤에불러오는예이다. procedure TForm1.Open1Click(Sender: TObject); if OpenDialog1.Execute then CurrentFile := OpenDialog1.FileName; Image.Picture.LoadFromFile(CurrentFile); 그림을파일로저장하기 VCL 그림객체는몇가지포맷의그래픽을읽고, 쓸수있다. 그리고, 자신만의그래픽파일포맷을생성하고, 등록시킬수있다. 이미지컨트롤의내용을파일에저장하려면 Picture 객체의 SaveToFile 메소드를이용하면된다. 다음의이벤트핸들러는 File Save, File Save As 메뉴를선택했을때사용할만한코드이다. 참고로하면, 쓸모가있을것이다. 이때 CurrentFile 변수는문자열형의전역변수로선언해서사용하면된다.
procedure TForm1.Save1Click(Sender: TObject); if CurrentFile <> '' then Image.Picture.SaveToFile(CurrentFile) else SaveAs1Click(Sender); procedure TForm1.SaveAs1Click(Sender: TObject); if SaveDialog1.Execute then CurrentFile := SaveDialog1.FileName; Save1Click(Sender); 그래픽에클립보드이용하기 이미지컨트롤을이용해서윈도우클립보드에 cut, copy, paste 기능을이용하려면 VCL 의클립보드객체를사용하면된다. VCL 클립보드객체를사용하려면사용하려는유닛의 uses 절에 Clipbrd.pas 유닛을추가하여야한다. 클립보드에그래픽복사, 잘라넣기 (copy and cut) 그림을클립보드에복사하려면, Picture 객체를클립보드객체로 Assign 메소드를사용해서대입하면된다. 그래픽을클립보드에잘라넣는것도복사하는것과비슷한방법을사용하지만, 소스에서그래픽을삭제하게된다. 그래픽을클립보드에잘라넣으려면, 먼저클립보드로복사를하고원래의내용을삭제하면된다. 삭제한이미지를어떻게보여줄것인가하는것도비교적중요한점이다. 흔히사용하는방법으로는지워진영역을하얗게표시하는방법이다. 다음의코드는클립보드로복사하기와잘라내기를메뉴에서선택했을때쓸수있는코드이다. procedure TForm1.Copy1Click(Sender: TObject); Clipboard.Assign(Image1.Picture);
procedure TForm1.Cut1Click(Sender: TObject); var ARect: TRect; Copy1Click(Sender); { 클립보드로일단복사한다.} with Image1.Canvas do CopyMode := cmwhiteness; { 복사모드를하얗게설정 } ARect := Rect(0, 0, Image1.Width, Image1.Height); { 비트맵크기를설정 } CopyRect(ARect, Image1.Canvas, ARect); { 비트맵을복사해넣는다.} CopyMode := cmsrccopy; { 원래의복사모드로복구 } 클립보드에서그래픽붙여넣기 (Paste) 윈도우클립보드에비트맵그래픽이담겨있으면, 이를이미지객체에붙여넣을수있다. 이를위해서는먼저클립보드의 HasFormat 메소드를호출하여클립보드가그래픽을가지고있는지를확인한다. HasFormat 메소드는 Boolean 값을반환하는데, 지정한데이터형이클립보드에담겨있으면 True 를반환한다. 비트맵이있는지확인하려면파라미터로 CF_BITMAP 를넘겨주면된다. 비트맵이있으면클립보드에서대입하면된다. 다음의코드는붙여넣기를구현한예제코드이다. procedure TForm1.PasteButtonClick(Sender: TObject); var Bitmap: TBitmap; if Clipboard.HasFormat(CF_BITMAP) then Image.Picture.Bitmap.Assign(Clipboard);
어플리케이션에드로잉객체이용하기 앞에서설명한다양한드로잉메소드는툴바나버튼패널에서도사용할수있다. 어플리케이션에그래픽을잘사용하면프로그램이화려하며고급스럽게보이게할수있지만, 너무많은색이나너무많은그래픽을사용할경우에는오히려비생산적이될수도있다. 그러므로, 적당한수준에서드로잉객체를잘사용하는것이좋은어플리케이션을만드는데상당히중요한역할을하는것이다. DIBs(Device Independent Bitmaps) 의지원 델파이는델파이 3 버전부터 TBitmap 객체에비트맵비트에대한포인터를프로퍼티로제공하기시작했다. 델파이 2 까지는 DDBs(Device Dependent Bitmaps) 를사용했으나, 델파이 3 부터 DIBs 를지원하게된것이다. DIB 비트에대한포인터를얻으려면 Bitmap.DibMemory 프로퍼티에접근하기만하면된다. 다음의코드는첫번째스캔라인의픽셀들을팔레트의첫번째색상을변경한다. 이코드는 256 색상의비트맵팔레트를기준으로작성되었기때문에, 다른형태의비트맵에는동작하지않을수있다. type TByteArray = array[0..0] of Byte; procedure TForm1.Button1Click(Sender: TObject); var p: ^TByteArray; i: Integer; Image1.Picture.LoadFromFile(c:\Windows\ 구름.bmp ); p := Image1.Picture.Bitmap.DibMemory; for i := 0 to (Image1.Picture.Bitmap.Width 1) do p^[i] := 0; 이코드를살펴보면, 바이트형의배열을선언하고여기에대한포인터변수 p 를선언한 다. 그리고, 비트맵을디스크에서읽어온후, DibMemoey 에대한포인터를변수 p 에대입 하고, DIB 메모리의첫번째스캔라인의색상을 DIB 팔레트의첫번째색상으로바꾼다.
라인을마음대로그리자! 지금까지설명한내용을바탕으로간단하게라인을그어주는어플리케이션을하나만들어보자. 제작할어플리케이션은사용자가마우스를움직이면이를따라런타임에서라인을그리는간단한드로잉프로그램이다. 이어플리케이션은마우스로클릭하고드래그를하면윈도우의캔버스에라인을그린다. 마우스버튼을누르면그리기가시작되고, 버튼을떼면그리기가완료된다. 마우스에반응하기 어플리케이션에서사용하는마우스의동작에는버튼을누르고 (MouseDown), 마우스를움직 이고 (MouseMove), 버튼을떼는 (MouseUp) 동작과클릭 (Click) 이있다. 1. 마우스이벤트의종류 VCL 에는 OnMouseDown, OnMouseMove, OnMouseUp 의 3 가지마우스이벤트가있다. VCL 어플리케이션이마우스의동작을감지하면해당되는이벤트핸들러를호출하게되는 데, 이들이벤트는모두 5 개의파라미터를사용한다. 이들파라미터의내용은다음과같다. 파라미터의미 Sender 마우스의동작을감지한객체 Button 어떤버튼의동작인가? mbleft, mbmiddle, mbright 중의하나이다. Shift Alt, Ctrl, Shift 키가눌렸는가? X, Y 이벤트가발생한좌표 2. MouseDown 이벤트의처리 사용자가마우스의버튼을누를때마다 OnMouseDown 이벤트가발생한다. 참고로라인을 긋는경우를생각해보자. 이때마우스버튼을누르면, 펜의위치가이동하도록해야한다. 그러므로다음과유사한이벤트핸들러를작성하여야할것이다. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Canvas.MoveTo(X, Y);
3. MouseUp 이벤트의처리 OnMouseUp 이벤트는사용자가마우스버튼을뗄때발생하게된다. 이때이이벤트는마우스를누른컨트롤에발생하기때문에, 커서의위치가컨트롤의범위를벗어나더라도이를처리할수있다. 그러므로, 라인을폼의범위를벗어난경우에도처리를할수있다. 어쨌든앞에서의 MouseDown 이벤트에이어서라인을긋도록하려면, OnMouseUp 이벤트핸들러를다음과같이작성하면된다. procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Canvas.LineTo(X, Y); 이렇게함으로써사용자는마우스버튼을클릭하고드래그한후, 이를놓으면라인을그릴 수있게된다. 4. MouseMove 이벤트의처리 OnMouseMove 이벤트는사용자가마우스를움직일때마다주기적으로발생한다. 다음의 예제코드는사용자가마우스버튼을누른채로마우스를움직일때라인을그려주게된다. procedure TForm1.FormMouseMove(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Canvas.LineTo(X, Y); 마우스동작의기록 드로잉어플리케이션을작성하기위해서는마우스의동작을감시하는것이필요하다. 이를위해서는폼객체에여기에대한객체필드를추가하는것이좋다. 다음의코드는마우스버튼의동작을기록하기위해, 마우스버튼이눌려있는지여부를기록하는 Drawing 이라는 Boolean 필드를추가하였다. 또한, 마우스의위치를기록하기위해서 TPoint 형의 Orgin, MovePT 필드도사용한다.
참고 : TPoint 데이터형 TPoint 데이터형은 X 와 Y 의값을포인트 (Point) 라고불리는하나의레코드에저장한것이다. 포인트를생성하는가장쉬운방법은 Point 함수를사용하는것이다. Point 함수는 X 와 Y 의값을받아들여 TPoint 라는레코드를반환하게된다. type TForm1 = class(tform) procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormMouseMove(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); public Drawing: Boolean; // 마우스버튼이눌렸는지여부를기록한다. Origin, MovePt: TPoint; // 포인트를기록하는필드 사용자가마우스버튼을누르면 Drawing 필드를 True 로, 버튼을떼면 False 로설정한다. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Drawing := True; Canvas.MoveTo(X, Y); procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Canvas.LineTo(X, Y); Drawing := False;
OnMouseMove 이벤트핸들러는 Drawing 프로퍼티가 True 일때에만동작하도록수정한 다. procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X, Y: Integer); if Drawing then //Drawing 플래그가설정되었을때만그린다. Canvas.LineTo(X, Y); 이렇게하면, 마우스버튼을누르고뗄때까지계속해서라인을이어서그리게된다. 마우스포인트의추적 라인을그릴때, Origin 필드에는 MouseDown 이벤트가발생한위치를기록하고이를이용 해서 MouseUp 이벤트에서라인을그려주면, 마우스를눌렀다가뗄때하나의라인이그려 질것이다. 다음과같이구현하면된다. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Drawing := True; Canvas.MoveTo(X, Y); Origin := Point(X, Y); procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Canvas.MoveTo(Origin.X, Origin.Y); Canvas.LineTo(X, Y); Drawing := False; 이렇게하면라인을제대로그릴수있게되지만, 마우스를움직일때의라인을볼수가없 다. 이를위해서는마우스의움직임을점검해서적절한조치를취할필요가있다.
마우스움직임의추적 현재의 OnMouseMove 이벤트핸들러는마지막마우스의위치를따라서라인을그리기때문에문제가있다. 이를해결하기위해서는라인을그리는위치를 origin 포인트로옮길필요가있다. procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X, Y: Integer); if Drawing then Canvas.MoveTo(Origin.X, Origin.Y); // 펜을시작포인트로이동 Canvas.LineTo(X, Y); 이렇게하면, 현재의마우스위치를따라서라인을그리게되지만, 계속라인을겹쳐서그리기때문에제대로볼수가없다. 그러므로, 이를시정하기위해서는다음라인을그리기전에원래의라인을지워야한다. 이를위해서는이전에사용한점의위치를알아야하는데, 이를저장하기위해 MovePt 필드를사용한다. MovePt 필드는각각의중간라인의끝점으로설정되며, Origin 과 MovePt 를연결하는라인을다음과같이그리면이전의라인을지우게된다. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Drawing := True; Canvas.MoveTo(X, Y); Origin := Point(X, Y); MovePt := Point(X, Y); procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
if Drawing then Canvas.Pen.Mode := pmnotxor; Canvas.MoveTo(Origin.X, Origin.Y); // 그리고, 지우기위해서 XOR 모드를사용 // 펜의위치를 origin 으로 Canvas.LineTo(MovePt.X, MovePt.Y); // 이전라인을지운다. Canvas.MoveTo(Origin.X, Origin.Y); // 펜의위치를 origin 으로 Canvas.LineTo(X, Y); // 새로운라인을그린다. MovePt := Point(X, Y); // 현재위치를기록한다. Canvas.Pen.Mode := pmcopy; 펜의모드를 pmnotxor 로선택하면, 라인이배경색과결합되어나타나게된다. 라인을지우기위해서는현재라인이그려진위치에라인을그리면된다. 그리고나면, 펜의모드를 pmcopy( 디폴트값 ) 로바꾼다. 이제완성이되었다. 특별히펜의스타일이나색상등을정하지않고디폴트값을사용하였으므로, 멋도없는매우단순한어플리케이션이지만기본적으로마우스를사용한드로잉프로그램을제작하는방법에대해서는익혔을것으로믿는다. 이어플리케이션을실행하고라인을마음대로그려보자. 그러면다음과같은형태의실행화면을얻을수있을것이다.
스크린캡쳐어플리케이션의제작 이번에는간단한스크린캡쳐어플리케이션을제작해보자. 이번어플리케이션에서배울점은 DC 에대한개념과비트맵을처리할때많이사용되는 API 함수를익히는것이다. 먼저폼을다음과같이디자인한다. 캡쳐프로그램의폼은그다지화려하지않아도되고, 어차피캡쳐를할때폼이숨겨져야하므로단순하게디자인한다. 캡쳐한파일을저장해야하므로, TSaveDialog 컴포넌트를하나추가한다. 그리고, DefaultExt 프로퍼티를.bmp, Filter 프로퍼티를 *.bmp *.bmp 로설정하자. 캡쳐할이미지를저장할 TImage 변수를다음과같이전역변수로선언한다. var Form1: TForm1; CaptureImage: TImage; 그러면, 실제로캡쳐를수행하는프로시저를작성해보자. 시저를 private 섹션에선언한다. 다음과같이 Capture 라는프로
private procedure Capture; 그러면, Capture 프로시저를실제로구현해보자. 스크린전체를캡쳐하는프로시저는다음 과같이구현할수있다. procedure TForm1.Capture; var DC, DCBuffer, Buffer: hdc; x, y: integer; Hide; Sleep(200); DC := CreateDC('DISPLAY', nil, nil, nil); x:= Screen.Width; Y:= Screen.Height; DCBuffer := CreateCompatibleDC(DC); Buffer := CreateCompatibleBitmap(DC, x, y); SelectObject(DCBuffer, Buffer); BitBlt(DCBuffer, 0, 0, x, y, DC, 0, 0, SRCCOPY); BitBlt(CaptureImage.Canvas.Handle, 0, 0, CaptureImage.Width, CaptureImage.Height, DCBuffer, 0, 0, SRCCOPY); DeleteDC(DCBuffer); DeleteDC(DC); CaptureImage.Refresh; Show; 지역변수로선언한 DC 는디스플레이객체의디바이스컨텍스트를저장한다. 그리고, DCBuffer 에는 DC 에대한메모리디바이스컨텍스트를생성해서대입하며, 실제비트맵에대한디바이스컨텍스트는 Buffer 변수에저장된다. x, y 는캡쳐할화면의크기를담는변수로쓰인다. 먼저 Hide 를호출하여폼을숨긴다. 그이후에 Sleep(200) 을호출한이유는폼을숨기는데약간의시간을벌어주기위한것이다. 필자가테스트한바로는바로캡쳐를시도할경우폼이숨겨지는모양이그대로캡쳐되어버린다. 그다음라인에서는사용할디바이스
컨텍스트를생성한다. CreateDC 함수는지정된이름의디바이스컨텍스트를생성한다. 여기서는화면에보여주는비트맵이므로 DISPLAY 로설정하여디스플레이디바이스컨텍스트를선택한다. 디바이스컨텍스트에대한자세한내용은이장의앞부분에잘정리해놓았으므로, 이를참고하기바란다. 그다음에는캡쳐할이미지의크기를스크린전체크기로설정하고, DCBuffer 와 Buffer 변수의값을설정한다. CreateCompatibleDC 함수는지정된디바이스에대한메모리디바이스컨텍스트를생성하는함수이다. 비트맵을선택하면, 디바이스컨텍스트는스크린으로복사되거나인쇄될이미지를준비하는데사용된다. CreateCompatibleDC 함수는래스터작업을지원하는장치에서만사용될수있다. 디스플레이 DC 는보존성이없기때문에, 이를통해출력한것은다른윈도우가그위치를덮어씌우기만해도문제가생길수밖에없다. 이렇게하면윈도우의특성상좋지않은것은당연하다. 이를막기위해서는디스플레이 DC 의내용을계속간직하고있을보존성이있는 DC 가필요하게되는데, 이런것이있다면디스플레이 DC 의내용을여기에보관했다가필요할때다시그려주면될것이다. 이때사용하는것이 CreateCompatibleDC 함수를이용해서생성하는메모리 DC 이다. 메모리 DC 의용도로는이미지를반복적으로사용할때와비트연산을하고자할때를들수있다. 메모리 DC 는경우에따라서빠르게생성해서사용할수있고, 저장도어느정도가능하기때문에장점이많지만, 메모리를소모한다는단점을가지고있다. 별것아닌것같지만디스플레이에필요한메모리가워낙크기때문에, 사용을남발하는것은절대로삼가해야하며, 사용한뒤에는반드시 DeleteDC 를호출하여꼭해제를해주어야한다. Buffer 변수에값을대입하기위해서사용하는 CreateCompatibleBitmap 함수는지정된디바이스컨텍스트의장치와호환되는비트맵을생성한다. 이함수에의해생성되는비트맵의컬러포맷은파라미터에지정된디바이스의컬러포맷과일치한다. 이비트맵은디바이스와호환되는메모리디바이스컨텍스트에선택되어사용되는경우가많다. 메모리디바이스컨텍스트는컬러와모노비트맵을모두허용하기때문에, 디바이스컨텍스트로메모리디바이스컨텍스트가지정될경우, CreateCompatibleBitmap 함수에의해반환되는비트맵의포맷은다를수있다. CreateCompatibleBitmap 함수의두번째와세번째파라미터는비트맵의크기를지정한다. 여기서는스크린의크기를지정한다. 그다음에는 SelectObject 함수를이용해서지정된디바이스컨텍스트에사용할그래픽객체를선택한다. 여기서는비트맵객체인 Buffer 를선택해야한다. 이제가장중요한 BitBlt 함수의사용법을익힐차례이다. BitBlt 함수에의해서실제로비트맵에그림을대입하게된다. BitBlt 함수는지정된소스디바이스컨텍스트에서목적디바이스컨텍스트로해당되는픽셀들의컬러데이터를전송하게된다. 이때스트레치 (stretch), 압축 (compress), 회전 (ratate) 등의변형을할수가있다. 소스와목적디바이스컨텍스트의컬러포맷이맞지않으면, 소스컬러포맷을목적포맷에맞추어변환을한다. 우리가사용한두가지디바이스컨텍스트는서로호환되도록설정하였으므로컬러포맷의
문제는없다. BitBlt 함수는파라미터가모두 9 개로무척많은데, 사실그내용을보면그다지어렵지않다. 첫번째파라미터에는목적디바이스컨텍스트의핸들을지정하며, 2~5 번째파라미터는목적디바이스컨텍스트에복사할영역의 4 군데좌표를설정한다. 6 번째파라미터는소스디바이스컨텍스트의핸들을지정하면되고, 7~8 번째파라미터는소스디바이스컨텍스트의좌측상단꼭지점의좌표를설정한다. 마지막으로 9 번째파라미터에는레스터작업의코드를지정한다. 여기서가장중요한것은마지막파라미터인데지우거나, 복사, 반전등의여러가지효과를지정할수있다. Capture 프로시저에사용한 BitBlt 함수의역할은디바이스컨텍스트인디스플레이장치의전체스크린영역을일단메모리디바이스컨텍스트로복사한후, 이를다시 CaptureImage 이미지컴포넌트의캔버스로복사하는것이다. 이때에는 SRCCOPY 를사용하여소스영역의내용을직접목적영역으로복사한다. 이것으로캡쳐작업이완료된다. 레스터작업코드는여러가지로사용되기때문에, 익혀두면많은도움이된다. 이를다음에정리하였으므로, 참고하기바란다. 값 설 명 BLACKNESS 목적영역을물리적팔레트의인덱스 0 인색상 ( 보통검은색 ) 으로채운다. DSTINVERT 목적영역을반전한다. MERGECOPY 소스영역의색상을 AND 연산을통해지정된패턴으로색상을합친다. MERGEPAINT 반전된소스영역의색상을목적영역의색상과 OR 연산을통해합친다. NOTSRCCOPY 소스영역을목적영역으로반전하여복사한다. NOTSRCERASE 소스와목적영역의색상을 OR 연산을해서합친후, 이를반전한다. PATCOPY 지정된패턴으로목적비트맵에복사한다. PATINVERT 지정된패턴의색상과목적영역의색상을 XOR 연산으로합친다. PATPAINT 소스영역의반전된색상과패턴의색상을 OR 연산을하고, 이를다시목적영역 의색상과 OR 연산을한다. SRCAND 소스와목적영역의색상을 AND 연산한다. SRCCOPY 소스영역의내용을목적영역으로직접복사한다. SRCERASE 목적영역의반전된색상과소스영역의색상을 AND 연산한다. SRCINVERT 소스와목적영역의색상을 XOR 연산한다. SRCPAINT 소스와목적영역의색상을 OR 연산한다. WHITENESS 목적영역을물리적팔레트의인덱스 1 인색상 ( 보통흰색 ) 으로채운다. 그러면, 이제폼의 OnCreate 이벤트핸들러에서이미지컴포넌트를생성하고, 프로퍼티를지정한다. 그리고, Capture 함수를호출하여일단스크린을 CaptureImage 변수에캡쳐한다.
procedure TForm1.FormCreate(Sender: TObject); CaptureImage := TImage.Create(Self); CaptureImage.Left := 0; CaptureImage.Top := 0; CaptureImage.Width:=Screen.Width; CaptureImage.Height:=Screen.Height; Capture; Button1 과 Button2 의이벤트핸들러를다음과같이작성하여캡쳐를하고, 캡쳐한이미지 를저장할수있도록한다. procedure TForm1.Button1Click(Sender: TObject); Capture; procedure TForm1.Button2Click(Sender: TObject); if SaveDialog1.Execute then CaptureImage.Picture.Bitmap.SaveToFile(SaveDialog1.FileName); 지역 (Region) 이란? 지역은상상할수있는모든형태의모양을클립핑영역으로설정할수있도록허용한다. 이를이용하면그리게되는모든것들이그영역에클리핑되거나마스크되게할수있다. 여기에대해서는델파이자체가지원하는메소드는없다. 그러므로, 이를사용하기위해서는 GDI 를직접사용해야한다. 지역을사용하는예제를간단하게하나만들어보자. 폼에버튼을하나얹고, Caption 을 실행 으로설정한뒤 OnClick 이벤트핸들러를다음과같이작성한다. procedure TForm1.Button1Click(Sender: TObject); var
NewRgn, OldRgn: hrgn; NewRgn := CreateEllipticRgn(0, 0, 100, 100); OldRgn := SelectObject(Canvas.Handle, NewRgn); PatBlt(Canvas.Handle, 0, 0, Width, Height, BLACKNESS); SelectObject(Canvas.Handle, OldRgn); DeleteObject(NewRgn); 지역을사용하기위해서는일단 2 개의지역을지정할수있는핸들을저장할변수를선언한후 CreateEllipticRgn 함수를실행하여동그란형태의지역을 NewRgn 변수에저장한다. 그리고, SelectObejct 함수로 NewRgn 지역객체를선택한다. 이때 SelectObject 객체는이전의지역객체를반환하므로, 이를저장해두었다가나중에지역을복구할때사용한다. PatBlt 함수는주어진사각영역에현재선택된브러쉬를이용해서칠하는함수로, 칠하는방법을마지막파라미터에지정하도록되어있다. 여기서는 BLACKNESS 를지정하여까맣게칠하도록하였다. 그다음에는원래의지역을복구하고, 지역객체를해제하면끝난다. 이어플리케이션을실행하면분명히 PatBlt 로사각형영역을칠하도록했지만, NewRgn 이동그란영역으로지정되었으므로다음과같이동그랗게칠해진다. 매핑모드 (Mapping Modes) 일반적으로윈도우의기본적인매핑모드는 MM_TEXT 를사용한다. 이모드에서는모든드로잉좌표가좌상측에서우하로내려올수록 X, Y 가 1 픽셀씩증가한다. 즉, 좌상꼭지점을 (0, 0) 으로하여우하로내려오면양의값으로증가하는것이다. 윈도우에는이모드말고도여러가지매핑에대한모드가존재하는데, 여기에는다음과같은것들이있다. 모드 설명
MM_TEXT 1 픽셀단위로, y 가증가할수록아래로내려온다. MM_LOMETRIC 0.1mm 단위로, y 가증가할수록위로올라간다. MM_HIMETRIC 0.01mm 단위로, y 가증가할수록위로올라간다. MM_LOENGLISH 0.01 인치단위로, y 가증가할수록위로올라간다. MM_HIENGLISH 0.001 인치단위로, y 가증가할수록위로올라간다. MM_TWIPS 1/20 포인트 (1/1440 인치 ) 단위로, y 가증가할수록위로올라간다. MM_ISOTROPIC SetWindowExtEx 와 SetViewportExtEx 함수를이용하여단위와축의방향을결 정한다. 그런데, x 와 y 의단위는같은크기이다. MM_ANISOTROPIC MM_ISOTROPIC 과거의동일하지만, x 와 y 의단위크기가다를수있다. 매핑모드를사용할때에는이들의변형을위해서몇가지함수를사용할수있다. 이들함수는과거의설정을반환하므로이를임시변수에저장했다가, 사용이끝나면다시복구시켜주는것이좋다. 가장흔히사용되는함수로는다음과같은것들이있다. 1. SetMapMode: 매핑모드를결정한다. 2. SetWindowOrgEx: (0, 0) 이위치할좌표를설정한다. 3. SetWindowExtEx: 논리적단위를결정한다. 4. SetViewportExtEx: 논리적단위가매핑할유닛의디바이스크기를결정한다. 간단한예제를하나만들어보자. 폼에버튼을 2 개얹고각각의캡션을 LOMETRIC, ANISOTROPIC 으로설정한다. 버튼의캡션에서도추측할수있겠지만, Button1 은 MM_LOMETRIC 모드를, Button2 는 MM_ANISOTROPIC 모드를사용한다. 먼저 Button1 의 OnClick 이벤트핸들러를다음과같이작성한다. procedure TForm1.Button1Click(Sender: TObject); var OldMapMode: Integer; OldOrigin: TPoint; OldMapMode := SetMapMode(Canvas.Handle, MM_LOMETRIC); SetWindowOrgEx(Canvas.Handle, 0, 200, @OldOrigin); Canvas.Ellipse(0, 0, 200, 200); SetWindowOrgEx(Canvas.Handle, OldOrigin.X, OldOrigin.Y, nil); SetMapMode(Canvas.Handle, OldMapMode);
코드는단순하다. SetMapMode 함수를이용하여 MM_LOMETRIC 모드로설정하고, 원점을 (0, 200) 으로설정한다. MM_LOMETRIC 모드는 0.1mm 단위이므로원점은폼의좌상에서아래로 2cm 아래에위치하게된다. 여기에서지름이 2cm 인원을그리게된다. 그리고나서, 원래의원점과매핑모드를각각 OldMapMode 와 OldOrigin 변수에저장했던것을다시복구하면된다. Button2 의이벤트핸들러는다음과같이작성한다. procedure TForm1.Button2Click(Sender: TObject); var OldMapMode: Integer; OldOrigin: TSize; OldWindowExtent: TSize; OldViewportExtent: TSize; OldMapMode := SetMapMode(Canvas.Handle, MM_ANISOTROPIC); SetWindowExtEx(Canvas.Handle, 100, 100, @OldWindowExtent); SetViewportExtEx(Canvas.Handle, 200, 100, @OldViewportExtent); SetWindowOrgEx(Canvas.Handle, 0, -100, @OldOrigin); Canvas.Ellipse(0, 0, 100, 100); SetWindowOrgEx(Canvas.Handle, OldOrigin.cx, OldOrigin.cy, nil); SetViewportExtEx(Canvas.Handle, OldViewportExtent.cx, OldViewportExtent.cy, nil); SetWindowExtEx(Canvas.Handle, OldWindowExtent.cx, OldWindowExtent.cy, nil); SetMapMode(Canvas.Handle, OldMapMode); 별로다를내용은없지만, MM_ANISOTROPIC 모드에서 SetViewportExtEx 함수에의해 x 축의크기요소를 y 축의 2 배로설정하였으므로 Ellipas(0, 0, 100, 100) 메소드에의해가로의길이가세로의 2 배인타원이그려지게된다. 이어플리케이션을실행하고, 두개의버튼을차례로클릭하면다음과같은결과를볼수있다.
어플리케이션에비디오클립추가 델파이 4 의애니메이션컨트롤을이용하면조용한비디오클립을간단하게어플리케이션에추가할수있다. Win32 페이지의 TAnimate 컴포넌트를사용하려면 CommonAVI, FileName, ResName, ResID 프로퍼티중의하나를설정해서보여줄비디오클립을선택한다. 일단 AVI 파일을메모리에적재한뒤에, Active 프로퍼티를설정하거나 Play 메소드에의해스크린에 AVI 클립을보여줄때첫번째프레임부터보여주려할경우에는 Open 프로퍼티를 True 로설정한다. 그리고, AVI 클립을반복할횟수를 Repetitions 프로퍼티에설정하는데, 이값에 0 을주면 Stop 메소드가호출될때까지재생이반복된다. 그밖에여러가지설정을변경할수있는데예를들어, 첫번째프레임부터보여주지않고특정프레임부터시작하게하려면 StartFrame 프로퍼티를프레임의번호로설정하면된다. 오디오 / 비디오를모두재생할수있는어플리케이션의제작 오디오와비디오를모두재생할수있는어플리케이션을제작하려면, System 페이지에있는 TMediaPlayer 컴포넌트를사용한다. 이컴포넌트를사용하려면먼저 DeviceType 프로퍼티를사용하려는적절한디바이스유형으로설정해야한다. 이프로퍼티가 dtautoselect 로설정되면디바이스유형은미디어파일의확장자에의해결정된다. 디바이스가미디어를파일로저장하면, 미디어파일의이름을 FileName 프로퍼티에지정하여사용하게된다 AutoOpen 프로퍼티를 True 로설정하면, 미디어플레이어는자동으로지정된디바이스를열게된다. 이값이 False 이면 Open 메소드가호출되어야만디바이스가열린다. AutoEnable 프로퍼티는미디어플레이어버튼을자동을 enable, disable 할것인지를결정
하는것이다. 이를설정하거나아니면, EnableButtons 프로퍼티를이용하여각각의버튼을 enable, disable 시킬수있다. 미디어플레이어의버튼으로는다음과같이 Play, Pause, Stop, Next, Previous 등이있다. 경우에따라서는미디어플레이어를런타임에보이지않게하고싶을때에는 Visible 프로퍼티를 False 로설정하면되며, 이럴때에는 Play, Pause, Stop, Next, Previous 등의적절한메소드를호출하여작동시키면된다. 그밖에도미디어가디스플레이윈도우를요구할경우에는 Display 프로퍼티를미디어를디스플레이할컨트롤로설정하면되며, 디바이스가다중트랙을사용할경우에는 Tracks 프로퍼티를해당되는트랙으로설정하면된다. 디바이스유형과재생되는종류와트랙과디스플레이윈도우의사용여부에대한사항을다음에나타내었다. 디바이스사용되는하드웨어 / 소프트웨어트랙사용여부 디스플레이 컨트롤사용여부 dtavivideo AVI Video Player for Windows No Yes (AVI Video files) dtcdaudio CD Audio Player for Windows Yes No CD Audio Player (CD Audio Disks) dtdat Digital Audio Tape Player Yes No (Digital Audio Tapes) dtdigitalvideo Digital Video Player for Windows No Yes (AVI, MPG, MOV files) dtmmmovie MM Movie Player (MM film) No Yes dtoverlay Overlay device (Analog Video) No Yes dtscanner Image Scanner No No N/a for Play (scans images on Record) dtsequencer MIDI Sequencer for Windows (MIDI files) Yes No dtvcr Video Cassette Recorder (Video Cassettes) No Yes dtwaveaudio Wave Audio Player for Windows (WAV files) No No 애니메이션컨트롤의활용
Animate 컨트롤은단일비디오스트림을가진 AVI 파일만을재생할수있으며, RLE8 압축기법으로압축되거나압축을해제하며, 팔레트변경은할수없다. 그리고, 사운드가있을경우압축이무시된다. Animate 컨트롤의재미있는특징중에하나는폼위에올려놓고, FileName 이나 CommonAVI 프로퍼티를설정하고 Active 프로퍼티를 True 로하면, 디자인시에서도애니메이션을볼수있다. 그러면, 표준으로제공되는애니메이션인 CommonAVI 프로퍼티의내용을사용자가선택하면이를보여주는간단한예제를하나만들어보자먼저폼에리스트박스하나와버튼 2 개, 애니메이트컨트롤 1 개를얹어서다음과같이디자인한다. 리스트박스의 Items 프로퍼티를앞의화면에보이는대로설정하고, 버튼 2 개의 Caption 프로퍼티를 시작 과 중지 로설정한다. ListBox1 의 OnClick 이벤트핸들러를다음과같이설정한다. procedure TForm1.ListBox1Click(Sender: TObject); case ListBox1.ItemIndex of 0: Animate1.CommonAVI := avifindfolder; 1: Animate1.CommonAVI := avifindfile; 2: Animate1.CommonAVI := avifindcomputer; 3: Animate1.CommonAVI := avicopyfiles; 4: Animate1.CommonAVI := avicopyfile; 5: Animate1.CommonAVI := avirecyclefile; 6: Animate1.CommonAVI := aviemptyrecycle; 7: Animate1.CommonAVI := avideletefile;
즉, 리스트박스에보여주는내용을그대로적용하는것이다. OnClick 이벤트핸들러는각각다음과같이작성한다. 그리고, 두개의버튼의 procedure TForm1.Button1Click(Sender: TObject); Animate1.Active := True; procedure TForm1.Button2Click(Sender: TObject); Animate1.Stop; 이제완성이되었으므로, 이를실행해보자. 볼수있을것이다. 파일복사 를선택하면다음과같은화면을 정리 (Summary) 이번장에서는델파이를이용하여그림을그리고, 비트맵을다루는방법과비디오와오디오클립을사용하는방법에대해서알아보았다. 사실그래픽에대한부분은따로책을한권써야할정도로설명할내용도많고, 이해해야할내용도많다. 그리고, 시각적컨트롤을나름대로제작해서쓰려고하는사람들은여기에대한필수적인이해가있어야한다. 팔레트에대해서도잘알고있어야하며, 그래픽이라는객체들이크기가상당히큰경우들이많기때문에메모리를효과적으로사용하는방법이나파일포맷과파일을다루는방법등에대해서도잘알고있어야한다. 이책에서는지면관계상그래픽에대해서가장기본적이고기초적인부분만소개하였다.
하지만, 훌륭한델파이프로그래머가되기위해서는꼭넘어야할산이므로단순히델파이가제공하는캔버스의메소드에의존하지말고, 필수적인 API 함수들의사용법은반드시익혀놓도록권하는바이다. 다음장에서는윈도우 95 가등장하면서부터제공되기시작한 Win32 의향상된공통컨트롤 (Common Control) 들의사용법과활용방법에대해서알아볼것이다. 델파이의수많은컴포넌트들중에서유용하게사용될수있음에도불구하고, 사용법에대한소개와활용이비교적미흡한편인컨트롤들을중심으로설명할것이다.