『게임 제작 강좌-프로그래밍 강좌 (go NGM)』 566번 제 목:[강좌] 심심강좌 16:한글 처리 올린이:myevan (송영진 ) 98/12/20 22:52 읽음:1953 관련자료 없음 ----------------------------------------------------------------------------- 나우누리 게임제작 포럼 엘리시아 이브의 심심강좌 Direct X : Windows Game Programing ==================================================================== Episode 1 ... 게임 엔진 제작 ==================================================================== Section 20 한글 변환 한글 윈도우에서는 기본적으로 한글 출력을 지원해 주고 있습니다만, 게임 에서 쓰기에는 글자 모양이 별로인듯 합니다. 모...그렇게 생각 안 하신다 면.. 이번 편은 그냥 넘어가셔도 좋습니다. 그래서 저의 경우는 한글 출력 루틴을 비트맵용으로 만들어 쓰고 있습니다. 그런데, 윈도우에서 쓰는 건 완성형이죠? 출력 루틴들은 거의 조합형인데, 이브군이 완성형 폰트라도 구한것일까요? 그럴 일은 없겠죠? ^^ 그럼... 워드프로세서나 에디터에서 조합형으로 작성 하라는 건... 역시 아닙니다. 자, 완성형폰트를 먼저 조합형으로 바꾸는 과정부터 알아보기로 합시다. 이부분을 상당히 어렵게 생각하시는 분들이 많은데요. 실은 매우 간단하게 할수 가 있습니다. 완성형 글꼴은 b0a1h 부터 시작해서 c8feh에 이르는 코드값을 가지고 있습 니다. 물론 중간에 빠지는 값들이 좀 있죠^^; +----------+-------------------------------+ | | 하위바이트 a1 ~~ fe | +----------+-------------------------------+ |상위바이트|가 | | | | | b0 | | | | .... | | ~ | | | | | | c8 | | | | 효| +----------+-------------------------------+ 음.. 표가 안 깨졌으면 좋겠는데요. 이런식으로 되어 있습니다. 즉, 가의 경우는 b0a1h, 효의 경우는 c8feh이죠 고로... 얍삽이 갑지만... 위 표를 조합형으로 써서 만든 다음에 표를 찾을 때 상위바이트에서 b0을 빼고, 하위에서 a1을 빼면^^ 룰루~~ 허억 -.- 그럼 저 표를 만들라고...? 하하. 직접 쓰실 필요는 없습니다. 왜냐하면... 여러분은 프로그래머이니까 요. #include main() { FILE *fp=fopen("Han.Inf","wb"); for(int hw=0xb000;hw<=0xc800;hw+=0x100) for(int lw=0x00a1;lw<=0x00fe;lw++) { short w=hw|lw; fputc(w>>8,fp); fputc(w & 0xff,fp); } fclose(fp); } 자, 이런 프로그램을 만들면... 위의 표를 만들수 있습니다. 다음에... 아무 에디터나 이것을 읽어서 조합형으로 저장해 주시면 됩니다 찾아서 읽는 일은 아주 간단하겠죠? 그럼~~끝~~^^ Section 21 한글 출력 자, 한글 출력을 해봅시다. 먼저 준비물이 필요합니다. 아까 만든 완성->조합 바꾸기 정보인 Han.Inf 와 도깨비 폰트가 들은 한글 영문 글꼴 파일입니다. 글꼴의 경우 통신망 공개 프로그램을 보면 쉽게 찾 을 수가 있습니다 - 도스용 프로그램의 경우요. 글꼴 파일의 구조는 어떨까요? 먼저 한글글꼴의 경우 3단계로 나뉘어져 있습니다. 초성, 중성, 종성으로... 먼저 나뉘고요. 각 부분은 벌수에 따라 다시 세분 됩니다. 초성의 경우는 8가지 글자 모양으로 20자가 있고요 중성은 4 가지 모양으로 22자, 종성은 역시 4가지 모양으로 28자 입니다. 한자는 32바이트입니다. 영문 글꼴은 단순합니다. 특수글꼴까지 합쳐 128자뿐입니다. 한자는 16바이트 입니다. ( 글자내용은 비트로 되어있습니다. 글자부분은 1로, 공백부분은 0이죠) 한글 프로그래밍에 있어 중요한 것은 각 글자 모양을 골라주는 일입니다. 글자 내용이 무엇인지 알아내는 일은 무지 쉽거든요. 먼저 한글인지 영문인지 알아내는 방법부터 볼까요? 한글의 경우 # # # # # # # # ^ | +- 이 첫 비트가 1로 세팅되어 있습니다. 즉, 한바이트 & 0x80을 하게 되면, 한글인지 영문인지 알수가 있습니다. 만약 한글이라면... 다음 바이트까지 읽어줍니다. # # # # # # # # # # # # # # # # | | | | | | | | +-초성--+ +-중성---+ +-종성--+ | | +- 한/영 구분 이런 모양으로 되어 있습니다. 각부분을 마스크 씌워서 나누어 주면 됩니다. // 한글로 합치기 WORD wHan; wHan=(*pb++)<<8; wHan|=(*pb++); // 각 성분으로 나누기 ChoCode =aabHanCodeTable[0][(wHan & 0x7c00)>>10]; JungCode=aabHanCodeTable[1][(wHan & 0x03e0)>>5]; JongCode=aabHanCodeTable[2][wHan & 0x001f]; 에... 이제 글자이 모양을 정해주어야 하는데요. 먼저 종성이 있냐 없냐에 따라 초성과 중성 모양이 바뀌게 됩니다. 종성의 모양은 중성에 따라 영향을 받고요, 중성의 모양은 초성과 종성에 영향을 받습니다. 초성은 종성과 중성의 영향을 받습니다. 이 변화값을 어찌 알까요? 일일이 한글자 한글자 찾아야 해야 겠지만, 다행히도 한글 프로그래밍의 선 구자님들이 이 정보를 미리 찾아 놓았기 때문에, 우리는 편하게 그 정보를 이용하면 됩니다. 이 정보는 소스에 집어 넣겠습니다. 모... 다음 할 일은 비트 내용을 찍어주는 일인데... 소스에서 설명해도 될 듯 하군요. 자, 그럼 글꼴 클래스를 보기로 할까요? #ifndef __FONT_CLASS_ #define __FONT_CLASS_ #define dHAN_COMPLETE 1 // 완성형 모드 #define dHAN_MIX 0 // 조합형 모드 #include "DD.H" class CFONT { // 한글 글꼴 영역 BYTE aabCho[8][20][32]; BYTE aabJung[4][22][32]; BYTE aabJong[4][28][32]; // 영문 글꼴 영역 BYTE abEng[128][16]; BYTE bColor; // 완성/조합 모드 BYTE bMode; BYTE **ppbMap; public: CFONT(); ~CFONT(); void SetColor(int Color); void SetMode(int Chk); void SetBmp(CBMP *pCBmp); void SetBmp(CBMPSCR *pCBmp); int LoadHan(LPSTR szName="Han.Fnt"); int LoadEng(LPSTR szName="Eng.Fnt"); int LoadInfo(LPSTR szName="Han.Inf"); void Puts(int x,int y,LPSTR szStr); void PutBit(int x,int y,WORD *pw); void PutBit(int x,int y,BYTE *pb); }; #endif 자, 이제 소스를 보기로 할까요? // 한글 조합용 정보 BYTE aabHanCodeTable[3][31]= { {0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, 0,0,0,0,0,0,0,0,0,0,}, {0,0,0,1,2,3,4,5,0,0,6,7,8,9,10,11,0,0,12,13,14,15,16,17, 0,0,18,19,20,21,0,}, {0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,0,17,18,19,20, 21,22,23,24,25,26,27,0,} }; BYTE aabChoInfo[2][22]= { {0,0,0,0,0,0,0,0,0,1,3,3,3,1,2,4,4,4,2,1,3,0}, {0,5,5,5,5,5,5,5,5,6,7,7,7,6,6,7,7,7,6,6,7,5} }; BYTE aabJungInfo[2][20]= { {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1}, {0,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3} }; BYTE abJongInfo[22]={0,0,2,0,2,1,2,1,2,3,0,2,1,3,3,1,2,1,3,3,1,1}; // 조합형 한글 변환 코드 정보 BYTE aabHanInfo[0xc8-0xb0+1][0xfe-0xa1+1][2]; 먼저 정보들입니다. 모... 말 그대로... 경험적 정보이기 때문에 설명은 붙이지 않겠습니다. int CFONT::LoadInfo(LPSTR szName) { HFILE hFile=_lopen(szName,OF_READ); if(hFile==HFILE_ERROR) return 0; _lread(hFile,aabHanInfo,4700); _lclose(hFile); return 1; } 완성형을 조합형으로 변환시켜주는 정보 파일을 읽어오는 것입니다. 간단해서리 -.-; 생략... int CFONT::LoadEng(LPSTR szName) { HFILE hFile; hFile=_lopen(szName,OF_READ); if(hFile==HFILE_ERROR) return 0; _lread(hFile,abEng,128*16); _lclose(hFile); return 1; } int CFONT::LoadHan(LPSTR szName) { HFILE hFile; hFile=_lopen(szName,OF_READ); if(hFile==HFILE_ERROR) return 0; // 초성 _lread(hFile,aabCho,8*20*32); // 중성 _lread(hFile,aabJung,4*22*32); // 종성 _lread(hFile,aabJong,4*28*32); _lclose(hFile); return 1; } 글꼴 파일을 읽어오는 건데요... 역시 말이 필요없겠죠? CFONT::CFONT() { SetBmp(CBmpScrV); bColor=255; bMode=dHAN_COMPLETE; LoadInfo(); LoadHan(); LoadEng(); } 자, 이제 생성자 부분입니다. SetBmp(CBmpScrV); 찍을 페이지를 설정하고요, bColor=255; 글자색을 정했습니다. bMode=dHAN_COMPLETE; 완성형 모드로 놓고요 LoadInfo(); LoadHan(); LoadEng(); 글꼴및 변환 정보를 읽어옵니다. 아래는 출력 루틴입니다. void CFONT::Puts(int x,int y,LPSTR szStr) { BYTE *pb=(BYTE*)szStr; BYTE abBuf[32]; while(*pb!=NULL) { if(*pb & 0x80) { WORD wHan; // 완성형 모드 if(bMode) { BYTE b1=(*pb++)-0xb0; BYTE b2=(*pb++)-0xa1; wHan= (aabHanInfo[b1][b2][0]<<8)| (aabHanInfo[b1][b2][1]); } // 조합형 모드 else { wHan=(*pb++)<<8; wHan|=(*pb++); } int ChoCode,JungCode,JongCode; int ChoN,JungN,JongN,Chk=0; ChoCode =aabHanCodeTable[0][(wHan & 0x7c00)>>10]; JungCode=aabHanCodeTable[1][(wHan & 0x03e0)>>5]; JongCode=aabHanCodeTable[2][wHan & 0x001f]; if(JongCode!=0) Chk=1; JongN=abJongInfo[JungCode]; JungN=aabJungInfo[Chk][ChoCode]; ChoN =aabChoInfo[Chk][JungCode]; memcpy(abBuf,aabCho[ChoN][ChoCode],32); BYTE *pb; if(JungCode) { pb=aabJung[JungN][JungCode]; for(int i=0;i<32;i++) abBuf[i]|=pb[i]; } if(JongCode) { pb=aabJong[JongN][JongCode]; for(int i=0;i<32;i++) abBuf[i]|=pb[i]; } PutBit(x,y,(WORD*)abBuf); x+=16; } else { memcpy(abBuf,abEng[*pb++],16); PutBit(x,y,abBuf); x+=8; } } } BYTE *pb=(BYTE*)szStr; 글줄 시작 부분을 얻어오고요. BYTE abBuf[32]; 출력 비트 정보를 넣을 곳입니다. while(*pb!=NULL) { 글줄끝까지 반복을... if(*pb & 0x80) { 만약 한글이라면... WORD wHan; // 완성형 모드 if(bMode) { BYTE b1=(*pb++)-0xb0; BYTE b2=(*pb++)-0xa1; wHan= (aabHanInfo[b1][b2][0]<<8)| (aabHanInfo[b1][b2][1]); 완성형이라면, 코드 변환 정보 테이블을 이용합니다. 찾기 방법은 위에서 설명한 듯 하내요. } // 조합형 모드 else { wHan=(*pb++)<<8; wHan|=(*pb++); } 조합형의 경우는 두 바이트을 하나로 합쳐줍니다. int ChoCode,JungCode,JongCode; int ChoN,JungN,JongN,Chk=0; ChoCode =aabHanCodeTable[0][(wHan & 0x7c00)>>10]; JungCode=aabHanCodeTable[1][(wHan & 0x03e0)>>5]; JongCode=aabHanCodeTable[2][wHan & 0x001f]; 초성, 중성, 종성을 분리해 내는 작업입니다. if(JongCode!=0) Chk=1; JongN=abJongInfo[JungCode]; JungN=aabJungInfo[Chk][ChoCode]; ChoN =aabChoInfo[Chk][JungCode]; 모양을 알아내는 부분이죠? memcpy(abBuf,aabCho[ChoN][ChoCode],32); 찾아낸 초성의 글자 모양을 출력 비트 정보에 넣어줍니다. BYTE *pb; if(JungCode) { pb=aabJung[JungN][JungCode]; for(int i=0;i<32;i++) abBuf[i]|=pb[i]; } 중성이 있다면 넣어줍니다. or연산을 하여 초성이 깨지지 않도록 합니다. if(JongCode) { pb=aabJong[JongN][JongCode]; for(int i=0;i<32;i++) abBuf[i]|=pb[i]; } 종성이 있다면 넣습니다. 역시 or연산으로 이전 글자 모양이 깨지지 않게 합니다. PutBit(x,y,(WORD*)abBuf); 워드형으로 비트 정보를 출력합니다. x+=16; } else { memcpy(abBuf,abEng[*pb++],16); PutBit(x,y,abBuf); 영문의 경우는 바로 찾아, 바이트형 비트 정보를 출력합니다. x+=8; } } void CFONT::PutBit(int Sx,int Sy,WORD *pw) { for(int y=Sy;y>8); for(int x=Sx;x>=1; } } } void CFONT::PutBit(int Sx,int Sy,BYTE *pb) { for(int y=Sy;y>=1; } } } 비트 정보를 출력하는 함수는 거의 비슷합니다. 최상위 비트 부터 마스크 를 해서 비트 내용이 있다면 점을 찍습니다. 그럼 날림 한글 출력 강좌도 끝~~;; ... 엘리시아 ===================================================================== e-mail myevan@nownuri.net & nowngm3@nownuri.net 나우누리 게임 제작 포럼 NGM http://blue.nownuri.net/ngm/ 게임 제작 팀 이터니티 http://mypage.channeli.net/blaze79/