관리 메뉴

ㄴrㅎnㅂrㄹrㄱi

[강좌 11] 그래픽2 본문

API 관련/API 강좌모음

[강좌 11] 그래픽2

님투 2007. 10. 26. 03:40
반응형

[API]강좌(11)<--그래픽2





윈도우즈에서 비트맵 그림을 출력하는 방법은 생각외로 간단합니다. 그런데 이 루
틴을 가지고 게임을 만들수 있을까요? 너무 느리죠.
이런 단점을 보완해서 나온것이 WinG라고 하는 그래픽 출력 라이브러리인데
그나마 속력이 많이 개선 되었다고 할수 있습니다. 우리가 아래에서 다룰 예제
로는 쉽게 빠르다는 것을 구분할수 없지만요...
자 그러면 먼저 일반적인 출력 방법부터 알아 봅시다. 일반적으로 비트맵을 다루
기 위해서는 리소스 파일에서 그 비트맵 파일을 정의 해 주었다는 것을 기억할 겁
니다. 역시 출력할 비트맵 파일을 리소스 파일에서 정의하면 됩니다. 비트맵 파일
을 정의하고 나서 그 파일을 이용하려면 비트맵 핸들을 얻었다는 것을 기억하십니
까? 잘 기억이 안난다고요? LoadBitmap()이라는 함수를 이용해서 했잖아요.
여기까지 이해가 된다면 이제 추가된 새로운 방법을 알아 봅시다. 먼저 비트맵 그림
을 출력하기 위해서는 메모리에 디바이스 컨텍스트 핸들을 생성해야 됩니다. 우리가
일반적으로 생성한 디바이스 컨텍스트 핸들을 이용해서 문자를 출력하면 바로 화면
에 보여질 겁니다. 그렇죠? 여지껏 그렇게 했잖아요. 그런데 메모리에 디바이스 컨
텍스트 핸들을 생성하고 그 핸들을 이용해서 출력하면 화면에 보일까요? 당연히 안
보이죠. 그런데 왜 비트맵을 출력할때 이런과정이 필요할까요? 그것은 메모리에
그림 전체를 출력해 놓은 다음에 그것을 디바이스 컨텍스트 핸들을 이용해서 통채로
복사하기 위해서입니다. 게임 프로그래밍을 해 보신 분은 쉽게 이해할수 있겠죠?
HDC CreateCompatibleDC(HDC hDC);
위 함수를 이용해서 메모리에 디바이스 컨텍스트 핸들을 생성할수 있습니다. 파라
미터로 현재 디바이스 컨텍스트 핸들을 지정하면 됩니다. 메모리 디바이스 컨텍스
트 핸들을 생성해서 사용한후에는 꼭 헤제해주어야 합니다.
BOOL DeleteDC(HDC hDC);
위 함수를 이용해서 해제해주면 됩니다. 파라미터로 메모리 디바이스 컨텍스트 핸들
을 지정해 주면 됩니다.
자 이번에는 비트맵 그림의 크기를 얻는 방법을 알아 봅시다. 우리가 출력할 비트맵
그림이야 그 크기가 얼마나 되는지 자신이 알고 있을수도 있지만 모르고 있는 경우
도 있습니다. 그런데 왜 그림의 크기를 알고 있어야 할까요? 메모리에 있는 그림을
현 디바이스 컨텍스트로 복사할때 사용하는 함수가 크기를 지정하도록 하는 파라미
터를 요구하기 때문입니다.
int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOID lpvObject);
위 함수를 이용해서 비트맵 그림에 대한 정보를 얻을수 있습니다. 첫번째 파라미터
로 비트맵 핸들을 지정하면 되고 세번째 파라미터에 BITMAP 구조체로 선언한 변수
의 주소를 지정하면 됩니다. 두번째 파라미터는 이 BITMAP 구조체의 크기를 지정하
면 되구요. 두번째 파라미터에 sizeof(BITMAP)이라고 지정하면 되겠죠.
아래는 BITMAP 구조체의 원형입니다.
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP;
위 구조체의 맴버중 bmWidth, bmHeight에 그 크기의 정보가 저장됩니다.
BOOL BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeigth,
HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop);
위 함수를 이용해서 메모리에 있는 그림을 현 디바이스 컨텍스트 핸들로 복사할수
있습니다. 즉 화면에 출력할수 있는것입니다.
첫번째 파라미터가 출력할 현 디바이스 컨텍스트 핸들을 의미하는 것입니다. 두번
째 파라미터와 세번째 파라미터는 출력할 좌표를 의미하고 네번째, 다섯번째 파라
미터는 그 넓이와 높이를 의미합니다. 이 파라미터에 비트맵 그림의 크기를 지정
하면 됩니다. 여섯번째 파라미터는 메모리 디바이스 컨텍스트 핸들을 의미합니다.
그러면 일곱번째와 여덟번째 파라미터는 뭘 의미할까요? 바로 메모리 디바이스 컨
텍스트 핸들의 첫번째 좌표를 의미하는 것입니다. 처음부터 다 복사하려면 0, 0을
지정하면되죠. 마지막 파라미터는 복사를 어떻게 할것인지를 의미하는 것입니다.
이 파라미터에 올수 있는 예약어에는 다음과 같은것들이 있습니다.
SRCCOPY 메모리의 내용을 그대로 복사한다.
SRCAND 메모리의 내용과 현 화면과 AND 연산을 한다.
SRCINVERT 메모리의 내용과 현 화면을 XOR 연산을 한다.
SRCPAINT 메모리의 내용과 현 화면을 OR 연산을 한다.
자 그러면 대략적으로 어떤식으로 되는지 알아 보았으니 실제로 이것을 이용한 예
제를 봅시다. 아래는 비트맵 그림을 화면에 출력하는 예제입니다.
먼저 리소스 파일입니다.
#include <windows.h>
MyBitmap BITMAP "test.bmp"
프로그램 소스입니다.
#include <windows.h>
#include <stdlib.h>
#include <string.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP hBitmap;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Graphic Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hBitmap = LoadBitmap(hInstance, "MyBitmap");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC, hMemDC;
BITMAP bitmap;
PAINTSTRUCT ps;
static int nX, nY;
switch(message)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
BitBlt(hDC, 0, 0, nX, nY, hMemDC, 0, 0, SRCCOPY);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
먼저 리소스 파일을 보면 알겠지만비트맵 파일을 정의하죠?
hBitmap = LoadBitmap(hInstance, "MyBitmap");
정의한 비트맵 그림을 로드하는 함수입니다. 이미 설명을 드렸을 겁니다. 자 그러면
실제로 화면에 출력하는 WM_PAINT 메시지 부분을 보도록 합시다.
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = CreateCompatibleDC(hDC);
메모리에 디바이스 컨텍스트 핸들을 얻는 과정입니다.
SelectObject(hMemDC, hBitmap);
메모리 디바이스 컨텍스트 핸들로 앞에서 로드한 비트맵 핸들을 취하는 과정입니다.
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
비트맵 핸들을 이용해서 비트맵에 관한 정보를 BITMAP 구조체에 넣는 과정입니다.
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
비트맵의 크기를 선언한 변수에 저장하고 있습니다. 이게 왜 필요한지는 아래 함수
를 보면 알수 있을 겁니다.
BitBlt(hDC, 0, 0, nX, nY, hMemDC, 0, 0, SRCCOPY);
메모리 디바이스 컨텍스트 핸들을 이용해서 메모리에 출력했던 것을 현 디바이스
컨택스트 핸들을 취하는 현재 화면에 복사하는 과정입니다.
DeleteDC(hMemDC);
메모리 디바이스 컨텍스트 핸들을 이용한후에는 반드시 해제 해 주어야 한다고 했
을 겁니다.
EndPaint(hWnd, &ps);
return 0;
자 이번에는 그래픽 출력을 빨리 해 준다는 WinG를 이용한 비트맵 그림의 출력에
대해 알아 보겠습니다. 원리는 같죠. 단지 함수만 다를뿐입니다. 우선 메모리에
디바이스 컨텍스트 핸들을 생성하는 부분과 실제로 화면에 복사하는 함수만 WinG에
서 제공하는 함수로 사용하면 됩니다.
HDC WINGAPI WinGCreateDC( void );
위 함수를 이용해서 디바이스 컨텍스트 핸들을 생성하면 됩니다. 파라미터를 요구
하지 않는군요.
BOOL WINGAPI WinGBitBlt( HDC hdcDest, int nXOriginDest,
int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc,
int nXOriginSrc, int nYOriginSrc );
위 함수를 이용해서 실제 화면에 복사하면 됩니다. 앞에서 알아본 BitBlt()함수와
파라미터의 의미는 같습니다. 단지 BitBlt()함수에 들어갔던 마지막 파라미터가 없
다는 차이점은 있습니다.
간단하죠? 그러면 소스를 보도록 합시다.
아래는 리소스 파일입니다.
#include <windows.h>
MyBitmap BITMAP "test.bmp"
프로그램 소스입니다.
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "wing.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP hBitmap;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Graphic Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hBitmap = LoadBitmap(hInstance, "MyBitmap");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC, hMemDC;
BITMAP bitmap;
PAINTSTRUCT ps;
static int nX, nY;
switch(message)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = WinGCreateDC();
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
WinGBitBlt(hDC, 0, 0, nX, nY, hMemDC, 0, 0);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
뭐 특별한 부분은 없죠?
#include "wing.h"
헤더 파일에 WinG 라이브러리를 사용하기 위한 헤더 파일을 선언했습니다.
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = WinGCreateDC();
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
WinGBitBlt(hDC, 0, 0, nX, nY, hMemDC, 0, 0);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
실제로 출력하는 부분인데 정말 간단하죠? 이 프로그램을 컴파일 할려면
wing32.lib 파일을 같이 묶어서 컴파일 해 주어야 합니다.
 
비트맵 그림을 출력하는 예제를 볼건데 이것은 그림의 크기에
관계없이 작업영역에 꽉차게 출력되게 하는 것입니다. 즉 그림크기가 작업영역
크기보다 작으면 확대하고 크면 축소해서 출력하는 것입니다.
역시 같은 방법으로 API 함수를 이용해서 하는 방법과 WinG 그래픽 라이브러리를 이
용해서 하는 방법 두가지로 알아 보겠습니다.
보면 알겠지만 앞에서 그림출력하는 과정을 잘 이해하셨다면 이번 부분은 특별하게
새로 배울 부분은 없을 겁니다. 왜냐하면 함수 하나만 다르거든요. 그림을 축소, 확
대 해서 복사하는 함수가 있습니다.
그러면 먼저 API 함수를 이용해서 해보도록 합시다. 작업영역의 크기만큼의 그림으로
출력할려면 작업영역의 크기를 얻는 과정이 있어야 합니다. 물론 여러분들은 어떻게
크기를 얻는지 알수 있을 겁니다. 앞에서 여러번 했으니까요. 그리고 한가지만 더
알면 됩니다. 바로 BitBlt() 함수대신에 StretchBlt()함수를 사용한다는 것입니다.
BOOL StretchBlt(HDC hdcDest, int nXDest, int nYDest, int nWidthDest,
int nHeigthDest, HDC hdcSrc, int nXSrc, int nYSrc, int nWidth,
int nHeight, DWORD dwRop);
위 함수를 이용해서 축소하거나 확대 할수 있는데 네번째, 다섯번째 파라미터에 유
저가 값을 지정해주면 그 크기로 복사가 됩니다. 그리고 BitBlt() 함수에 없던 새로
운 파라미터가 있는데 바로 아홉번째와 열번째 파라미터입니다. 이 부분에는 실제
그림의 크기를 지정해 주면 됩니다.
자 그러면 예제를 보도록 합시다.
아래는 리소스 파일의 소스입니다.
#include <windows.h>
MyBitmap BITMAP "test.bmp"
프로그램 소스입니다.
#include <windows.h>
#include <stdlib.h>
#include <string.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP hBitmap;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Graphic Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hBitmap = LoadBitmap(hInstance, "MyBitmap");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC, hMemDC;
BITMAP bitmap;
PAINTSTRUCT ps;
RECT rect;
static int nX, nY;
switch(message)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
GetClientRect(hWnd, &rect);
StretchBlt(hDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0,
nX, nY, SRCCOPY);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
머리가 좋으신분은 이미 다 이해했을 겁니다. 너무 쉽죠.
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
GetClientRect(hWnd, &rect);
StretchBlt(hDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, nX, nY,
SRCCOPY);
작업 영역의 크기를 얻은뒤 그 크기만큼 그림을 화면에 출력하는 과정입니다.
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
별로 설명 드릴부분은 없군요. 이번에는 WinG 그래픽 라이브러리를 이용해서 똑같
은 기능을 하는 프로그램을 만들어 봅시다. 이번에도 역시 함수 하나만 알면 됩니
다.
BOOL WINGAPI WinGStretchBlt( HDC hdcDest, int nXOriginDest,
int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc,
int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc );
파라미터의 의미는 StretchBlt() 함수와 같습니다. 단지 마지막 파라미터에 있던
것이 없어졌을 뿐입니다.
#include <windows.h>
MyBitmap BITMAP "test.bmp"
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "wing.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP hBitmap;
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
static char szAppName[] = "Graphic Example";
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW|CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass))
return FALSE;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hBitmap = LoadBitmap(hInstance, "MyBitmap");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC, hMemDC;
BITMAP bitmap;
PAINTSTRUCT ps;
RECT rect;
static int nX, nY;
switch(message)
{
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = WinGCreateDC();
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
GetClientRect(hWnd, &rect);
WinGStretchBlt(hDC, 0, 0, rect.right, rect.bottom, hMemDC,
0, 0, nX, nY);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
간단하죠?
case WM_PAINT :
hDC = BeginPaint(hWnd, &ps);
hMemDC = WinGCreateDC();
SelectObject(hMemDC, hBitmap);
GetObject(hBitmap, sizeof(BITMAP), (BITMAP *)&bitmap);
nX = bitmap.bmWidth;
nY = bitmap.bmHeight;
GetClientRect(hWnd, &rect);
WinGStretchBlt(hDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, nX, nY);
작업영역의 크기를 알아 낸뒤 그 크기만큼 그림을 출력하는 과정입니다.
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
return 0;
반응형

'API 관련 > API 강좌모음' 카테고리의 다른 글

[강좌 13] 멀티미디어1  (0) 2007.10.26
[강좌 12] 그래픽3  (0) 2007.10.26
[강좌 10] 그래픽1  (0) 2007.10.26
[강좌 9] 메뉴2  (0) 2007.10.26
[강좌 8] 메뉴1  (0) 2007.10.26
Comments