Files
Cluster/insDavi2.0/src/lib/InsFont.c
2026-04-17 18:22:45 +08:00

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;
}