汉字特技字幕功能实现 一、汉字字形库。 中文字形库中存放以点阵或笔画形式的中文汉字信息。中文字形库存取的内容是 中文字形和其他种可见图形字符的字形信息。根据字库工作方式和存储介质,通 常可把字库分成字母式和点阵式两种,下表列出几种汉字字形发生方式和有关特点: 由上表可以看出,作为一般用途的字库有数字点阵式和数字笔画式,随着Windows 的出现,又添加了一种TrueType字体。点阵式和TrueType字体适宜打印用途,可 以得到高清晰度的字体,但是占用内存大,而且不灵活。相对而言,笔画式字形 库易于实现文字控制,符合制作汉字特技的需要,因此选用这种字形库。实践也 表明,利用该种字形库制作的汉字特技效果明显优于点阵字形库,它占用内存小 ,可实现无极缩放,字体无锯齿,处理方便可靠。 二、平面位图字体实现。 使用矢量字库,可实现任意缩放、旋转。不同的矢量字库其结构也不尽相同,本 文用的是RAINBOW矢量字库。矢量字库分为汉字字库和符号字库两类。首先要搞清 楚所用矢量字库的结构,从字库文件中取得相应的字体信息。本文用的字库可以 根据国标码得到汉字或字符中每一笔画的顶点坐标,然后 在进行各种处理。用 Windows 编程时,首先在内存设备背景中建立画笔和画刷,然后用CreateBitmap( )建立一个虚拟位图,下面就可以在内存设备背景中画字体位图了。但是,如果对 每个笔画用GDI的画多边形函数Polygon( )来画,会出现字体中笔画交叉处留有空 隙,不能填充颜色。对此,采用多个多边形画线函数PolyPolygon( )可完好地解 决该问题,具体做法是,把每个笔画作为一个多边形,相邻两个多边形首尾相连 ,形成一多个多边形数组,然后画图。 hBitmap=CreateBitmap(nWidth,nHeight,1,1,NULL); hDC=GetDC(NULL); hMemoryDC=CreateCompatibleDC(hDC); hBitmap=SelectObject(hMemoryDC,hBitmap); PatBlt(hMemoryDC,0,0,Width,Height,BLACKNESS); //将位图置为全黑,以备下面用画笔和画刷绘图 hBrush=SelectObject(hMemoryDC,GetStockObject(WHITE_BRUSH)); hPen=SelectObject(hMemoryDC,GetStockObject(WHITE_PEN)); { 处理每个笔画的顶点群,生成一多个多边形数组; } SetPolyFillMode(hMemoryDC,WINDING); //设置画图填充模式 PolyPolygon(hMemoryDC,XYPoint,avertices,numpoly); //画图并用画刷自动填充 这样就在虚拟内存中建立了位图字体。 三、汉字特技实现 (1)立体阴影字。 给出阴影方向和阴影长度,在该方向上重复画阴影长度次位图字体,就得到所需的 字体效果。 (2)轮廓字。 用上面的程序,不选入画刷,只用画笔画多边形。 (3)字体倾斜。 给出倾斜度,利用下面计算公式对字体信息中的顶点坐标值重新计算: fx=XYPoint[j+numpoint].x;//字体信息中顶点坐标 fy=XYPoint[j+numpoint].y; if (nCharSlant<0)// nCharSlant 为字体倾斜度,单位为整数 fx=fx*(nCharWidth+nCharSlant)/dot -nCharSlant*fy/nCharHeight; // nCharWidth 为字体宽度,nCharHeight为字体高度,dot是字库信息中字体的原始 宽度。 else fx=fx*(nCharWidth-nCharSlant)/dot +nCharSlant*(nCharHeight-fy)/nCharHeight; fy=fy*nCharHeight/dot; XYPoint[j+numpoint].x=(int)fx+ox; XYPoint[j+numpoint].y=(int)fy+oy; 用得到的新字体顶点坐标制作位图字体,得到倾斜平面字。可以利用该平面字,根据立体阴影字的制作方法得到倾斜立体阴影字。 (4)字体无极缩放。 由(3)的计算公式,当字体倾斜度nCharSlant=0时,字体宽度nCharWidth、字体 高度nCharHeight是任意给定的,经过公式计算得到字体顶点坐标的调整值,制作 位图字体,然后利用上面的方法实现字体特技。 四、小结。 本文利用汉字和字符的矢量字库,在Windows环境下实现了几种特技。读者可以根 据自己的兴趣和需要,对字体各个控制量,改变输入方式,使界面符合具体工作 需要。对Turbo C熟悉的用户,可以根据此文介绍的方法,实现该语言环境下的汉 字特技,不过要自己编写多边形填充程序,作者已与人合作实现了此功能并用于 商场大屏幕汉字显示系统。(附程序) 参考文献: (1)《计算机图形学教程》----唐荣锡、汪嘉业、彭群生等编著,科学出版社出 版。 (2)《 PROGRAMMINGING WINDOWS 3.1程序设计》---北京希望电脑公司编著,海洋 出版社出版。 (3)《计算机中文信息处理》---赵伯璋、徐力编著,宇航出版社出版。 ////////* font.def */////////// NAME HELLOWIN DESCRIPTION 'Hello Windows Program (c) Charles Petzold, 1992' EXETYPE WINDOWS STUB 'WINSTUB.EXE' CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE MULTIPLE HEAPSIZE 10240 STACKSIZE 8192 /////////* font.c *////////////// #include #include #define indexorg 0x100 int nCharWidth=80,nCharHeight=150,nCharSlant=0,nCharSpace=5; int nTextMode=0,nCharShadow=8; WORD RgbColor=RGB(255,0,0); long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG) ; BOOL OutChar(int ,int ,char *,int ); void DisplayChar(int , int, unsigned char *,int ,int ,int); void bmp_to_tvp(HDC,int ,int ,HBITMAP ); char *hzname="d:\\font\\rainbow.001", //汉字字库 *charname="d:\\font\\rainbow.030"; //符号字库 int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { static char szAppName[] = "test" ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; if (!hPrevInstance) { 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 ; RegisterClass (&wndclass) ; } hwnd = CreateWindow (szAppName, "show ny word", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640,480,//CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, nCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam) { HDC hdc ; PAINTSTRUCT ps ; char * ch="祝你: 天天快乐!"; int n; switch (message) { case WM_CREATE: hdc = GetDC (hwnd) ; ReleaseDC (hwnd, hdc) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; n=strlen(ch); OutChar(0,0,ch,n); EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL OutChar(int nCharx,int nChary,char ch[],int n) { // position of string, string ,length of the sting int fi; unsigned i,j,idot; long pixelorg ,wordaddr ,wordlen ; long code,v,u[2]; BYTE *hzlib;// HANDLE hhz; int wid=0; long len2; unsigned char ch1,ch2; char pp; char *filename; //begin hhz=LocalAlloc(LMEM_MOVEABLE|LMEM_ZEROINIT, sizeof(char)*1000); hzlib=(BYTE *)LocalLock(hhz); i=0; while(i0x20) { code-=0x21; //ASCII '!' is 0x21 the first word //len=0xe; filename=charname; } else filename=NULL; fi=_lopen(filename,READ); if(fi==NULL) return FALSE; _llseek(fi,0x8a,SEEK_SET); //pixel array _lread(fi,&idot,2); _lread(fi,&pixelorg,4); //array adress code =((long)code+188)<<2; v=indexorg+code; _llseek(fi,v,SEEK_SET); //index adress _lread(fi, &u,8 ); wordaddr=u[0]+pixelorg ; wordlen=u[1]-u[0]; _llseek(fi,wordaddr,SEEK_SET); _lread(fi, hzlib,wordlen); for (j=0;j=2 len-=2; } else { for (j=0;j1) { XYPoint[n+numpoint]=XYPoint[0+numpoint]; numpoint=(WORD)numpoint+(n+1); avertices[numpoly]=n+1; numpoly++; } /*SetROP2(hMemoryDC,R2_NOT); Polyline(hMemoryDC,XYPoint,n); SetROP2(hMemoryDC,rm); Polyline(hMemoryDC,XYPoint,n+1); */ } } //must use PolyPolygon() instead of Polygon //otherwise there will be empty interior the font. //edit by tjx 1996/11/12 whole day SetPolyFillMode(hMemoryDC,WINDING); PolyPolygon(hMemoryDC,XYPoint,(int *)avertices,numpoly); // SetROP2(hMemoryDC,rm); //no use Raster operate mode // SelectObject(hMemoryDC,hBrush); // SelectObject(hMemoryDC,hPen); //SelectObject(hMemoryDC,hBrush); //SelectObject(hMemoryDC,hPen);//no use hBitmap=SelectObject(hMemoryDC,hBitmap); /* switch(nShadowDir) { case 0: x= 1;y=-1;break; case 1: x=-1;y=-1;break; case 2: x=-1;y= 1;break; case 3: x= 1;y= 1;break; } */ x= 1;y= 1; if (nTextMode==1) kk=nCharShadow-1; else kk=0; for (j=nCharShadow;j>kk;j--) bmp_to_tvp(hDC,cx+j*x-ox,cy+j*y-oy,hBitmap); /* cxWidth=(DWORD)GetDibWidth (lpDibM); GetObject(hBitmap,sizeof(BITMAP),&Bitmap); Mmove=(cxWidth-Bitmap.bmWidth-1)/2; if(Mmove>0) bmp_to_tvp(lpDibM,Mmove,cx,cy,hBitmap); */ DeleteDC(hMemoryDC); ReleaseDC(NULL,hDC); DeleteObject(hBitmap); GlobalUnlock(hglb); GlobalFree(hglb); } void bmp_to_tvp(HDC hdc,int x,int y,HBITMAP hBmp) { BITMAP Bitmap; HGLOBAL hGbl; long len; unsigned char huge *buff; unsigned char huge *ptr; int i,j,k,m; unsigned char b; GetObject(hBmp,sizeof(BITMAP),&Bitmap); if (Bitmap.bmBitsPixel!=1) return; len=(long)Bitmap.bmHeight*(long)Bitmap.bmWidthBytes; hGbl=(HGLOBAL)GlobalAlloc(GHND,len); if (hGbl==NULL) return; buff=(unsigned char huge *)GlobalLock(hGbl); GetBitmapBits(hBmp,len,buff); ptr=buff; for (i=0;i>=1) { k++; if (k>Bitmap.bmWidth) break; if ((b & ptr[j])&&((x+j*8+m)<640)&&((x+j*8+m)>=0) &&((y+i)>=0)&&((y+i)<480)) SetPixel(hdc,x+j*8+m,200-(y+i),RgbColor); } } ptr+=Bitmap.bmWidthBytes; } GlobalUnlock(hGbl); GlobalFree(hGbl); }