414 lines
12 KiB
C
414 lines
12 KiB
C
#include <InsCfg.h>
|
|
#include <lib/InsFont.h>
|
|
|
|
#include <ft2build.h>
|
|
#include <freetype/freetype.h>
|
|
#include <freetype/ftglyph.h>
|
|
|
|
#define DBG_LEVEL DBG_INFO
|
|
#define DBG_TAG "LibFont"
|
|
#include <InsDbg.h>
|
|
|
|
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;
|
|
}
|
|
|