随笔 - 16, 文章 - 0, 评论 - 55, 引用 - 0
数据加载中……

在windows下,当FLTK界面上包含中文的时候启动速度很慢,以下为修正过程

问题描述:
在windows下,当FLTK界面包含中文的时候,打开程序的时候会花费好几秒的时间才能完整显示界面

原因:
查了代码,最后发现原因在于绘制字符的时候通过GetTextExtentPoint32W这个函数获取字符宽度,由于这个函数本身速度不够快,所以FLTK使用缓存方式来保存宽度,问题在于缓存的方式不适合中文这种宽字符,当前的缓存方式是每当获取一个字符宽度时,把这个字符左右共1024个相邻字符的宽度提前获取并保存,以后每次获取字符宽度之前先搜索缓存,如果没有再通过API实际获取。

这个做法对于英文没有问题,因为GetTextExtentPoint32W处理英文的速度很快,而且一次获取1024个相邻字符基本就把程序可能会用到的字符全部囊括了,但是当界面出现中文的时候这种做法就出现问题了,中文的字符集是很大的,一次获取相邻个1024字符宽度并不能保证囊括了绝大多数的字符,所以每次界面显示之前都会花很多时间获取很多用不到的字符宽度,虽然显示一次之后的速度很快,但是启动程序的时候会出现卡顿

所以我做了修正,每当需要获取字符宽度的时候只保存当前字符的宽度,不获取相邻字符的宽度,这样就避免了问题

修正代码:
一共修改2个文件,当前修改的是FLTK3,如果要修改FLTK1.3.X,只要找到对应的代码即可
src/fltk3/font.h (fltk1.3.X对应的文件是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对应的文件是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) // 修改主体就是这个方法,具体代码如下
{
    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];
}

posted on 2014-04-29 17:28 cyantree 阅读(2018) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理