CEGUIFont资源加载流程
Posted 林多
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CEGUIFont资源加载流程相关的知识,希望对你有一定的参考价值。
CEGUI Font资源加载流程
- Font(字体),主要两个类型的字体:位图字体、矢量字体。
- 位图字体:PixmapFont,相当于每个字形(glyph)对应一个图片元素。有时候也称为光栅字体。
- 矢量字体:FreetypeFont,动态字体。每个字形(glyph)由数学矢量组成,支持任意缩放。
- 本文以CEGUI 0.8.7为例子
Font加载流程
- CEGUI种可以通过Scheme文件加载,也可以使用下面的代码显式加载。
FontManager::getSingleton().createFromFile("DejaVuSans-12.font");
- DejaVuSans-12.font 文件内容。
<?xml version="1.0" ?>
<Font version="3" name="DejaVuSans-12" filename="DejaVuSans.ttf" type="FreeType" size="12" nativeHorzRes="1280" nativeVertRes="720" autoScaled="vertical"/>
- createFromFile会调用到其父类NamedXMLResourceManager的定义。
//----------------------------------------------------------------------------//
template<typename T, typename U>
T& NamedXMLResourceManager<T, U>::createFromFile(const String& xml_filename,
const String& resource_group,
XMLResourceExistsAction action)
U xml_loader;
xml_loader.handleFile(xml_filename, resource_group);
return doExistingObjectAction(xml_loader.getObjectName(),
&xml_loader.getObject(), action);
- 这里 U为 Font_xmlHandler 类型,T为 Font类型。Font_xmlHandler为重载handleFile,调用到其父类XMLHandler中的定义。
void XMLHandler::handleFile(const String& fileName, const String& resourceGroup)
System::getSingleton().getXMLParser()->parseXMLFile(
*this, fileName, getSchemaName(),
resourceGroup.empty() ? getDefaultResourceGroup() :
resourceGroup);
- 调用到XMLParser中的parseXMLFile函数。FileName就是字体资源文件名,shcemaName为“Font.xsd”,resourceGroup为CEGUI中资源组的概念(可以理解为资源路径),接下来开始解析 .Font文件。这里注意一点,对于Font加载,传入的this,为Font_xmlHandler这个类型。
void XMLParser::parseXMLFile(XMLHandler& handler, const String& filename, const String& schemaName, const String& resourceGroup)
// 省略
// Acquire resource using CEGUI ResourceProvider
// 这里会把.font文件内容,读入内存
RawDataContainer rawXMLData;
System::getSingleton().getResourceProvider()->loadRawDataContainer(filename, rawXMLData, resourceGroup);
CEGUI_TRY
// The actual parsing action (this is overridden and depends on the specific parser)
// 这里开始解析.Font文件中的元素
parseXML(handler, rawXMLData, schemaName);
CEGUI_CATCH (const Exception&)
// 省略,这里是一些异常的处理
// Release resource
// 释放资源
System::getSingleton().getResourceProvider()->unloadRawDataContainer(rawXMLData);
- 接下来调用具体xml解析模块的parseXML函数,这里handler(Font_xmlHandler)传入。例如走的是LibxmlParser
void LibxmlParser::parseXML(XMLHandler& handler,
const RawDataContainer& source,
const String& /*schemaName*/)
// 从内存中解析,因为之前已经将.font文件内容,加载到内存中了。
xmlDocPtr doc = xmlParseMemory(
reinterpret_cast<const char*>(source.getDataPtr()),
source.getSize());
if (!doc)
xmlError* err = xmlGetLastError();
CEGUI_THROW(GenericException(
String("xmlParseMemory failed in file: '") +
err->file + "' at line number" +
PropertyHelper<uint>::toString(err->line) + ". Error is:" +
err->message));
// get root element
xmlNode* root = xmlDocGetRootElement(doc);
// 这里这里开始解析xml元素。
processXMLElement(handler, root);
// release the xmlDoc
xmlFreeDoc(doc);
- processXMLElement的入参为Handler(Font_xmlHandler)和XML的RootElement。
// internal helper function to process elements
void processXMLElement(XMLHandler& handler, xmlNode* node)
// build attributes block for the element
// 省略
// element start processing
// Font_xmlHandler在这里发挥了这种。element的信息,在这里传给对应的handler。
handler.elementStart(reinterpret_cast<const encoded_char*>(node->name), attrs);
// 遍历
for (xmlNode* cur_node = node->children; cur_node; cur_node = cur_node->next)
switch(cur_node->type)
case XML_ELEMENT_NODE:
// 迭代
processXMLElement(handler, cur_node);
break;
case XML_TEXT_NODE:
if (cur_node->content != 0 && *cur_node->content!= '\\0')
handler.text(reinterpret_cast<const encoded_char*>(cur_node->content));
break;
default:
break;
// element end processing
// 结束解析
handler.elementEnd(reinterpret_cast<const encoded_char*>(node->name));
- 这里最重要的是,调用了Font_xmlHandler的elementStart,这里面会真正加载.ttf字体。
//----------------------------------------------------------------------------//
void Font_xmlHandler::elementStart(const String& element,
const XMLAttributes& attributes)
// handle root Font element
if (element == FontElement) // 解析 <Font />中的内容。
elementFontStart(attributes);
// handle a Mapping element
else if (element == MappingElement)
elementMappingStart(attributes);
// anything else is a non-fatal error.
else
Logger::getSingleton().logEvent("Font_xmlHandler::elementStart: "
"Unknown element encountered: <" + element + ">", Errors);
- 这个函数里,根据Element Name,做不同的处理。elementFontStart函数中,会根据 Font元素中的内容,加载字体文件。
void Font_xmlHandler::elementFontStart(const XMLAttributes& attributes)
validateFontFileVersion(attributes);
// get type of font being created
// 获取字体类型
// 最上面的.font文件中,写明了 type="FreeType"
const String font_type(attributes.getValueAsString(FontTypeAttribute));
// log the start of font creation.
CEGUI_LOGINSANE(
"Started creation of Font from XML specification:");
if (font_type == FontTypeFreeType) // 矢量字体
createFreeTypeFont(attributes);
else if (font_type == FontTypePixmap) // 位图字体
createPixmapFont(attributes);
else
CEGUI_THROW(InvalidRequestException(
"Encountered unknown font type of '" + font_type + "'"));
- 接下来调用 createFreeTypeFont,加载矢量字体。
void Font_xmlHandler::createFreeTypeFont(const XMLAttributes& attributes)
// name="DejaVuSans-12"
const String name(attributes.getValueAsString(FontNameAttribute));
// filename="DejaVuSans.ttf"
const String filename(attributes.getValueAsString(FontFilenameAttribute));
const String resource_group(attributes.getValueAsString(FontResourceGroupAttribute));
#ifdef CEGUI_HAS_FREETYPE
// 创建FreeTypeFont对象,FreeTypeFont在创建过程中,会加载ttf字体文件
// 并,构建 字形索引 和字符映射表
d_font = CEGUI_NEW_AO FreeTypeFont(name,
attributes.getValueAsFloat(FontSizeAttribute, 12.0f),
attributes.getValueAsBool(FontAntiAliasedAttribute, true),
filename, resource_group,
PropertyHelper<AutoScaledMode>::fromString(
attributes.getValueAsString(FontAutoScaledAttribute)),
Sizef(attributes.getValueAsFloat(FontNativeHorzResAttribute, 640.0f),
attributes.getValueAsFloat(FontNativeVertResAttribute, 480.0f)),
attributes.getValueAsFloat(FontLineSpacingAttribute, 0.0f));
#else
CEGUI_THROW(InvalidRequestException(
"CEGUI was compiled without freetype support."));
#endif
- 创建FreeTypeFont对象
FreeTypeFont::FreeTypeFont(const String& font_name, const float point_size,
const bool anti_aliased, const String& font_filename,
const String& resource_group,
const AutoScaledMode auto_scaled,
const Sizef& native_res,
const float specific_line_spacing) :
Font(font_name, Font_xmlHandler::FontTypeFreeType, font_filename,
resource_group, auto_scaled, native_res),
d_specificLineSpacing(specific_line_spacing),
d_ptSize(point_size),
d_antiAliased(anti_aliased),
d_fontFace(0)
if (!ft_usage_count++)
FT_Init_FreeType(&ft_lib); // 初始化 FreeType
addFreeTypeFontProperties();
updateFont(); // 这里会使用 freetype字体引擎处理字体文件
char tmp[50];
snprintf(tmp, sizeof(tmp), "Successfully loaded %d glyphs",
static_cast<int>(d_cp_map.size()));
Logger::getSingleton().logEvent(tmp, Informative);
- freetype是一个开源的字体引擎库,关于其概念和使用可以字形百度。接下来updateFont,会使用freetype加载ttf字体文件,进行相关的初始化。
void FreeTypeFont::updateFont()
free();
// 将.ttf 读入到内存中
System::getSingleton().getResourceProvider()->loadRawDataContainer(
d_filename, d_fontData, d_resourceGroup.empty() ?
getDefaultResourceGroup() : d_resourceGroup);
FT_Error error;
// 使用FreeType创建FontFace对象
// create face using input font
if ((error = FT_New_Memory_Face(ft_lib, d_fontData.getDataPtr(),
static_cast<FT_Long>(d_fontData.getSize()), 0,
&d_fontFace)) != 0)
CEGUI_THROW(GenericException("Failed to create face from font file '" +
d_filename + "' error was: " +
((error < FT_Err_Max) ? ft_errors[error] : "unknown error")));
// check that default Unicode character map is available
if (!d_fontFace->charmap)
FT_Done_Face(d_fontFace);
d_fontFace = 0;
CEGUI_THROW(GenericException(
"The font '" + d_name + "' does not have a Unicode charmap, and "
"cannot be used."));
uint horzdpi = static_cast<uint>(System::getSingleton().getRenderer()->getDisplayDPI().d_x);
uint vertdpi = static_cast<uint>(System::getSingleton().getRenderer()->getDisplayDPI().d_y);
float hps = d_ptSize * 64;
float vps = d_ptSize * 64;
if (d_autoScaled != ASM_Disabled)
hps *= d_horzScaling;
vps *= d_vertScaling;
// 设置像素尺寸,其实就是根据字号,进行设定
if (FT_Set_Char_Size(d_fontFace, FT_F26Dot6(hps), FT_F26Dot6(vps), horzdpi, vertdpi))
// For bitmap fonts we can render only at specific point sizes.
// Try to find nearest point size and use it, if that is possible
float ptSize_72 = (d_ptSize * 72.0f) / vertdpi;
float best_delta = 99999;
float best_size = 0;
for (int i = 0; i < d_fontFace->num_fixed_sizes; i++)
float size = d_fontFace->available_sizes [i].size * float(FT_POS_COEF);
float delta = fabs(size - ptSize_72);
if (delta < best_delta)
best_delta = delta;
best_size = size;
if ((best_size <= 0) ||
FT_Set_Char_Size(d_fontFace, 0, FT_F26Dot6(best_size * 64), 0, 0))
char size [20];
snprintf(size, sizeof(size), "%g", d_ptSize);
CEGUI_THROW(GenericException("The font '" + d_name + "' cannot be "
"rasterised at a size of " + size + " points, and cannot be "
"used."));
if (d_fontFace->face_flags & FT_FACE_FLAG_SCALABLE)
//float x_scale = d_fontFace->size->metrics.x_scale * FT_POS_COEF * (1.0/65536.0);
float y_scale = d_fontFace->size->metrics.y_scale * float(FT_POS_COEF) * (1.0f / 65536.0f);
d_ascender = d_fontFace->ascender * y_scale;
d_descender = d_fontFace->descender * y_scale;
d_height = d_fontFace->height * y_scale;
else
d_ascender = d_fontFace->size->metrics.ascender * float(FT_POS_COEF);
d_descender = d_fontFace->size->metrics.descender * float(FT_POS_COEF);
d_height = d_fontFace->size->metrics.height * float(FT_POS_COEF);
if (d_specificLineSpacing > 0.0f)
d_height = d_specificLineSpacing;
// 构建字符映像
initialiseGlyphMap();
- 字符映像其实是 字符码与字形索引的映射关系。字符码,就是某个字符在某种编码下得值,比如C的ASCII码为83。字形索引,就是字体文件中用来查找某个字形的索引。通过字体文件提供的字符映射表,来查找某个字符码对应的字形索引。
void FreeTypeFont::initialiseGlyphMap()
FT_UInt gindex;
FT_ULong codepoint = FT_Get_First_Char(d_fontFace, &gindex);
FT_ULong max_codepoint = codepoint;
while (gindex)
if (max_codepoint < codepoint)
max_codepoint = codepoint;
// 这里将字体中,所有的字符码,存储到map中。
d_cp_map[codepoint] = FontGlyph();
codepoint = FT_Get_Next_Char(d_fontFace, codepoint, &gindex);
setMaxCodepoint(max_codepoint);
- 到这里完成了字体加载的主要部分。字体渲染时,会根据传入的字符码,使用FT_Load_Char函数,将对应的字符码渲染出位图,并进行OpenGL的相应纹理(CEGUI的Render为OPENGL类型时)进行渲染。
以上是关于CEGUIFont资源加载流程的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )