#include #include #include #include #include #define DBG_LEVEL DBG_INFO #define DBG_TAG "LibFont" #include static FT_Library mFtLib = NULL; static FT_Face mFtFace[MAX_FT_FACE_NUM]; static Int32 mFtFaceIdCnt = 0; /* 根据指定的字体文件初始化ftLib, 成功返回Ins_TRUE, libIDs数组中存放初始化完成的ftLib ID 失败或者部分成功返回Ins_FALSE, libIDs数组中可能会被赋值为INS_INVALID_RES_ID */ Bool InitFtLibs ( const char *ftFiles[], /*[in] 字体文件路径名字符串数组*/ Int32 *libIDs, /*[out] ftLib id数组, 从0开始[0,n]*/ Int32 num /*[in] 字体文件数量, ftLib数量*/ ) { Int32 i; Bool retv = INS_TRUE; if(mFtLib == NULL && FT_Init_FreeType(&mFtLib) != 0) { for(i = 0; i < num; i++)libIDs[i] = INS_INVALID_RES_ID; printf("FT_Init_FreeType fail\n"); return INS_FALSE; } for(i = 0; i < num && i < MAX_FT_FACE_NUM; i++) { if(FT_New_Face(mFtLib, ftFiles[i], 0, &mFtFace[mFtFaceIdCnt]) != 0) { mFtFace[mFtFaceIdCnt] = NULL; retv = INS_FALSE; libIDs[i] = INS_INVALID_RES_ID; printf("FT_New_Face %s fail\n", ftFiles[i]); } else { libIDs[i] = mFtFaceIdCnt; mFtFaceIdCnt++; } } for(; i < num; i++) /*(num >= MAX_FT_FACE_NUM)out of range?*/ { retv = INS_FALSE; libIDs[i] = INS_INVALID_RES_ID; } return retv; } /*释放ftLib占用的资源*/ void DeInitFtLibs ( Int32 *libIDs, /*[in] 需要释放的ftLib ID数组*/ Int32 num /*[in]数组大小*/ ) { if(mFtLib == NULL)return; int i; for(i = 0; i < MAX_FT_FACE_NUM; i++) { if(mFtFace[i] != NULL) { FT_Done_Face(mFtFace[i]); mFtFace[i] = NULL; } } FT_Done_FreeType(mFtLib); mFtLib = NULL; } /* 根据指定的ftLib ID及字符编码生成字形图 */ Bool GenFontBmps ( Int32 libID, /*[in] ftLib ID*/ Int32 charSize, /*[in] 单个字符最大宽度(最大高度为最大宽度的1.5倍)*/ Int32 texWidth, /*[in] 自行图宽度(=2^n)*/ Int32 codeNum, /*[in] 字符编码数组大小*/ const UInt32 *ucsCode, /*[in] 字符编码数组*/ Int32 *texHigh, /*[out] 字形图中包含有效字形的像素高度*/ UInt08 **pixelBuff, /*[out] 自行图像素(灰度/Alpha)数据(一个字节对应一个像素的Alpha值), 字节数大于texWidth x texHigh */ Flt32 **widthRate, /*[out] 字形实际宽度比值数组(实际宽度/charSize)*/ Flt32 **texcoord, /*[out] 与字符编码数组对应的OpenGL纹理坐标(左上角、右下角两组坐标)*/ Bool tight /*[in] 左右相邻的两个字形是否紧密排列, Ins_FALSE则按每个字形占charSize个像素宽度排列,无论字形的"肥瘦"*/ ) { Int32 x, y, n, column, row; Int32 texX0, texY0; UInt08 *tmpBuff; if(charSize > texWidth || pixelBuff == NULL || texHigh == NULL || codeNum < 1 || libID >= mFtFaceIdCnt || libID < 0) { return INS_FALSE; } FT_Set_Pixel_Sizes(mFtFace[libID], charSize, charSize); texX0 = 0; texY0 = 0; column = texWidth/charSize; /*glyph_num per line */ row = (codeNum+column-1)/column; /*line count*/ tmpBuff = (UInt08*)malloc(row*3*charSize/2*texWidth); /*glyph high is [3 x charSize / 2]*/ if(widthRate != NULL)widthRate[0] = (Flt32*)malloc(codeNum*sizeof(Flt32)); if(texcoord != NULL)texcoord[0] = (Flt32*)malloc(codeNum*4*sizeof(Flt32)); memset(tmpBuff, 0, row*3*charSize/2*texWidth); for(n = 0; n < codeNum; n++) { FT_Glyph glyph; UInt08 *bmpBuff; Int32 left, top, innx, inny, w, h; FT_Load_Glyph(mFtFace[libID], FT_Get_Char_Index(mFtFace[libID], ucsCode[n]), FT_LOAD_DEFAULT); if(FT_Get_Glyph(mFtFace[libID]->glyph, &glyph) != 0) { if(texcoord != NULL) { texcoord[0][4*n+0] = 0.0f; texcoord[0][4*n+1] = 0.0f; texcoord[0][4*n+2] = 0.0f; texcoord[0][4*n+3] = 0.0f; } if(widthRate != NULL)widthRate[0][n] = 0.0f; continue; } if(glyph->format != FT_GLYPH_FORMAT_BITMAP) { FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); } FT_BitmapGlyph bmpGlyph = (FT_BitmapGlyph)glyph; w = bmpGlyph->bitmap.width; h = bmpGlyph->bitmap.rows; left = bmpGlyph->left; top = bmpGlyph->top; if(top > charSize) top = charSize; bmpBuff = bmpGlyph->bitmap.buffer; if(w == 0) /*space*/ { w = charSize/2; h = 0; left = 0; top = 0; bmpBuff = NULL; } if(tight) { if(widthRate != NULL)widthRate[0][n] = 1.0f*w/charSize; if(texX0 + w > texWidth) { texX0 = 0; texY0 += 3*charSize/2; } x = texX0; y = texY0 + charSize - top; if(texcoord != NULL) { texcoord[0][4*n+0] = 1.0f*texX0/texWidth; texcoord[0][4*n+1] = texY0; texcoord[0][4*n+2] = 1.0f*(texX0+w)/texWidth; texcoord[0][4*n+3] = texY0+3.0f*charSize/2; } } else { if(widthRate != NULL)widthRate[0][n] = 1.0f; if(texX0 + charSize > texWidth) { texX0 = 0; texY0 += 3*charSize/2; } x = texX0 + left; y = texY0 + charSize - top; if(texcoord != NULL) { texcoord[0][4*n+0] = 1.0f*texX0/texWidth; texcoord[0][4*n+1] = texY0; texcoord[0][4*n+2] = 1.0f*(texX0+charSize)/texWidth; texcoord[0][4*n+3] = texY0+3.0f*charSize/2; } } for(inny = 0; inny < h; inny++) { for(innx = 0; innx < w; innx++) { tmpBuff[(y+inny)*texWidth + x + innx] = bmpBuff[inny*w+innx]; } } if(tight) { texX0 += w+2;/* +2 to make a gap between glyphs*/ } else { texX0 += charSize; } //printf("texX0:%d\n", texX0); FT_Done_Glyph(glyph); } pixelBuff[0] = tmpBuff; *texHigh = texY0 + 3.0f*charSize/2; if(texcoord != NULL)for(n = 0; n < codeNum; n++) { texcoord[0][4*n+1] = *texHigh - texcoord[0][4*n+1]; texcoord[0][4*n+3] = *texHigh - texcoord[0][4*n+3]; texcoord[0][4*n+1] /= *texHigh; texcoord[0][4*n+3] /= *texHigh; //printf("(%f, %f), (%f, %f)\n", texcoord[0][4*n+0], texcoord[0][4*n+1], texcoord[0][4*n+2], texcoord[0][4*n+3]); } /* if(widthRate != NULL)for(n = 0; n < codeNum; n++) { printf("%f\n", widthRate[0][n]); } for(y = 0; y < 0; y++)// *texHigh; y++) { for(x = 0; x < texWidth; x++) { if(pixelBuff[0][y*texWidth+x] < 10)printf("00%d ", pixelBuff[0][y*texWidth+x]); else if(pixelBuff[0][y*texWidth+x] < 100)printf("0%d ", pixelBuff[0][y*texWidth+x]); else printf("%d ", pixelBuff[0][y*texWidth+x]); } printf("\n"); } */ for(y = 0; y < *texHigh/2; y++) { for(x = 0; x < texWidth; x++) { texY0 = *texHigh-y-1; n = pixelBuff[0][y*texWidth + x]; pixelBuff[0][y*texWidth + x] = pixelBuff[0][texY0*texWidth + x]; pixelBuff[0][texY0*texWidth + x] = n; } } return INS_TRUE; } Bool DrawSingleLineTxtBmp ( Int32 libID, Int32 charSize, Int32 *bmpWidth, Int32 *bmpHigh, const UInt32 *ucsCode, UInt08 *bmpPixel, Bool tight, Int32 *numDrawn ) { Int32 x, y, n, cursorX, failWidth; FT_Glyph glyph[1]; if(charSize > *bmpWidth || bmpPixel == NULL || libID >= mFtFaceIdCnt || libID < 0) { if(numDrawn != NULL)*numDrawn = 0; return INS_FALSE; } FT_Set_Pixel_Sizes(mFtFace[libID], charSize, charSize); /* use '_' rectangle info for invalid(fail) character */ if(FT_Load_Glyph(mFtFace[libID], FT_Get_Char_Index(mFtFace[libID], '_'), FT_LOAD_DEFAULT) != 0 || FT_Get_Glyph(mFtFace[libID]->glyph, glyph) != 0) { failWidth = charSize/2; } else { if(glyph[0]->format != FT_GLYPH_FORMAT_BITMAP) { FT_Glyph_To_Bitmap(glyph, FT_RENDER_MODE_NORMAL, 0, 1); } FT_BitmapGlyph bmpGlyph = (FT_BitmapGlyph)glyph[0]; failWidth = bmpGlyph->bitmap.width; } FT_Done_Glyph(glyph[0]); glyph[0] = NULL; cursorX = 0; for(n = 0; ucsCode[n] != 0; n++) { UInt08 *buff = NULL; UInt32 idx, innx, inny; Int32 glyphLeft, glyphTop, glyphWidth, glyphHeight; if((idx = FT_Get_Char_Index(mFtFace[libID], ucsCode[n])) == 0 /*invalid character*/ || FT_Load_Glyph(mFtFace[libID], idx, FT_LOAD_DEFAULT) != 0 /*load fail*/ || FT_Get_Glyph(mFtFace[libID]->glyph, glyph) != 0) /*get fail*/ { printf("load glyph fail[ucs:0x%x]\n", ucsCode[n]); glyphWidth = 0; } else { if(glyph[0]->format != FT_GLYPH_FORMAT_BITMAP) { FT_Glyph_To_Bitmap(glyph, FT_RENDER_MODE_NORMAL, 0, 1); } FT_BitmapGlyph bmpGlyph = (FT_BitmapGlyph)glyph[0]; glyphWidth = bmpGlyph->bitmap.width; glyphHeight = bmpGlyph->bitmap.rows; glyphLeft = bmpGlyph->left; glyphTop = bmpGlyph->top; // printf("load glyph ok[ucs:0x%x]\n", ucsCode[n]); // printf("(%d, %d, %d, %d)\n", glyphLeft, glyphTop, glyphWidth, glyphHeight); buff = bmpGlyph->bitmap.buffer; } if(glyphWidth == 0) { glyphWidth = failWidth; glyphHeight = 0; glyphLeft = 0; glyphTop = 0; } if(tight) { if(cursorX + glyphWidth > (*bmpWidth)) { FT_Done_Glyph(glyph[0]); glyph[0] = NULL; break; } x = cursorX; y = charSize - glyphTop; } else { if(cursorX + charSize > (*bmpWidth)) { FT_Done_Glyph(glyph[0]); glyph[0] = NULL; break; } x = cursorX + glyphLeft; y = charSize - glyphTop; } if(y < 0)y = 0; // printf("cursorX:%d, bmpWidth:%d, bmpHeight:%d, (%d,%d)\n", cursorX, *bmpWidth, *bmpHigh, x, y); for(inny = 0; inny < glyphHeight; inny++) { for(innx = 0; innx < glyphWidth; innx++) { // printf("%x%x,", buff[inny*glyphWidth+innx]/16, buff[inny*glyphWidth+innx]%16); if((*bmpHigh) <= y+inny)continue; bmpPixel[((*bmpHigh)-y-inny-1)*(*bmpWidth) + x + innx] = buff[inny*glyphWidth+innx]; // bmpPixel[(y+inny)*(*bmpWidth) + x + innx] = buff[inny*glyphWidth+innx]; } // printf("\n"); } if(tight) { cursorX += glyphWidth+2;/* +2 to make a gap between glyphs*/ } else { cursorX += charSize; } FT_Done_Glyph(glyph[0]); glyph[0] = NULL; } if(numDrawn != NULL)*numDrawn = n; if(tight)cursorX -= 2; for(y = 1; y < *bmpHigh; y++) { for(x = 0; x < cursorX; x++) { bmpPixel[y*cursorX+x] = bmpPixel[y*(*bmpWidth)+x]; } } x = (*bmpHigh)*cursorX; y = (*bmpHigh)*(*bmpWidth); while(x < y)bmpPixel[(x++)] = 0; *(bmpWidth) = cursorX; // printf("width:%d, height:%d\n", (*bmpWidth), (*bmpHigh)); return INS_TRUE; }