当前位置: 首页 > 工具软件 > gfx > 使用案例 >

gfx使用嵌入字体时的优化

徐安康
2023-12-01

本文讨论优化多个进程在共用gfx嵌入字体的过程.

flash做界面有着天然的优势.随着gfx的发展也看到越来越多的游戏使用gfx做界面工作.

本项目组使用gfx也有几年时间了,这次接了个棋牌项目,很自然的就想使用flash作为界面.

与客户端项目不同的是单个棋牌游戏必须保持"身材瘦小".都能保持在5M以下是比较满意的.而且启动速度越快越好.

gfx使用的字体方案分为两种.

一种是系统字体.Windows字体或FT.

一种是嵌入字体即将整个字体库嵌入到swf文件当中,一个字体大概就在5M左右.粗体,斜体都算另一种字体.

gfx有着自己的字体渲染系统,在使用系统字体时会有明显的锯齿.对于一些应用来说是不可接受的.

但使用嵌入字体一是大小增加了近20M.共用文件又使游戏间产生了依赖关系.二是启动时读取一个大文件会影响启动速度.

于是想到使用共用内存来解决依赖关系并减少IO读取,提高启动速度.

Windows下合适的方案就是文件映射了.由于不存在写操作.所以不需要同步.整个过程非常简单.


fontfilemaping.h

#pragma once

#include <Windows.h>
#define FONTLIBMOVIE		"fonts_cn.swf"

//为gfx本地化字库做内存共享
class FontFileMaping
{
public:

	FontFileMaping();
	~FontFileMaping();

	bool	CreateFontFileMaping( void );

	void	OpenFontFileMaping( void );

protected:
	unsigned char*	m_zData;
	int				m_iFileSize;
	HANDLE			hFontMap;	//字体文件映射句柄
};


fontfilemaping.cpp

#include "stdafx.h"
#include "FontFileMaping.h"

#define FONTMAPINGNAME		"gfxfontfile"
#define FONTSIZEMAPINGNAME	"gfxfontfilesize"


FontFileMaping::FontFileMaping()
{
	m_zData = NULL;
	m_iFileSize = 0;
	hFontMap = NULL;
}

FontFileMaping::~FontFileMaping()
{
	if ( m_zData )
	{
		UnmapViewOfFile( m_zData );
		m_zData = NULL;
	}
	if ( hFontMap )
	{
		CloseHandle( hFontMap );
		hFontMap = NULL;
	}
}
bool	FontFileMaping::CreateFontFileMaping( void )
{
	HANDLE fh;

	fh = ::CreateFileA( FONTLIBMOVIE, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	m_iFileSize = GetFileSize( fh, NULL );
	hFontMap = CreateFileMappingA( fh, NULL, PAGE_READONLY, 0, m_iFileSize, FONTMAPINGNAME );
	if ( hFontMap == 0 )
	{
		int iEr = GetLastError();
		printf( "Can not Create File Maping. error Code : %d", iEr );
		//assert( 0 );
		return false;
	}
	printf( "Create File Maping Success" );
	MapViewOfFile( hFontMap, PAGE_READONLY, 0, 0, 0 );

	CloseHandle( fh );

	HANDLE hMapSize = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, FONTSIZEMAPINGNAME );	//用来传递文件大小.
	if ( hMapSize == 0 )
	{
		int iEr = GetLastError();
		assert( 0 );
		return false;
	}

	int* lpData = (int*)MapViewOfFile( hMapSize, FILE_MAP_ALL_ACCESS, 0, 0, 1024 );
	lpData[0] = m_iFileSize;
	return true;
}

void	FontFileMaping::OpenFontFileMaping( void )
{
	hFontMap = OpenFileMappingA( FILE_MAP_READ, false, FONTMAPINGNAME );

	m_zData = (unsigned char*)MapViewOfFile( hFontMap, FILE_MAP_READ, 0, 0, 0 );

	HANDLE hMapSize = OpenFileMappingA( FILE_MAP_READ, false, FONTSIZEMAPINGNAME );

	int* lpSizeData = (int*)MapViewOfFile( hMapSize, FILE_MAP_READ, 0, 0, 1024 );
	m_iFileSize = lpSizeData[0];
}

接下来要在自定义gfx读写文件类,在读取文件时对已做文件映射的字体文件进行特殊处理.

gfx提供了现成的读写已在内存中的文件的类GMemoryFile.


ShareFontFileOpener.h

#pragma once

#include "..\FontFileMaping.h"

class ShareFontFileOpener : public GFxFileOpener, public FontFileMaping
{
public:

	ShareFontFileOpener( void );
	~ShareFontFileOpener( void );

	virtual GFile* OpenFile(const char* purl, 
		SInt flags = GFileConstants::Open_Read|GFileConstants::Open_Buffered, 
		SInt mode = GFileConstants::Mode_ReadWrite);

	inline virtual SInt64 GetFileModifyTime(const char* purl)
	{
		return 0;
	}

	inline virtual GFile* OpenFileEx(const char* purl, class GFxLog *plog, 
		SInt flags = GFileConstants::Open_Read|GFileConstants::Open_Buffered, 
		SInt mode = GFileConstants::Mode_ReadWrite)
	{
		return OpenFile( purl );
	}
};


ShareFontFileOpener.cpp


#include "stdafx.h"
#include "ShareFontFileOpener.h"

ShareFontFileOpener::ShareFontFileOpener( void )
{
	OpenFontFileMaping();
}

ShareFontFileOpener::~ShareFontFileOpener()
{

}


GFile* ShareFontFileOpener::OpenFile(const char* purl, SInt flags, SInt mode)
{
	if ( strcmp( purl, FONTLIBMOVIE ) == 0 )
	{
		GMemoryFile* pMemFile = new GMemoryFile( purl, m_zData, m_iFileSize );
		return pMemFile;
	}
	return GFxFileOpener::OpenFile( purl, flags, mode );
}

最后在定义GfxLoader时设置FileOpener

	GPtr<GFxFileOpener> pfileOpener = *new ShareFontFileOpener;
	loader.SetFileOpener(pfileOpener); 


一切OK.对于项目应用来说.在大厅启动时CreateFontFileMaping.考虑到可能启动多个大厅的情况.则创建失败后仅输出log并返回.

游戏启动时则共用同一个字体文件.



 类似资料: