|
1 引言
我们在介绍 MiniGUI 体系结构的第一篇文章中提到,MiniGUI 采用了面向对象的技术实现了 GAL、IAL 以及多字体和多字符集的支持。字体和字符集的支持,对任何一个 GUI 系统来讲都是不可缺少的。不过,各种 GUI 在实现多字体和多字符集的支持时,采用不同的策略。比如,对多字符集的支持,QT/Embedded采用 UNICODE 为基础实现,这种方法是目前比较常用的方法,是一种适合于通用系统的解决方案。然而,这种方法带来许多问题,其中最主要就是 UNICODE 和其他字符集之间的转换码表会大大增加 GUI 系统的尺寸。这对某些嵌入式系统来讲是不能接受的。
MiniGUI 在内部并没有采用 UNICODE 为基础实现多字符集的支持。MiniGUI的策略是,对某个特定的字符集,在内部使用和该字符集完全一致的内码表示。然后,通过一系列抽象的接口,提供对某个特定字符集文本的一致分析接口。该接口既可以用于对字体模块,也可以用来实现多字节字符串的分析功能。如果要增加对某个字符集的支持,只需要实现该字符集的接口即可。到目前为止,MiniGUI 已经实现了 ISO8859-x 的单字节字符集支持,以及 GB2312、BIG5、EUCKR、UJIS 等多字节字符集的支持。
和字符集类似,MiniGUI 也针对字体定义了一系列抽象接口,如果要增加对某种字体的支持,只需实现该字体类型的接口即可。到目前为止,MiniGUI 已经实现了对 RBF 和 VBF 字体(这是 MiniGUI 定义的两种光栅字体格式)、TrueType 和 Adobe Type1 字体等的支持。
在多字体和多字符集的抽象接口之上,MiniGUI 通过逻辑字体为应用程序提供了一致的接口。
本文重点介绍 MiniGUI 的逻辑字体、多字体和多字符集的实现,并以 EUCKR(韩文)字符集和 Adobe Type1 字体为例,说明如何在 MiniGUI 中实现一种新的字符集支持和新的字体类型支持。
2 逻辑字体、设备字体以及字符集之间的关系
在 MiniGUI 中,每个逻辑字体至少由一个单字节的设备字体组成。设备字体是直接与底层字体相关联的数据结构。每个设备字体有一个操作集(即 font_ops),其中包含了 get_char_width、get_char_bitmap 等抽象接口。每个 MiniGUI 所支持的字体类型,比如等宽光栅字体(RBF)、变宽光栅字体(VBF)、TrueType 字体、Adobe Type1 字体等均对应一组字体操作集。通过这个字体操作集,我们就可以从相应的字体文件中获得某个字符的点阵(对光栅字体而言)或者轮廓(对矢量字体而言)。之后,MiniGUI 上层的绘图函数就可以将这些点阵输出到屏幕上,最终就可以看到显示在屏幕上的文字。
图 1 给出了逻辑字体、设备字体以及字符集之间的关系。
在设备字体结构中,还有一个字符集操作集(即 charset_ops),其中包含了 len_first_char、char_offset、len_first_substr 等抽象接口。每个 MiniGUI 所支持的字符集,比如 ISO8859-x、GB2312、BIG5 等字符集均对应一组字符集操作集。通过这个字符集操作集,我们就可以对某个多种字符集混合的字符串进行文本分析。比如在“ABC中文”这个字符串中,头三个字符是属于 ISO8859 的字符,而“中文”是属于 GB2312 的字符。通过调用这两个字符集操作集中的函数,我们就可以了解该字符串中哪些字符是属于 ISO8859 的字符,哪些字符是属于 GB2312 的字符,甚至可以进行更加复杂的分析。比如,MiniGUI 中的 GetFirstWord 函数可以从这种字符串中获得第一个单词。比如“ABC DEF 中文”字符串中的第一个单词是“ABC”,而第二个单词是“DEF”,第三个单词和第四个单词分别是“中”和“文”。该函数的实现如下:
int GUIAPI GetFirstWord (PLOGFONT log_font, const char* mstr, int len, WORDINFO* word_info) { DEVFONT* sbc_devfont = log_font->sbc_devfont; DEVFONT* mbc_devfont = log_font->mbc_devfont; if (mbc_devfont) { int mbc_pos; mbc_pos = (*mbc_devfont->charset_ops->pos_first_char) (mstr, len); if (mbc_pos == 0) { len = (*mbc_devfont->charset_ops->len_first_substr) (mstr, len); (*mbc_devfont->charset_ops->get_next_word) (mstr, len, word_info); return word_info->len + word_info->nr_delimiters; } else if (mbc_pos > 0) len = mbc_pos; } (*sbc_devfont->charset_ops->get_next_word) (mstr, len, word_info); return word_info->len + word_info->nr_delimiters; } |
该函数首先判断该逻辑字体是否包含多字节设备字体(mbc_devfont是否为空),如果是,则调用多字节字符集对应的操作函数 pos_first_char、len_first_substr、get_next_word 等函数获得第一个单词信息,并填充 word_info 结构。如果该逻辑字体只包含单字节设备字体,则直接调用单字节字符集对应的操作函数 get_next_word。一般而言,在 GetFirstWord 等函数中,我们首先要进行多字节字符集的某些判断,比如 pos_first_char 返回的是字符串中属于该字符集的第一个字符的位置。如果返回值不为零,表明第一个字符是单字节字符;如果为零,才会调用其他函数进行操作。
有了这样的逻辑字体、设备字体和字符集结构定义,当我们需要新添加一种字符集或者字体支持时,只需按照我们的字体操作集和字符集操作集定义对应的新操作集结构即可,而对上层程序没有任何影响。
3 MiniGUI 中的字符集支持 3.1 字符集操作集
在 MiniGUI 中,每个特定的字符集由对应的字符集操作集来表示。字符集操作集的定义如下(include/gdi.h。前面的数字表示在该文件中的行数,下同):
250 typedef struct _CHARSETOPS 251 { 252 int nr_chars; // 该字符集中字符的个数 253 int bytes_per_char; // 每个字符的平均字节数 254 int bytes_maxlen_char; // 字符的最大字节数 255 const char* name; // 字符集名称 256 char def_char [MAX_LEN_MCHAR]; // 默认字符 257 258 int (*len_first_char) (const unsigned char* mstr, int mstrlen); 259 int (*char_offset) (const unsigned char* mchar); 260 261 int (*nr_chars_in_str) (const unsigned char* mstr, int mstrlen); 262 263 int (*is_this_charset) (const unsigned char* charset); 264 265 int (*len_first_substr) (const unsigned char* mstr, int mstrlen); 266 const unsigned char* (*get_next_word) (const unsigned char* mstr, 267 int strlen, WORDINFO* word_info); 268 269 int (*pos_first_char) (const unsigned char* mstr, int mstrlen); 270 271 #ifndef _LITE_VERSION 272 unsigned short (*conv_to_uc16) (const unsigned char* mchar, int len); 273 #endif /* !LITE_VERSION */ 274 } CHARSETOPS; |
其中,前几个字段(nr_chars、bytes_per_char、bytes_maxlen_char、name、def_char 等)表示了该字符集的一些基本信息,具体含义参见注释。这里需要对 bytes_maxlen_char 和 def_chat 作进一步解释:
在上述字符集的操作集定义中,后几个字段定义为函数指针,它们均由逻辑字体接口用来进行文本分析:
在 src/font/charset.c 中,定义了系统支持的所有字符集操作集,并由函数 GetCharsetOps 返回某个字符集名称对应的字符集操作集(src/font/charset.c):
716 static CHARSETOPS* Charsets [] = 717 { 718 &CharsetOps_iso8859_1, 719 &CharsetOps_iso8859_5, 720 #ifdef _GB_SUPPORT 721 &CharsetOps_gb2312, 722 #endif 723 #ifdef _BIG5_SUPPORT 724 &CharsetOps_big5, 725 #endif 726 #ifdef _EUCKR_SUPPORT 727 &CharsetOps_euckr, 728 #endif 729 #ifdef _UJIS_SUPPORT 730 &CharsetOps_ujis 731 #endif 732 }; 733 734 #define NR_CHARSETS (sizeof(Charsets)/sizeof(CHARSETOPS*)) 735 736 CHARSETOPS* GetCharsetOps (const char* charset_name) 737 { 738 int i; 739 740 for (i = 0; i < NR_CHARSETS; i++) { 741 if ((*Charsets [i]->is_this_charset) (charset_name) == 0) 742 return Charsets [i]; 743 } 744 745 return NULL; 746 } 747 |
如果我们需要定义一种新的字符集支持时,只需在该文件中添加相应的操作集函数以及对应的操作集结构定义即可,比如,对 EUCKR 字符集的支持定义如下(src/font/charset.c):
468 #ifdef _EUCKR_SUPPORT 469 /************************* EUCKR Specific Operations ************************/ 470 static int euckr_len_first_char (const unsigned char* mstr, int len) 471 { 472 unsigned char ch1; 473 unsigned char ch2; 474 475 if (len < 2) return 0; 476 477 ch1 = mstr [0]; 478 if (ch1 == '\0') 479 return 0; 480 481 ch2 = mstr [1]; 482 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE) 483 return 2; 484 485 return 0; 486 } 487 488 static int euckr_char_offset (const unsigned char* mchar) 489 { 490 if(mchar [0] > 0xAD) 491 return ((mchar [0] - 0xA4) * 94 + mchar [1] - 0xA1 - 0x8E); 492 else 493 return ((mchar [0] - 0xA1) * 94 + mchar [1] - 0xA1 - 0x8E); 494 } 495 496 static int euckr_is_this_charset (const unsigned char* charset) 497 { 498 int i; 499 char name [LEN_FONT_NAME + 1]; 500 501 for (i = 0; i < LEN_FONT_NAME + 1; i++) { 502 if (charset [i] == '\0') 503 break; 504 name [i] = toupper (charset [i]); 505 } 506 name [i] = '\0'; 507 508 if (strstr (name, "EUCKR") ) 509 return 0; 510 511 return 1; 512 } 513 514 static int euckr_len_first_substr (const unsigned char* mstr, int mstrlen) 515 { 516 unsigned char ch1; 517 unsigned char ch2; 518 int i, left; 519 int sub_len = 0; 520 521 left = mstrlen; 522 for (i = 0; i < mstrlen; i += 2) { 523 if (left < 2) return sub_len; 524 525 ch1 = mstr [i]; 526 if (ch1 == '\0') return sub_len; 527 528 ch2 = mstr [i + 1]; 529 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE) 530 sub_len += 2; 531 else 532 return sub_len; 533 534 left -= 2; 535 } 536 537 return sub_len; 538 } 539 540 static int euckr_pos_first_char (const unsigned char* mstr, int mstrlen) 541 { 542 unsigned char ch1; 543 unsigned char ch2; 544 int i, left; 545 546 i = 0; 547 left = mstrlen; 548 while (left) { 549 if (left < 2) return -1; 550 551 ch1 = mstr [i]; 552 if (ch1 == '\0') return -1; 553 554 ch2 = mstr [i + 1]; 555 if (ch1 >= 0xA1 && ch1 <= 0xFE && ch2 >= 0xA1 && ch2 <= 0xFE) 556 return i; 557 558 i += 1; 559 left -= 1; 560 } 561 562 return -1; 563 } 564 565 #ifndef _LITE_VERSION 566 static unsigned short euckr_conv_to_uc16 (const unsigned char* mchar, int len) 567 { 568 return '?'; 569 } 570 #endif 571 572 static CHARSETOPS CharsetOps_euckr = { 573 8836, 574 2, 575 2, 576 FONT_CHARSET_EUCKR, 577 {'\xA1', '\xA1'}, 578 euckr_len_first_char, 579 euckr_char_offset, 580 db_nr_chars_in_str, 581 euckr_is_this_charset, 582 euckr_len_first_substr, 583 db_get_next_word, 584 euckr_pos_first_char, 585 #ifndef _LITE_VERSION 586 euckr_conv_to_uc16 587 #endif 588 }; 589 /************************* End of EUCKR *************************************/ 590 #endif /* _EUCKR_SUPPORT */ |
4 MiniGUI 中的字体支持
4.1 设备字体在 MiniGUI 中,设备字体定义如下(include/gdi.h):
319 struct _DEVFONT 320 { 321 char name [LEN_DEVFONT_NAME + 1]; 322 DWORD style; 323 FONTOPS* font_ops; 324 CHARSETOPS* charset_ops; 325 struct _DEVFONT* sbc_next; 326 struct _DEVFONT* mbc_next; 327 void* data; 328 }; |
其中各字段说明如下:
<type>-<name>-<style>-<width>-<height>-<charset1[,charset2]> |
在 MiniGUI 启动时,将根据 MiniGUI.cfg 文件中的定义建立两个设备字体链表,分别为单字节设备字体链和多字节设备字体链。这两个链表将由 CreateLogFont 使用,通过查找和匹配,建立对应的逻辑字体。
4.2 逻辑字体逻辑字体的定义如下(include/gdi.h):
228 typedef struct _LOGFONT { 229 char type [LEN_FONT_NAME + 1]; 230 char family [LEN_FONT_NAME + 1]; 231 char charset [LEN_FONT_NAME + 1]; 232 DWORD style; 233 int size; 234 int rotation; 235 DEVFONT* sbc_devfont; 236 DEVFONT* mbc_devfont; 237 } LOGFONT; 238 typedef LOGFONT* PLOGFONT; |
显然,每个逻辑字体由最匹配该字体要求(大小、字符集、样式等)的两个设备字体(sbc_devfont和 mbc_devfong)组成,分别用来处理多字节字符串中的单字节字符和多字节字符。其中单字节设备字体是必不可少的。
逻辑字体的匹配算法可参见 src/gdi/logfont.c 和src/font/devfont.c 文件。限于篇幅,不再赘述。
4.3 设备字体操作集和字符集操作集一样,MiniGUI 中的设备字体操作集针对每种设备字体类型而定义,包括对这种设备字体的各种操作函数(include/gdi.h):
276 typedef struct _FONTOPS 277 { 278 int (*get_char_width) (LOGFONT* logfont, DEVFONT* devfont, 279 const unsigned char* mchar, int len); 280 int (*get_str_width) (LOGFONT* logfont, DEVFONT* devfont, 281 const unsigned char* mstr, int n, int cExtra); 282 int (*get_ave_width) (LOGFONT* logfont, DEVFONT* devfont); 283 int (*get_max_width) (LOGFONT* logfont, DEVFONT* devfont); 284 int (*get_font_height) (LOGFONT* logfont, DEVFONT* devfont); 285 int (*get_font_size) (LOGFONT* logfont, DEVFONT* devfont, int expect); 286 int (*get_font_ascent) (LOGFONT* logfont, DEVFONT* devfont); 287 int (*get_font_descent) (LOGFONT* logfont, DEVFONT* devfont); 288 289 /* TODO */ 290 // int (*get_font_ABC) (LOGFONT* logfont); 291 292 size_t (*char_bitmap_size) (LOGFONT* logfont, DEVFONT* devfont, 293 const unsigned char* mchar, int len); 294 size_t (*max_bitmap_size) (LOGFONT* logfont, DEVFONT* devfont); 295 const void* (*get_char_bitmap) (LOGFONT* logfont, DEVFONT* devfont, 296 const unsigned char* mchar, int len); 297 298 const void* (*get_char_pixmap) (LOGFONT* logfont, DEVFONT* devfont, 299 const unsigned char* mchar, int len, int* pitch); 300 /* Can be NULL */ 301 302 void (*start_str_output) (LOGFONT* logfont, DEVFONT* devfont); 303 /* Can be NULL */ 304 int (*get_char_bbox) (LOGFONT* logfont, DEVFONT* devfont, 305 const unsigned char* mchar, int len, 306 int* px, int* py, int* pwidth, int* pheight); 307 /* Can be NULL */ 308 void (*get_char_advance) (LOGFONT* logfont, DEVFONT* devfont, 309 int* px, int* py); 310 /* Can be NULL */ 311 312 DEVFONT* (*new_instance) (LOGFONT* logfont, DEVFONT* devfont, 313 BOOL need_sbc_font); 314 /* Can be NULL */ 315 void (*delete_instance) (DEVFONT* devfont); 316 /* Can be NULL */ 317 } FONTOPS; |
比如,get_char_width 用来获得某个字符的宽度,而 get_char_bitmap 用来获得某个字符的位图信息等等。
在 src/font/rawbitmap.c 和 src/font/varbitmap.c 文件中分别定义了对 RBF 和 VBF 两种字体的操作函数,比如对变宽光栅字体来讲(VBF),其 get_char_bitmap 定义如下(src/font/rawbitmap.c):
155 static const void* get_char_bitmap (LOGFONT* logfont, DEVFONT* devfont, 156 const unsigned char* mchar, int len) 157 { 158 int offset; 159 unsigned char eff_char = *mchar; 160 VBFINFO* vbf_info = VARFONT_INFO_P (devfont); 161 162 if (*mchar < vbf_info->first_char || *mchar > vbf_info->last_char) 163 eff_char = vbf_info->def_char; 164 165 if (vbf_info->offset == NULL) 166 offset = (((size_t)vbf_info->max_width + 7) >> 3) * vbf_info->height 167 * (eff_char - vbf_info->first_char); 168 else 169 offset = vbf_info->offset [eff_char - vbf_info->first_char]; 170 171 return vbf_info->bits + offset; 172 } |
其中,VARFONT_INFO_P 是一个宏,用来从设备字体的 data 字段中获得 VBFINFO 结构的指针。有了这个指针之后,该函数计算字符位图的偏移量最后返回字符的位图。
4.4 新设备字体的实现举例这里以 Adobe Type1 字体的实现为例,说明如何在 MiniGUI 中实现一种新的设备字体。MiniGUI 借用了 T1Lib 函数库实现了对 Type1 字体的支持。
4.4.1 Type1 字体简介
Type1 矢量字体1格式由 Adobe 公司设计,并被该公司的ps标准支持。因此,它在Linux下也被支持得很好。它被 X和 ghostscript支持。一个典型的Type1字体包括一个afm(adobe font metric) 度量文件,一个外形文件,通常是一个pfb ( printer font binary) 或者 pfa (printer font ascii) 文件,外形文件包括所有的轮廓,而度量文件包含了所有的度量。比如紧排,连字等信息。
4.4.2 T1Lib 简介
T1Lib 是用 C 语言实现的一个库,它可以从 Adobe Type 1 字体生成位图。它可以使用X11R5 或者更新版本提供的光栅化工具的很多功能,但避免了其已知的缺点。当然,T1Lib完全可以在没有 X11 的环境下工作。T1Lib 可以被编译成静态或者动态库,从而可以方便地连接。
这里是T1Lib 的一些特性:
4.4.3 Adobe Type1 字体支持的实现
在 MiniGUI 设备字体定义中,有一个 data 字段可用来保存设备字体相关的数据结构。对 Type1 字体来讲,我们使用 TYPE1INFO和TYPE1INSTANCEINFO两个数据结构来存储这种设备字体的类信息和实例信息。
1) TYPE1INFO和TYPE1INSTANCEINFO 结构
这两个结构的定义如下(src/font/type1.h):
22 typedef struct tagTYPE1GLYPHINFO { 23 int font_id; 24 //BBox font_bbox; 25 //int ave_width; 26 BOOL valid; 27 } TYPE1INFO, *PTYPE1INFO; 28 29 typedef struct tagTYPE1INSTANCEINFO { 30 PTYPE1INFO type1_info; 31 int rotation;/*in tenthdegrees*/ 32 T1_TMATRIX * pmatrix; 33 int size; 34 int font_height; 35 int font_ascent; 36 int font_descent; 37 38 int max_width; 39 int ave_width; 40 41 double csUnit2Pixel; 42 /* 43 * last char or string's info 44 * T1_SetChar, T1_SetString, T1_AASetSting, T1_AASetString all return a static 45 * glyph pointer, we save the related infomation here for later use. 46 * */ 47 char last_bitmap_char; 48 char last_pixmap_char; 49 char * last_bitmap_str; 50 char * last_pixmap_str; 51 int last_ascent; 52 int last_descent; 53 int last_leftSideBearing; 54 int last_rightSideBearing; 55 int last_advanceX; 56 int last_advanceY; 57 unsigned long last_bpp; 58 char * last_bits; 59 60 } TYPE1INSTANCEINFO, *PTYPE1INSTANCEINFO; 61 |
如前面所说,TYPE1INFO和TYPE1INSTANCEINFO数据结构来存储设备字符的类信息和实例信息。初始华时,其实只是注册一个模板,此时利用TYPE1INFO记住其在 T1lib中的Font ID,这里valid用来说明该设备字体是否初始化完毕。
当用户创建一逻辑字体时,如果用户选择的是Type1字体的某一种,就会调用 font_ops 的函数new_instance,该函数根据存在于 DevFont 的data的 TYPE1INFO 结构中的 id,以及用户提供的相关参数,构造一个TYPE1INSTANCEINFO类型的变量,并放入新的设备字体的私有数据data中。从而每个字体实例可以有自己的各种属性。如旋转度。
前面各个字段的意义可以根据名字推测出来,从csUnix2Pixel 开始则是为了实现的方便和高效而自己定义的一些变量,后面解释函数实现时将会说明。last*系列函数主要起缓冲的作用。
2) InitType1Fonts 和 TermType1Fonts 函数
这两个函数负责整个 Type 1 字体的初始化和终结。
InitType1Fonts 的主要任务是:初始化T1lib,根据配置文件提供的信息,将各种字体注册到T1lib,并为每一个字体生成一个 DevFont 结构,注册到系统中去。该结构中包括的 font_ops,是上层对Type 1字体各种操作的窗口。
其实主要的处理功能在 T1lib 中,每次程序向 T1lib 注册一个字体,T1lib会返回一个 Font ID,以后利用该ID 向T1lib请求关于对应字体的某些服务。
TermType1Fonts 则是注销 Type1 字体,关闭T1lib。
InitType1Fonts 注册向系统注册了用来处理 Abode Type1 字体的字体操作集,定义如下(src/font/type1.c):
780 static FONTOPS type1_font_ops = { 781 get_char_width, 782 get_str_width, 783 get_ave_width, 784 get_max_width, 785 get_font_height, 786 get_font_size, 787 get_font_ascent, 788 get_font_descent, 789 char_bitmap_size, 790 max_bitmap_size, 791 get_char_bitmap, 792 get_char_pixmap, 793 start_str_output, 794 get_char_bbox, 795 get_char_advance, 796 new_instance, 797 delete_instance 798}; |
先说明一些基本概念。
这样,get_char_width、get_str_width、get_ave_width、get_max_width、 get_font_height、get_font_size、get_font_ascent、get_font_descent、 char_bitmap_size、max_bitmap_size、get_char_advance 等函数的功能就很明显了,它们其实就是取出字体的一些度量(Metrics)。其实,这些信息都是从T1lib内部取得,需要注意的是T1lib 内部使用 PS 单位,而MiniGUI使用的单位是pixel, 需要转换。以下以 get_char_bitmap 和 get_char_pixmap 等函数为例说明。
3) get_char_bitmap 和 get_char_pixmap
这两个函数是主要的光栅化函数。它们首先判断一下需要光栅化的字符是否刚刚被光栅化过,如果是,直接返回缓冲里的值。
前面讲过,T1Lib 支持5灰度的低分辨率和17灰度的高分辨率的反走样。这里的get_char_bitmap返回普通的光栅化位图,而get_char_pixmap返回经过反走样后的像素位图。如果字体在初始化时调用
T1_AASetLevel (T1_AA_LOW) |
则这里使用5灰度像素,如果初始化时是调用:
T1_AASetLevel (T1_AA_HIGH) |
则这里使用17灰度像素。
这里使用的反走样其实很简单,就是先将字体放大,然后再取样缩小。低精度是放大四倍(2*2),高精度则是放大16倍(4*4),灰度值则有n+1种。
当然,为了提高性能,每次光栅化的结果都要被放到缓冲里,下次如果要光栅化相同的字符,并且方式相同,则可以大大地提高效率。
4) start_str_output
开始字符串输出时调用该函数。完成一些初始化工作。
5) get_char_bbox
给出当前原点值(*px,*py),调用该函数要求得到在字符被画出后的原点值(新的*px,*py),以及当前字符的宽度和高度。
6) new_instance 和 delete_instance
当用户创建一个新的逻辑字体时调用new_instance ,当用户删除一个逻辑字体时会调用delete_instance。
new_instance 根据传给它的一些参数(size,rotation,font_id等)初始化一个TYPE1INSTANCEINFO类型的变量,并将其与新的设备字体关联,将该设备字体返回。以后上层就通过该设备字体得到字体实例相关的信息。
delete_instance 则用来删除相关的数据结构。
5 小结
面向对象技术在软件设计当中占有非常重要的地位,但面向对象并不是 C++ 等语言的专利。实际上,在诸如操作系统等系统软件当中,面向对象技术的使用是非常广泛的。利用 C 语言实现面向对象技术,不仅结构清晰,而且在执行效率等方面也有 C++ 等语言无法相比的优势。从本文描述的字体和字符集的实现当中我们可以看到,采用面向对象技术,将大大提高系统的灵活性和可扩展性。
MiniGUI 作为一个面向实时嵌入式系统的图形用户界面支持系统,对其执行效率、可定制、可扩展等方面有非常高的要求。为了提高系统的灵活性和可扩展性,我们在一些关键模块当中使用了面向对象的技术。实践表明,面向对象的技术在 MiniGUI 中的运用是成功的。