問(wèn)題描述:
在windows下,當(dāng)FLTK界面包含中文的時(shí)候,打開(kāi)程序的時(shí)候會(huì)花費(fèi)好幾秒的時(shí)間才能完整顯示界面
原因:
查了代碼,最后發(fā)現(xiàn)原因在于繪制字符的時(shí)候通過(guò)GetTextExtentPoint32W這個(gè)函數(shù)獲取字符寬度,由于這個(gè)函數(shù)本身速度不夠快,所以FLTK使用緩存方式來(lái)保存寬度,問(wèn)題在于緩存的方式不適合中文這種寬字符,當(dāng)前的緩存方式是每當(dāng)獲取一個(gè)字符寬度時(shí),把這個(gè)字符左右共1024個(gè)相鄰字符的寬度提前獲取并保存,以后每次獲取字符寬度之前先搜索緩存,如果沒(méi)有再通過(guò)API實(shí)際獲取。
這個(gè)做法對(duì)于英文沒(méi)有問(wèn)題,因?yàn)镚etTextExtentPoint32W處理英文的速度很快,而且一次獲取1024個(gè)相鄰字符基本就把程序可能會(huì)用到的字符全部囊括了,但是當(dāng)界面出現(xiàn)中文的時(shí)候這種做法就出現(xiàn)問(wèn)題了,中文的字符集是很大的,一次獲取相鄰個(gè)1024字符寬度并不能保證囊括了絕大多數(shù)的字符,所以每次界面顯示之前都會(huì)花很多時(shí)間獲取很多用不到的字符寬度,雖然顯示一次之后的速度很快,但是啟動(dòng)程序的時(shí)候會(huì)出現(xiàn)卡頓
所以我做了修正,每當(dāng)需要獲取字符寬度的時(shí)候只保存當(dāng)前字符的寬度,不獲取相鄰字符的寬度,這樣就避免了問(wèn)題
修正代碼:
一共修改2個(gè)文件,當(dāng)前修改的是FLTK3,如果要修改FLTK1.3.X,只要找到對(duì)應(yīng)的代碼即可
src/fltk3/font.h (fltk1.3.X對(duì)應(yīng)的文件是src/fl_font.h)
class Fl_Font_Descriptor {
public:
...
# ifdef WIN32
HFONT fid;
int *width[64];
unsigned char *widthcached[64]; // 0-not cache, 1-cached //這里增加定義
...
};
src/fltk3/win32_font.cxx(fltk1.3.X對(duì)應(yīng)的文件是src/fl_font_win32.cxx)
Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, fltk3::Fontsize fsize)
{
...
int i;
for (i = 0; i < 64; i++) {
width[i] = NULL;
widthcached[i] = NULL; // 這里增加
}
...
}
Fl_Font_Descriptor::~Fl_Font_Descriptor()
{
...
int i;
for (i = 0; i < 64; i++) {
if ( width[i] != NULL ) free(width[i]);
if ( widthcached[i] != NULL ) free(widthcached[i]); // 這里增加
}
}
double fltk3::GDIGraphicsDriver::width(unsigned int c) // 修改主體就是這個(gè)方法,具體代碼如下
{
Fl_Font_Descriptor *fontsize = font_descriptor();
unsigned int r;
SIZE s;
// Special Case Handling of Unicode points over U+FFFF.
// The logic (below) computes a lookup table for char widths
// on-the-fly, but the table only covers codepoints up to
// U+FFFF, which covers the basic multilingual plane, but
// not any higher plane, or glyphs that require surrogate-pairs
// to encode them in WinXX, which is UTF16.
// This code assumes that these glyphs are rarely used and simply
// measures them explicitly if they occur - This will be slow...
if(c > 0x0000FFFF) { // UTF16 surrogate pair is needed
if (!fl_gc) { // We have no valid gc, so nothing to measure - bail out
return 0.0;
}
int cc; // cell count
unsigned short u16[4]; // Array for UTF16 representation of c
// Creates a UTF16 string from a UCS code point.
cc = fltk3::ucs_to_Utf16(c, u16, 4);
// Make sure the current font is selected before we make the measurement
SelectObject(fl_gc, fontsize->fid);
// measure the glyph width
GetTextExtentPoint32W(fl_gc, (WCHAR*)u16, cc, &s);
return (double)s.cx;
}
// else - this falls through to the lookup-table for glyph widths
// in the basic multilingual plane
r = (c & 0xFC00) >> 10;
unsigned short ii;
HDC gc;
HWND hWnd;
if (!fontsize->width[r]) {
fontsize->width[r] = (int*) malloc(sizeof(int) * 0x0400);
fontsize->widthcached[r] = (unsigned char *)malloc(sizeof(unsigned char) * 0x0400);
for (int k=0; k<0x0400; k++) fontsize->widthcached[r][k] = 0;
ii = r * 0x400;
// The following code makes a best effort attempt to obtain a valid fl_gc.
// If no fl_gc is available at the time we call fltk3::width(), then we first
// try to obtain a gc from the first fltk window.
// If that is null then we attempt to obtain the gc from the current screen
// using (GetDC(NULL)).
// This should resolve STR #2086
gc = fl_gc;
hWnd = 0;
if (!gc) { // We have no valid gc, try and obtain one
// Use our first fltk window, or fallback to using the screen via GetDC(NULL)
hWnd = fltk3::first_window() ? fl_xid(fltk3::first_window()) : NULL;
gc = GetDC(hWnd);
}
if (!gc) fltk3::fatal("Invalid graphic context: fltk3::width() failed because no valid HDC was found!");
SelectObject(gc, fontsize->fid);
ii += c &0x03FF;
GetTextExtentPoint32W(gc, (WCHAR*)&ii, 1, &s);
fontsize->width[r][c&0x03FF] = s.cx;
fontsize->widthcached[r][c&0x03FF] = 1;
if (gc && gc!=fl_gc) ReleaseDC(hWnd, gc);
//printf("[%d,%X]\n", s.cx, c);
} else {
if ( fontsize->widthcached[r][c&0x03FF] == 1 ) return (double) fontsize->width[r][c & 0x03FF];
ii = r * 0x400;
gc = fl_gc;
hWnd = 0;
if (!gc) { // We have no valid gc, try and obtain one
// Use our first fltk window, or fallback to using the screen via GetDC(NULL)
hWnd = fltk3::first_window() ? fl_xid(fltk3::first_window()) : NULL;
gc = GetDC(hWnd);
}
if (!gc) fltk3::fatal("Invalid graphic context: fltk3::width() failed because no valid HDC was found!");
SelectObject(gc, fontsize->fid);
ii += c &0x03FF;
GetTextExtentPoint32W(gc, (WCHAR*)&ii, 1, &s);
fontsize->width[r][c&0x03FF] = s.cx;
fontsize->widthcached[r][c&0x03FF] = 1;
if (gc && gc!=fl_gc) ReleaseDC(hWnd, gc);
//printf("[%d,%X]\n", s.cx, c);
}
return (double) fontsize->width[r][c & 0x03FF];
}