안녕하세요.김성익(NOERROR)임니다.간단히 한글 출력에 대해 글을 써 내려가 보도록 하겠습니다. 자세한 응용은 각자 해보시길 바라고 여기 선 16x16크기로 8x4x4벌 한글폰트를 출력하는 꼴만을 기준으로 설명 드 리지요. 1. 한글구성 아주 기본적인 거지만 간단히 집고 넘어가겠습니다. 한글은 초성,중성,종성으로 이루어져 있습니다.( '김'이란 글자의 경 우 초성은 'ㄱ',중성은 'ㅣ',종성은 'ㅁ'이 되겠죠.) 초성 19자:ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ 중성 21자:ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ 종성 27자:ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅅㅆㅇㅈㅊㅋㅌㅍㅎ 로 이루어져 있습니다. 한글폰트는 초성,중성,종성별 데이타로 구성되어 있고 출력시엔 각 해당부분끼리 합성해서 완성된 형태를 출력합니다. 만약 일일이 합성된 한글로 구성해서 폰트를 만든다면 폰트적재만으로 메모리가 모자라겠죠 ? ( 모 잡지에 어떤 대회 대상 수상자분께서 한글을 메모리땜에 넣지 않았다는 코메디성발언을 하신적이 있지요. 아! 우수워.... 대상이라니 .. 제가 알기론 모잡지는 상당히 권위가 있는 잡지인 데...쩝) 글자 조합 형태는 초성+중성, 초성+중성+종성 2종류로 나눌수 있습니 다. 2. 한글코드 한글은 다들 아시다시피 2byte로 구성되어 있습니다.2byte에 초성,중 성,종성코드가 들어 있지요. 조합형문자의 구성을 알아보도록하죠. 76543210 76543210 x : 한글코드임을 나타냄 xxxxx : 초성 xx xxx : 중성 xxxxx : 종성 그럼 간단히 한글 코드를 이용해 초성,중성,종성으로 분류하는 루틴 을 짜보죠. void puthan(unsigned code) { unsigned char first; /* 초성 */ unsigned char middle; /* 중성 */ unsigned char last; /* 종성 */ first=(code>>10)&31; middle=(code>>5)&31; last=(code)&31; .. } 그런데 무신 이유에서인지 초성이나 중성,종성 모두 각 자별로 순차 적으로 들어 있는 것이 아니라 "ㄱ,ㄲ,.."을 순서대로 1,2,3,..의 번 호를 매길수가 없습니다.(중간에 사용하지 않는 부분이 있어서...) 그 래서 간단히 테이블처리를 하면 순서대로 처리할수 있습니다. unsigned char _first[]={ 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 }; unsigned char _middle[]={ 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 }; unsigned char _last[]={ 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 }; void puthan(unsigned code) { ... /* 앞의 내용 */ first=_first[first]; middle=_middle[middle]; last=_last[last]; ... } 이렇게 하면 first,middle,last에 각 글자의 데이타가 들어가게 되 죠.이제는 각 해당 폰트를 합성해서 출력하기만 하면 됨니다. 3. 한글벌수 한글출력루틴을 만드는 것이 어려운건 아니지만 다들 폰트와의 연 개부분(벌수조정)에서 어려움을 느끼고 포기하곤 하죠.하지만 그다지 어려운 내용도 아님니다. 다들 아시겠지만 한글출력이 단순히 초성,중성,종성만을 합성하는 것이 아님니다. 간단한 예로 같은 초성 'ㄱ'의 경우 '갬','그'의 초 성모양을 비교해 보세요.완전히 다름니다. '갬'에서 쓰이는 초성모양 으로 '그'를 만든다면 상당히 부자연스럽겠죠? 그래서 미려한 글자를 위해 여러가지 글자조합방식을 연구했는 데 .... 그중에서 가장 대중적인 것이 8x4x4나 10x4x4벌식의 한글이죠. (어떤분이 첨에 쓰셨는 지는 알수없지만 조금씩 낳은 모양,낳은 구조 로 발전하다가 결국 이런 형태로 굳어져 대부분 이런 구조로 폰트를 만드는 거겠죠.) 여기선 8x4x4에 대해 알아보죠. 8x4x4벌이란 초성 8벌,중성 4벌,종성 4벌을 나타냄니다.각 벌의 구 성을 알아보죠.그럼 아마 벌이란 무엇인지 아실검니다. 초성 초성 1벌 : 받침없는 'ㅏㅐㅑㅒㅓㅔㅕㅖㅣ' 와 결합 초성 2벌 : 받침없는 'ㅗㅛㅡ' 초성 3벌 : 받침없는 'ㅜㅠ' 초성 4벌 : 받침없는 'ㅘㅙㅚㅢ' 초성 5벌 : 받침없는 'ㅝㅞㅟ' 초성 6벌 : 받침있는 'ㅏㅐㅑㅒㅓㅔㅕㅖㅣ' 와 결합 초성 7벌 : 받침있는 'ㅗㅛㅜㅠㅡ' 초성 8벌 : 받침있는 'ㅘㅙㅚㅢㅝㅞㅟ' 중성 중성 1벌 : 받침없는 'ㄱㅋ' 와 결합 중성 2벌 : 받침없는 'ㄱㅋ' 이외의 자음 중성 3벌 : 받침있는 'ㄱㅋ' 와 결합 중성 4벌 : 받침있는 'ㄱㅋ' 이외의 자음 종성 종성 1벌 : 중성 'ㅏㅑㅘ' 와 결합 종성 2벌 : 중성 'ㅓㅕㅚㅝㅟㅢㅣ' 종성 3벌 : 중성 'ㅐㅒㅔㅖㅙㅞ' 종성 4벌 : 중성 'ㅗㅛㅜㅠㅡ' 대충 감을 잡으셨겠죠? 폰트가 다음과 같이 구성되어 있습니다. 폰 트에디터같은 걸로 보시면 초성문자 8묶음,중성 4묶음,종성 4묶음 씩 되어 있을 텐데... 각 묶음은 위와 같은 조건으로 그림거지요. 그럼 출력루틴을 만들어 보지요. 4. 벌수 분석 이제 한글 출력을 위한 모든 자료는 모였다고 볼수있습니다. 이제 출력을 위해 글자를 합성하기만 하면 되죠.앞에서 각 구성 코드로 나 누는 부분을 짜 보았고 이제 각 초성,중성,종성을 분석해서 벌수를 알아 내는 루틴을 만들어 보죠. 역시 테이블처리를 하면 간단합니다. unsigned char cho[]={ 0, 0,0,0,0,0,0,0,0,1,3,3,3,1,2,4,4,4,2,1,3,0 }; unsigned char cho2[]={ 0, 6,6,6,6,6,6,6,6,7,8,8,8,7,7,8,8,8,7,7,8,6 }; unsigned char jong[]={ 0, 0,2,0,2,1,2,1,2,3,0,2,1,3,3,1,2,1,3,3,1,1 }; void puthan(unsigned code) { char bul1,bul2,bul3; /* 초성,중성,종성 벌 저장변수 */ ... if(last==0) { /* 먼저 종성이 있는 지 없는 지를 조사 */ bul1=cho[middle]; /* 초성 */ if(first==1||first==24) bul2=1; /* 종성 */ else bul2=2; ... /* 이부분에서 합성하고 출력하도록하죠. */ } else { bul1=cho2[middle]; /* 초성 */ if(first==1||first==24) bul2=3; /* 종성 */ else bul2=4; bul3=jong[middle]; ... } } 바로 이렇게 bul1,bul2,bul3 형태로 나누는 검니다. 초성의 경우 중성문자에 따라 벌수를 정하고 중성은 'ㄱㅋ'인지를 조사해서 벌 수를 정하고 종성은 중성에 따라 역시 정하면 되지요. 이렇게 벌수를 나누었는 데... 실제로 벌수는 어떻게 쓰일 까요? 바로 폰트를 읽어들인 버퍼에서 해당 글자의 위치를 벌수에 맞추어 이동시켜주면 됨니다. 간단히 어드레스 계산에 대해 알아보죠. 각 글자 모양은 32바이트로 이루어져 있습니다.(가로 16,세로 16 인데 가로의 경우 2바이트가 16비트이므로 2*16바이트에 폰트모양 을 나타내는 거죠.) 그러니 초성이나 중성이나 각 글자별 코드에 32를 곱하면 해당 글자모양의 위치를 알수있습니다. 벌수의 경우에는 글자 하나를 뛰어 넘는 것이 아니라 아예 몽땅 뛰어 넘는 것이므로 각 묶음의 크기를 알아야 합니다. 초성의 경우 20자(빈공간포함 초성19자)이니 20*32하면 한 벌의 크기를 알수있 죠.(640이죠?) 앞에서 글자크기에 글자코드를 곱해서 위치를 얻은 것과 마찬가지로 벌수크기에 벌수를 곱하면 각 글자의 벌수를 알수 있지요. 초성 20자*32=640 중성 22자*32=704 종성 28자*32=896 참고) 전체크기 640*8+704*4+896*4=5120+2816+3584=11520 초성,중성,종성 글자의 포인터 위치를 알아내는 식을 아래와 같 이 나타낼수있죠. 위치 = 벌크기 * 벌수 + 글자크기 * 글자번호 5. 문자 합성 이제 모든 준비가 되었군요.이정도면 출력 루틴을 만드실수 있겠 겠죠? 한글코드를 주면 문자를 합성해서 리턴해주는 함수를 만들어 보 겠습니다.여기에 위에서 쓴 대부분의 내용이 나오겠죠. 아래가 바로 구현 소스임니다. unsigned char *font; /* 여기에 폰트데이타 11520byte가 있죠. */ unsigned char _first[]={0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19 }; unsigned char _middle[]={ 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 }; unsigned char _last[]={ 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 }; unsigned char cho[]={0,0,0,0,0,0,0,0,0,1,3,3,3,1,2,4,4,4,2,1,3,0 }; unsigned char cho2[]={0,6,6,6,6,6,6,6,6,7,8,8,8,7,7,8,8,8,7,7,8,6}; unsigned char jong[]={0,0,2,0,2,1,2,1,2,3,0,2,1,3,3,1,2,1,3,3,1,1}; #define COPY(A,B) for(loop=0;loop<32;loop++) *(B+loop)=*(A+loop); #define OR(A,B) for(loop=0;loop<32;loop++) *(B+loop)|=*(A+loop); void hancode(unsigned code,unsigned char *buffer) { unsigned first,middle,last,loop; unsigned offset; first=_first[(code>>10)&31]; middle=_middle[(code>>5)&31]; last=_last[(code)&31]; if(last==0) { offset=cho[middle]*640; /* 초성 */ offset+=first*32; COPY(font+offset,buffer); if(first==1||first==24) offset=5120; /* 중성 */ else offset=5120+704; offset+=middle*32; OR(font+offset,buffer); } else { offset=cho2[middle]*640; /* 초성 */ offset+=first*32; COPY(font+offset,buffer); if(first==1||first==24) offset=5120+704*2; /* 중성 */ else offset=5120+704*3; offset+=middle*32; OR(font+offset,buffer); offset=5120+2816+jong[middle]*896; /* 종성 */ offset+=last*32; OR(font+offset,buffer); } } 6. 영문자합성 영문자는 폰트에 따라 다르지만 폰트가 아스키코드 그대로 있다 고 한다면 아래와 같이 위의 한글 형태로 작성할수있습니다. (영문자는 8x16으로 한글자가 16 byte로 이루어져 있습니다.) unsigned char *fonte; /* 영문 폰트 (16*256)byte */ void engcode(unsigned char code,unsigned char *buffer) { unsigned offset,loop; offset=code*16; COPY(fonte+offset,buffer); } 7. 문자 출력 이제 문자 합성도 되니 출력하는 일만 남았군요.먼저 bit이미지를 출력하는 루틴을 만들어야 겠네요.특정모드용은 각자 만드시길 바라 고 간단히 점찍는 함수를 이용해서 비트이미지 출력하는 루틴을 만 들어보죠. unsigned char bitmask[]={128,64,32,16,8,4,2,1}; void _8x16(int x,int y,unsigned char color,char *image) { unsigned char xs,ys; for(ys=0;ys<16;ys++) for(xs=0;xs<8;xs++) if(image[ys]&bitmask[xs]) pixel(x+xs,y+ys,color); } void _16x16(int x,int y,unsigned char color,char *image) { unsigned char xs,ys; for(ys=0;ys<16;ys++) { for(xs=0;xs<8;xs++) if(image[ys*2]&bitmask[xs]) pixel(x+xs,y+ys,color); for(xs=0;xs<8;xs++) if(image[ys*2+1]&bitmask[xs]) pixel(x+xs+8,y+ys,color); } } 8. 글출력 대충 함수들이 다 모였군요.간단히 위의 함수들로 문장출력하는루 틴을 또 만들어보죠. void string(int x,int y,unsigned char color,unsigned char *str) { unsigned code; unsigned char temp[32]; while(*str){ code=*str++; if(code>=128) { /* 한글 */ code*=256; code|=*str++; hancode(code,temp); _16x16(x,y,color,temp); x+=16; } else { engcode(code,temp); _8x16(x,y,color,temp); x+=8; } } } /* 예. string(10,10,2,"우하하..푸하하...PUHAHA"); */