这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数: // 得到文字在纹理中的位置 void CFont:: /*------------------------------------------------------------- char c1 --- 文字的第1个字节 char c2 --- 文字的第2个字节 int & tX --- 写入纹理中的坐标x int & tY --- 写入纹理中的坐标y -------------------------------------------------------------*/ Char2Texture( char c1, char c2, int & tX, int & tY ) { WORD w = MAKEWORD(c1, c2); // 把此字变为WORD vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w ); if ( it == _vBuf.end() ) // 如果没找到 { it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置 if ( it == _vBuf.end() ) // 缓冲已满 { for(; it!=_vBuf.begin(); it-- ) { it->hz = 0; } // Log.Output( "字体缓冲已满, 清空!" ); } // 计算当前空闲的Char在缓冲中是第几个 int at = it-_vBuf.begin(); // 得到空闲位置的坐标 tX = (at % _RowNum) * _TextSize; tY = (at / _RowNum) * _TextSize; // 设置这个Char为使用中 (*it).hz = w; RECT rect = {0, 0, _TextSize, _TextSize}; char sz[3] = {c1, c2, '\0'}; // 填充背景为黑色(透明色) FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) ); // 往hBitmap上写字 ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 ); // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明) D3DLOCKED_RECT d3dlr; _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK); BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX ); for (DWORD y=0; y<_TextSize; y++) { WORD * pDst16 = (WORD*)pDstRow; for (DWORD x=0; x<_TextSize; x++) { BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4); if (bAlpha > 0) *pDst16++ = (bAlpha << 12) | 0x0fff; else *pDst16++ = 0x0000; } pDstRow += d3dlr.Pitch; } _pTexture->UnlockRect( NULL ); } else { // 计算当前空闲的Char在缓冲中是第几个 int at = it-_vBuf.begin(); // 得到这个字的坐标 tX = (at % _RowNum) * _TextSize; tY = (at / _RowNum) * _TextSize; } } 以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的 struct Char{ char hz[3]; // 保存汉字 int frequency;// 使用频率 RECT rect; // 这个字对应位图的区域 Bool isUsing; // 是否使用 } 后来因为将char hz[3]合成为WORD,所以改为WORD hz。然后对于int frequency,这个词频应该如何表现,我一直没有想到很好的方法。frequency应该在何时++呢?是在每次被使用的时候吗?但是这样的话,上面说过,游戏是以60fps的速度在刷新,如果停上1分钟的话,变量很快就会溢出了。就算是使用像是DWORD或__int64这样的巨型变量保存,也是不安全的。除非能在某个合适的时候将frequency清零,但是这个“时候”是什么时候呢?或者设置一个最大值,如65535,但是这样也基本上没什么用途,很快,所有在vector中的Char中的frequency都会++成65535的。回忆一下最初,是因为想把常用字放到vector的前面,以便每次find操作可以最快返回结果的。而经过我的测试,即使不做这样的优化操作,速度也是很快的,毕竟Cache不是很大,加上vector是连续内存空间。所以可以放弃使用int frequency。 然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。 而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。 为什么要对结构Char这么精雕细琢呢? 1. 既然没有必要的东西,就应该删除 2. Char结构的大小越大,vector所要求的内存越大 3. 小的结构,find可以更快地查找出所结果 为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样: struct Char{ WORD hz; // 文字 Char() : hz(0) {} // 用作查找文字 inline bool operator == ( WORD h ) const { return hz==h ? true : false; } }; 是不是很简单?^___^ 终于到了显示的函数了: // 得到文字在纹理中的位置 bool CFont:: /*------------------------------------------------------------- char szText[] --- 显示的字符串 int x --- 屏幕坐标x int y --- 屏幕坐标y D3DCOLOR --- 颜色及alpha值 int nLen --- 字符串长度 float fScale --- 放大比例 -------------------------------------------------------------*/ TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale ) { Assert( szText!=NULL ); float sx = x, sy = y, offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0; w = h = (float)_TextSize * fScale; char ch[3] = {0,0,0}; FONT2DVERTEX * pVertices = NULL; UINT wNumTriangles = 0; _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD); if ( -1 == nLen || // 默认值-1 nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度 nLen = lstrlen( szText ); for (int n=0; n<nLen; n++ ) { ch[0] = szText[n]; if ( ch[0]=='\n' ) { sy+=h; sx=x; continue; } if ( ch[0] & 0x80 ) { n++; ch[1] = szText[n]; offset = w; } else { ch[1] = '\0'; offset = w / 2 ; } int a, b; Char2Texture( ch[0], ch[1], a, b ); // 计算纹理左上角 0.0-1.0 tx1 = (float)(a) / _TextureSize; ty1 = (float)(b) / _TextureSize; // 计算纹理右上角 0.0-1.0 tx2 = tx1 + (float)_TextSize / _TextureSize; ty2 = ty1 + (float)_TextSize / _TextureSize; // 填充顶点缓冲区 *pVertices++ = FONT2DVERTEX(sx, sy + h, 0.9f, color, tx1, ty2); *pVertices++ = FONT2DVERTEX(sx, sy, 0.9f, color, tx1, ty1); *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2); *pVertices++ = FONT2DVERTEX(sx + w, sy, 0.9f, color, tx2, ty1); *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2); *pVertices++ = FONT2DVERTEX(sx, sy, 0.9f, color, tx1, ty1); wNumTriangles+=2; sx+=offset; // 坐标x增量 } _pVB->Unlock(); _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles ); return true; }
|