PXF0255-WinCE屏幕绘图

Shanghai Passiontech Tech, 021-51870017, sales@51Lm.cn

     
     
     

WinCE屏幕绘图


型号: PXF0255


简介:/* Generator: eWebEditor */p.MsoNormal, li.MsoNormal, div.MsoNormal {margin:0cm;margin-bottom:.0001pt;text-align:justify;text-justify:inter-ideograph;font-size:10.5pt;font-family:"Times New Roman";}div.Section1 {page:Section1;}/* Generator: eWebEditor */p.MsoNormal, li.MsoNormal, div.MsoNormal {margin:0cm;margin-bottom:.0001pt;text-align:justify;text-justify:inter-ideograph;font-size:10.5pt;font-f...
品牌 葩星 葩星
产地中国
型号PXF0255
折扣其他电询

2 屏幕绘图
概述
在第
1章,示例程序HelloCE完成一项工作:在屏幕上显示一行文字。显示这行文字只需要调用一次DrawText即可,因为Windows CE代为处理了很多细节,例如字体、字体颜色、文本行在屏幕上的位置等等。借助图形用户接口的力量,应用程序不只能在屏幕上输出本文行,还能做更多的事情。应用程序可以绘制出非常精细的显示外观。

纵观微软Windows操作系统,用于绘制屏幕的函数数量发生了巨大的扩展。Windows每个后续的版本里,都增加了许多函数以扩展程序员可以使用的工具集。虽然新函数增加了,但旧函数依然被保留,这样即使有旧函数被新函数取代,旧程序依然可以继续运行在新版本的Windows上。这种函数不断堆积,旧函数被保留以向后兼容的策略,在最初的Windows CE版本里却被废弃了。因为需要制作更小版本的Windows,CE团队苦览Win32 API,并只复制适合Windwos CE目标市场的应用程序绝对需要的API。

这种精简对Win32 API影响最大的领域之一就是图形函数。到不是您会缺乏用于工作的函数,只是在Win32 API的冗余度方面,对图形函数做了教大的精简。程序员面临的新挑战之一就是不同的Windows CE平台支持略微不同的API集合。Windows CE图形功能与桌面系统不同之处,其中之一就是Windows CE不支持不同的映射模式,而这在其他Windows系统里是支持的。Windows CE设备环境始终设置为MM_TEXT映射模式。坐标转化在Windows CE下也不支持。虽然这些特性在一些类型的应用中很有用,但在小型便携式设备的Windows CE环境里,这些需求并不突出。所以当你阅读本章里使用的函数和技术时,请记住其中一些可能不能在所有平台上被支持。通过GetDeviceCaps函数,程序可以判断系统支持什么函数。GetDeviceCaps返回当前图形设备的实际能力。贯穿本章始末, 当判定在目标设备上什么函数被支持时,我会谈到GetDeviceCaps函数的。

像书中第一部分里其它章节一样,本章回顾Windows CE所支持的绘画功能。需要记住的最重要的事情之一是虽然Windows CE不支持全部Win32 图形API,但它的快速发展使它可以支持一些Win32里最新的函数--其中一些非常新,可能您对它们都不熟悉。本章将为您展示您可以使用的函数以及如何在这个有一些函数不被Windows CE支持的领域里工作。

绘图基础
综观历史,
Windows被细分成三个主要部分:核心层,处理进程和管理内存;用户层,处理窗口接口和控件;图形设备接口(GDI)负责底层绘制。在Windows CE里,用户层和GDI层合成为图形窗口及事件处理器,即GWE。你可能有时会听Windows CE程序员谈起GWE。GWE并不是什么新事务,只是标准Windows部件的不同包装而已。在本书里,我通常将GWE的图形部分依然称为GDI,以保持和标准Windows编程术语的一致性。

不论你是为Windows CE、2000、还是XP编写程序,需要做的不仅仅是处理WM_PAINT消息这么简单。理解什么时候和为什么WM_PAINT消息要被送到窗口是很有益处的。

有效和无效区域
某些情况下,窗口的一部分暴露给用户,这些部分在
Windows里称为区域,这些区域被标记为无效。当应用程序消息队列里没有其它消息在等待处理并且应用程序窗口包含有无效区域的时候,Windows会给窗口发送WM_PAINT消息。正如第一章里提到的,处理WM_PAINT消息的绘制操作是包含在BeginPaint和EndPaint调用之间的。BeginPaint实际上执行很多动作。首先将无效区域标记为有效,接下来计算需要裁减的区域。裁减区是限制绘图动作的区域。BeginPaint接下来发送WM_ERASEBACKGROUND消息,如果需要,还会重绘背景,如果用于指示文本输入的光标可见,它还会隐藏光标。最后BeginPaint获得显示设备环境变量的句柄,该句柄可以用于应用程序中。EndPaint函数则释放设备环境,如果必要,还会重新显示光标。如果WM_PAINT处理过程没有更多的操作要执行,您也必须至少调用BeginPaint 和EndPaint ,以便将无效区域标记为有效。

作为替代,您可以调用ValidateRect来强制使矩形有效。但这种情况下没有绘制动作发生,因为应用程序在窗口上绘制任何东西之前,必须有设备环境句柄。

应用程序经常需要强制重画它的窗口。应用程序决不应该邮递或发送(post or send)WM_PAINT消息给自身或其它窗口。您应该使用以下函数:

BOOL InvalidateRect (HWND hWnd, const RECT *lpRect, BOOL bErase);
请注意
InvalidateRect并不要求窗口设备环境句柄,只要窗口句柄自身。lpRect表示窗口中需要无效的区域。该参数为NULL则表示整个窗口无效。bErase用来指出在调用BeginPaint的时候是否重画窗口背景。请注意不像Windows的其它版本,Windows CE要求hWnd必须是一个有效的窗口句柄。

设备环境
设备环境经常被简称为
DC,它被Windows用于管理对显示器和打印机的访问,当然在本章我将只讨论显示器。除非特别说明,下面的讨论适合所有Windows,而并不具体针对Windows CE。

Windows应用程序从不直接写屏幕。相反,它们为适当的窗口请求一个显示设备环境句柄,之后用该句柄在设备环境里绘制。接下来Windows作为中间人将像素从DC操纵到屏幕上。

只应该在WM_PAINT消息里调用BeginPaint,它为窗口返回一个显示DC句柄。通常应用程序在处理WM_PAINT消息时在屏幕上绘制东西。Windows将绘制作为一个低优先级的任务,这样做是适当的,因为将绘制作为高优先级会导致为每个小的显示改变都产生一个绘图消息,这将使绘图消息泛滥。而通过处理所有正在等待的消息,允许应用程序先完成所有未完成的事务,这样使所有无效区域能够被有效的一次性绘制完成。用户并不会注意到由WM_PAINT消息低优先级所带来的微小延迟。

当然,有些时候是需要立即绘制的。字处理器就是一个例子,当键盘被按下后,字处理器需要立即显示对应的字符。为了在WM_PAINT消息以外的时刻进行绘制,可以用GetDC函数来获得DC句柄。GetDC原型如下:HDC GetDC (HWND hWnd);
GetDC返回窗口客户区
DC句柄。接下来可以在窗口客户区的任何地方进行绘制,因为这个过程不像WM_PAINT消息的处理过程,没有裁减区域来限制您只能在一个无效区域里绘制。

Windows CE支持另一个获得DC的函数,该函数如下:
HDC GetDCEx (HWND hWnd, HRGN hrgnClip, DWORD flags);
GetDCEx允许您对返回的设备环境有更多的控制。新参数
hrgnClip使您可以定义用来限制绘制区域的裁减区域。flags指出当您在DC上绘制的时候,DC如何反应。注意Windows CE不支持下面的标志:DCX_PARENTCLIP, DCX_NORESETATTRS, DCX_LOCKWINDOWUPDATE, 和DCX_VALIDATE。

当绘制完成后,必须调用ReleaseDC来释放设备环境。ReleaseDC原型如下:

int ReleaseDC (HWND hWnd, HDC hDC);

GetDC用于在客户区内绘制,有时应用程序需要访问窗口非客户区,比如标题栏。为了获得整个窗口的DC,使用以下函数:HDC GetWindowDC (HWND hWnd);

如前所述,当绘制完成后,要调用ReleaseDC。

Windows CE下的DC函数同XP下的设备环境函数是一样的。这是可以预料到的,因为DC是Windows 绘图体系里的核心。对这个领域的API进行改变将导致Windows CE程序与它的桌面程序严重的不兼容。

输出文本

在第一章里,例子程序HelloCE调用DrawText函数显示了一行文本。代码如下:
DrawText (hdc, TEXT ("Hello Windows CE!"), -1, &rect,
          DT_CENTER | DT_VCENTER | DT_SINGLELINE);
DrawText是一个相当高级的函数,允许由程序显示文本,而由
Windows处理大部分细节。DrawText的头几个参数几乎是不言而喻,很直观。当前正在使用的设备环境句柄被传入,同时传入的还有被TEXT宏包围的用来显示的文本,声明成Unicode字符串是为了符合Windows CE的需要。

第三个参数是要输出的字符个数,当为-1,则表示传入的是以NULL为终止符的字符串并由Windows计算其长度。

第四个参数是一个指向rect结构的指针,为文本规定了格式化矩形。DrawText用该矩形作为文本格式化输出的基础。文本如何格式化取决于函数的最后一个参数--格式化标志位。这些标志位指定文本如何被放在格式化的矩形里。在指定了DT_CALCRECT标志位的情况下,由DrawText来计算需要输出的文本的尺寸。DrawText甚至用自动计算出的行中断(line break)来将文本格式化多行。在HelloCE的情况里,标志位规定文本应该水平居中(DT_CENTER)和垂直居中(DT_VCENTER)。DT_VCENTER标志只在单行文本的情况下有效,所以最后一个标志位DT_SINGLELINE规定如果矩形宽度不足以显示整个字符串时,文本不应该折成多行。

画文本的另一个方法就是使用下面的函数:
BOOL ExtTextOut (HDC hdc, int X, int Y, UINT fuOptions,
                 const RECT *lprc, LPCTSTR lpString,
                 UINT cbCount, const int *lpDx);
ExtTextOut函数同
DrawText相比有一些优势。首先,ExtTextOut画单行文本往往更快一些。其次,文本并不在矩形里格式化,而是以传入的x、y坐标作为文本绘制的起始坐标。通常,该点是矩形的左上角坐标,但它可以随着DC中文本对齐方式来改变。传入的rect参数用做剪切矩形,如果背景模式是opaque,则背景颜色的区域被绘制。该矩形参数可以是NULL,表示不需要剪切或者opaquing(不透明化)。接下来的两个参数是文本及字符个数。最后一个参数允许应用程序指定相邻字符之间的水平距离。

Windows CE与其它版本的Windows不同的地方在于只有这两个文本绘制函数用于显示文本。通过使用DrawText或ExTextOut,您可以模拟TextOut 和 TabbedTextOut等其它版本Windows里文本函数所能做的大部分操作。这是Windows CE同Windows早期版本不同的地方之一,通过牺牲向后兼容性来获得一个更小的操作系统。

设备环境属性
关于HelloCe中用的DrawText,我还没有提到的是当显示文本时程序对DC配置做的大量假设。在Windows设备环境上绘制需要很多参数,例如前景色和背景色、文字如何绘制在背景上以及文字的字体等。每次绘制时并不是指定所有这些参数,设备环境保持当前设置,也就是属性,每次在绘制设备变量上绘制时都使用这些属性。

前景色和背景色
文本属性最显而易见的是前景色和背景色。
SetTextColor 和GetTextColor允许程序设置和获取当前颜色。这两个函数在Windwos CE设备支持的灰度级屏幕和彩色屏幕上都可以运行的很好。

使用前面提到的GetDeviceCaps函数,可以确定设备支持多少颜色。该函数原型如下:
int GetDeviceCaps (HDC hdc, int nIndex);
您需要被查询的
DC的句柄,因为不同的DC有不同的内容。例如,打印机DC就不同于显示器DC。第二个参数指出要查询的内容。在返回设备上可用颜色时,当设备支持256色或更少颜色时,NUMCOLORS返回支持的颜色数量。当超过256时,NUMCOLORS对应的返回值为-1,用BITSPIXEL可以返回颜色,此时返回的是每个像素所用的位(bit)数。通过将BITSPIXEL的返回值左移动一位,可以将这个值转换为颜色数,代码示例如下:
nNumColors = GetDeviceCaps (hdc, NUMCOLORS);
if (nNumColors == -1)
    nNumColors = 1 << GetDeviceCaps (hdc, BITSPIXEL);

文字对齐方式
当用ExtTextOut显示文本时,系统用DC的文字对齐方式来决定在哪里绘制文本。通过SetTextAlign 函数,文字可以水平和垂直对齐。

SetTextAlign 如下:
UINT WINAPI SetTextAlign (HDC hdc, INT fmode);
传给
fmode的对齐标方式标志位如下所示:
TA_LEFT 文本左边缘与控制点对齐(控制点解释见后)

TA_RIGHT文本右边缘与控制点对齐

TA_TOP文本顶部与控制点对齐

TA_CENTER文本以控制点为中心水平居中

TA_BOTTOM文本底部边缘与控制点对齐

TA_BASELINE文本基线与控制点对齐

TA_NOUPDATECP在调用
ExtTextOut后,DC的当前点并不更新
TA_UPDATECP在调用
ExtTextOut后,DC的当前点更新
上面描述中提到的控制点指的是传给
ExtTextOut的x、y坐标。对SetTextAlign的每次调用,垂直对齐标志和水平对齐标志可以组合在一起。

因为很难形象化的描述这些标志位的效果,图2-1展示了每个标志的效果。在图中,X表示控制点。

2-1(略):当前绘制点和文本对齐标志之间的关系

绘制模式
影响文本输出的另一个属性是背景模式。当字母在设备环境上绘制时,系统使用前景色绘制字母自身。字母之间的空间则是另一回事。如果背景模式设置为不透明,则使用当前背景色来绘制该空间。但如果背景模式设置为透明,字母之间的空间保持文字绘制之前的状态。虽然这可能不像是一个大的区别,但设想一个使用图画或者图片来填充的窗口背景。如果文字在图片顶部输出,背景模式设置为不透明,文字周围的区域会被填充,背景色会填充在图片上。如果背景模式是透明,文字在图片上看起来就像文字原本就在图片上一样,图片会在文本的字母之间显示出来。

TextDemo示例程序
TextDemo程序,演示了文本颜色、背景色和背景模式之间的关系。程序如列表
2-1所示:
列表
2-1:TextDemo程序
TextDemo.h
//================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
  
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
  
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT {                             // Structure associates
    UINT Code;                                  // messages
                                                // with a function.
    LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD {                              // Structure associates
    UINT Code;                                  // menu IDs with a
    LRESULT (*Fxn)(HWND, WORD, HWND, WORD);     // function.
};
  
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
  
// Window procedures
LRESULT CALLBACK MainWndProc (HWND, UINT, WPARAM, LPARAM);
  
// Message handlers
LRESULT DoPaintMain (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyMain (HWND, UINT, WPARAM, LPARAM);

TextDemo.cpp
//================================================================
// TextDemo - Text output demo
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h>                 // For all that Windows stuff
#include "TextDemo.h"                // Program-specific stuff
  
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("TextDemo");
HINSTANCE hInst;                     // Program instance handle
  
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
    WM_PAINT, DoPaintMain,
    WM_DESTROY, DoDestroyMain,
};
//======================================================================
// Program Entry Point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPWSTR lpCmdLine, int nCmdShow) {
    MSG msg;
    int rc = 0;
    HWND hwndMain;
  
    // Initialize this instance.
    hwndMain = InitInstance (hInstance, lpCmdLine, nCmdShow);
    if (hwndMain == 0)
        return 0x10;
  
     // Application message loop
    while (GetMessage (&msg, NULL, 0, 0)) {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    // Instance cleanup
    return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow){
    WNDCLASS wc;
    HWND hWnd;
  
    hInst = hInstance;   // Save handle in global variable.
  
#if defined(WIN32_PLATFORM_PSPC)
    // If Pocket PC, allow only one instance of the application.
    hWnd = FindWindow (szAppName, NULL);
    if (hWnd) {
        SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));   
        return 0;
    }
#endif   
    // Register application main window class.
    wc.style = 0;                             // Window style
    wc.lpfnWndProc = MainWndProc;             // Callback function
    wc.cbClsExtra = 0;                        // Extra class data
    wc.cbWndExtra = 0;                        // Extra window data
    wc.hInstance = hInstance;                 // Owner handle
    wc.hIcon = NULL,                          // Application icon
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
    wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
    wc.lpszMenuName =  NULL;                  // Menu name
    wc.lpszClassName = szAppName;             // Window class name
  
    if (RegisterClass (&wc) == 0) return 0;
  
    // Create main window.
    hWnd = CreateWindowEx (WS_EX_NODRAG,      // Ex Style flags
                         szAppName,           // Window class
                         TEXT("TextDemo"),    // Window title
                         // Style flags
                         WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
                         CW_USEDEFAULT,       // x position
                         CW_USEDEFAULT,       // y position
                         CW_USEDEFAULT,       // Initial width
                         CW_USEDEFAULT,       // Initial height
                         NULL,                // Parent
                         NULL,                // Menu, must be null
                         hInstance,           // Application instance
                         NULL);               // Pointer to create
                                              // Parameters
    // Return fail code if window not created.
    if ((!hWnd) || (!IsWindow (hWnd))) return 0;
  
    // Standard show and update calls
    ShowWindow (hWnd, nCmdShow);
    UpdateWindow (hWnd);
    return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
    return nDefRC;
}
//======================================================================
// Message handling procedures for MainWindow
//
//----------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
                              LPARAM lParam) {
    INT i;
    //
    // Search message list to see if we need to handle this
    // message.  If in list, call procedure.
    //
    for (i = 0; i < dim(MainMessages); i++) {
        if (wMsg == MainMessages[i].Code)
            return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
    }
    return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
                     LPARAM lParam) {
    PAINTSTRUCT ps;
    RECT rect, rectCli;
    HBRUSH hbrOld;
    HDC hdc;
    INT i, cy;
    DWORD dwColorTable[] = {0x00000000, 0x00808080,
                            0x00cccccc, 0x00ffffff};
  
    GetClientRect (hWnd, &rectCli);
  
    hdc = BeginPaint (hWnd, &ps);
  
    // Get the height and length of the string.
    DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rect,
              DT_CALCRECT | DT_CENTER | DT_SINGLELINE);
  
    cy = rect.bottom - rect.top + 5;
  
    // Draw black rectangle on right half of window.
    hbrOld = (HBRUSH)SelectObject (hdc, GetStockObject (BLACK_BRUSH));
    Rectangle (hdc, rectCli.left + (rectCli.right - rectCli.left) / 2,
               rectCli.top, rectCli.right, rectCli.bottom);
    SelectObject (hdc, hbrOld);
  
    rectCli.bottom = rectCli.top + cy;
    SetBkMode (hdc, TRANSPARENT);
    for (i = 0; i < 4; i++) {
        SetTextColor (hdc, dwColorTable[i]);
        SetBkColor (hdc, dwColorTable[3-i]);
  
        DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rectCli,
                  DT_CENTER | DT_SINGLELINE);
        rectCli.top += cy;
        rectCli.bottom += cy;
    }
  
    SetBkMode (hdc, OPAQUE);
    for (i = 0; i < 4; i++) {
        SetTextColor (hdc, dwColorTable[i]);
        SetBkColor (hdc, dwColorTable[3-i]);
  
        DrawText (hdc, TEXT ("Hello Windows CE"), -1, &rectCli,
                  DT_CENTER | DT_SINGLELINE);
        rectCli.top += cy;
        rectCli.bottom += cy;
    }
    EndPaint (hWnd, &ps);
    return 0;
}
//----------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window.
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
                       LPARAM lParam) {
    PostQuitMessage (0);
    return 0;
}
TextDemo的实质内容在
OnPaintMain函数里。对DrawText的第一次调用并没有在设备环境上画任何东西。相反,使用DT_CALCRECT标志指示Windows将文本串的矩形尺寸存在rect里。该信息用于计算字符串的高度,并存储在cy里。接下来,在窗口的右侧绘制了一个黑色矩形。我将在本章稍后一些讨论如何绘制一个矩形。该矩形用来在文本被书写之前,产生两个不同的背景。函数接下来用不同的前景色、背景色在透明和不透明模式下输出了 同样的字符串。程序结果如图2-2所示:

头四行使用透明模式来绘制。后四行使用不透明模式来绘制。文本颜色从黑到白,每行使用不同的颜色,同时背景色设置为从白到黑。在透明模式下,背景色是无关紧要的,因为并不使用该背景色。但是在不通明模式下,背景色欣然出现在每行里。
2-2(略):TextDemo展示了文本色、背景色以及背景模式之间的关系。

字体
如果Windows提供的全部灵活性就是设置前景色和背景色,那我们还是回到MS-DOS和字符属性时代的好。有证据表明,Windows同MS-DOS最显著的变化就是Windows可改变显示文本字体的能力。所有Windows操作系统都是建立在WYS/WYG-所见即所得-的概念上的,而采用可变字体就是达到这一目的的一个主要手段。

在所有Windows操作系统里都有两种字体类型-光栅型(raster)和TrueType型。光栅型字体存储为位图,即小的像素图像,每个字符都有一个。光栅字体容易存储和使用,但有一个重要问题:不能很好的缩放。就像小图放大时出现锯齿纹一样,光栅字体放大到更大的字体时会出现锯齿纹。

TrueType字体解决了缩放问题。它不是存储成图象,每个TrueType字符存都存储成如何绘制字符的描述信息。作为在屏幕上绘制字符的Windows功能的一部分,字体引擎获得描述信息,并按需要的尺寸在屏幕上绘制字符。一个Windows CE系统支持TrueType或光栅字体,但不会同时支持。幸运的是,对光栅字体和TrueType字体来说,编程接口都是一样的,这减少了Windows 开发者对字体技术的担忧,毕竟字体技术用在所有应用里,也是最让人吃力的技术之一。

Windows CE里的字体函数同Windows其它版本中的字体函数很相近。从创建字体、选择进DC到最后删除字体,让我们看一下字体生存周期中用到的函数吧。如何查询当前字体以及枚举可使用的字体等,都会在下面几节里涉及到。

创建字体
在应用可以使用非默认字体之前,该字体必须被创建并被选进设备环境里。在新字体被选进
DC后,在DC里绘制的任何文本都使用这个新字体。
Windows CE中创建字体的方法如下:
HFONT CreateFontIndirect (const LOGFONT *lplf);
该函数接受一个指向
LOGFONT结构的指针,该结构必须使用您需要的字体描述来填充。
typedef struct tagLOGFONT {
    LONG lfHeight;
    LONG lfWidth;
    LONG lfEscapement;
    LONG lfOrientation;
    LONG lfWeight;
    BYTE lfItalic;
    BYTE lfUnderline;
    BYTE lfStrikeOut;
    BYTE lfCharSet;
    BYTE lfOutPrecision;
    BYTE lfClipPrecision;
    BYTE lfQuality;
    BYTE lfPitchAndFamily;
    TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;

lfHeight 规定字体在设备单位下的高度。如果该域为0,字体管理器返回使用的字体系中默认字体尺寸。对于大多数应用程序来说,您需要创建一个特殊尺寸的字体。下面的公式用于给lfHeight转换磅值:
lfHeight = –1 * (PointSize * GetDeviceCaps (hdc, LOGPIXELSY) / 72);
这里传给
GetDeviceCaps的LOGPIXELSY告诉函数返回垂直方向上每英寸的逻辑像素数。72表示每英寸的磅值(磅是用于排版的计算单位)

lfWidth规定了平均字符宽度。因为字体的高度比宽度更重要,所以大多数程序都把这个值设为0。表示让字体管理器根据字体高度计算出合适的宽度。lfEscapement 和lfOrientation 规定了字符基线同X坐标轴之间以十分之一度为单位的旋转角度。lfWeight规定了从0到1000范围内的字体粗细程度,其中400是标准字体,700是加重字体。接下来的三个域规定了字体是否是斜体、下划线或者删除线。

lpCharSet规定了您选择的字符集。该域对于软件的国际版本是很重要的,因为国际版本一般都要求特定的语言字符集合。lfOutPrecision 规定Windows匹配您请求的字体的精确程度。在众多可以使用的标志当中,OUT_TT_ONLY_PRECIS标志规定创建的字体必须是TrueType字体。

lfClipPrecision规定Windows如何裁剪超出显示区域的字符。

lfQuality 可以设置为以下几种:
DEFAULT_QUALITY   默认系统质量

DRAFT_QUALITY   牺牲质量换取速度

CLEARTYPE_QUALITY   用ClearType技术绘制文本

CLEARTYPE_COMPAT_QUALITY   用ClearType技术绘制文本。对非ClearType字体使用同样的空间。

ClearType是一种为字体提供更清晰的外观的文本显示技术,该技术独立寻址红、绿、蓝LCD,它们在彩色LCD显示器上构成一个像素。根据系统的不同,有的可能不支持ClearType,有的可能对系统里的所有字体都支持。对于支持ClearType但不完全支持的系统来说,使用CLEARTYPE_QUALITY 或CLEARTYPE_COMPAT_QUALITY 可以创建用ClearType绘制的字体。因为ClearType并不改进所有字体的外观,所以您应该测试一下,看ClearType是否对您选择的字体有所改进。

lfPitchAndFamily规定了您选择的字体的字系。当您请求诸如Swiss字系--一种没有衬线的专业字体--时,使用该域很方便。再例如选择Roman字系--一种带衬线的专业字体--时,也很方便,但您要注意不要针对特定的字体。您也可以用该域来规定均衡或等宽字体,并让Windows来决定使用哪种字体来匹配传给LOGFONT结构的特定特性。最后,lfFaceName域用来指明具体字体的字体名称。当用一个填充后的LOGFONT结构调用CreateFontIndirect时,Windows根据提供的特性,创建一个最匹配的逻辑字体。为了使用该字体,需要做的最后一步是选择该字体到一个设备环境里。

选择字体到设备环境
SelectObject 函数将字体选进DC里,函数如下所示:
HGDIOBJ SelectObject (HDC hdc, HGDIOBJ hgdiobj);
该函数不仅仅用于设置默认字体,还用在很多地方;正如您很快就会看到的,可以用该函数来选择其它
GDI对象。该函数返回先前被选择的对象(在本例中返回的是先前选择的字体),应该保存该返回值以便当您用完新的字体后可以把先前的选择回DC里。代码行如下所示:
hOldFont = (HFONT)SelectObject (hdc, hFont);

当逻辑字体被选择时,系统从可使用的字体中选择最匹配的逻辑字体。对没有TrueType字体的设备,匹配的字体同指定的参数相比有一定数量的差异。因此,不要想当然的认为您请求了一个特殊字体,返回的就是准确匹配的字体。例如,您要求的字体高度可能和选进设备环境的字体高度是不一样的。

查询字体特性
为了确定选进设备环境的字体的特性,调用
GetTextMetrics 函数来返回字体特性,函数原型如下:
BOOL GetTextMetrics (HDC hdc, LPTEXTMETRIC lptm);
TEXTMETRIC结构带有返回信息,该结构定义如下:

typedef struct tagTEXTMETRIC {
    LONG tmHeight;
    LONG tmAscent;
    LONG tmDescent;
    LONG tmInternalLeading;
    LONG tmExternalLeading;
    LONG tmAveCharWidth;
    LONG tmMaxCharWidth;
    LONG tmWeight;
    LONG tmOverhang;
    LONG tmDigitizedAspectX;
    LONG tmDigitizedAspectY;
    char tmFirstChar;
    char tmLastChar;
    char tmDefaultChar;
    char tmBreakChar;
    BYTE tmItalic;
    BYTE tmUnderlined;
    BYTE tmStruckOut;
    BYTE tmPitchAndFamily;
    BYTE tmCharSet;
} TEXTMETRIC;

TEXTMETRIC结构包含了LOGFONT结构中的很多域,但此时TEXTMETRIC的值是选进设备变量的字体的特性。图2-3显示了一些域同实际字符之间的关系。
2-3(图略):TEXTMETRIC结构及其与字体的关系。

除了可以判断您是否真的获得了您需要的字体外,GetTextmetrics函数调用还有另外一个有价值的用途--确定字体高度。回忆一下TextDemo程序,行的高度是通过调用DrawText函数计算出来的。虽然那种方法很方便,但它有点慢。您可以使用TEXTMETRIC数据,以更加直接的方法来计算高度。将表示字符高度的tmHeight域与tmExternalLeading域--表示一行底部像素到下一行顶部像素之间的间距--相加,您就可以获得两行文本基线的间距了。

虽然GetTextMetrics对确定字符高度来说是很好用的,但它只提供了字体的平均宽度和最大宽度。如果需要TrueType的更多细节,可以使用函数GetCharABCWidths,函数原型如下:
BOOL GetCharABCWidths (HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
GetCharABCWidths返回由
uFirstChar 和 uLastChar 参数所确定的一系列字符的ABC宽度。该函数检查hdc参数指定的DC里的字体。ABC结构如下:
typedef struct _ABC {
    int     abcA;
    UINT    abcB;
    int     abcC;
} ABC;
abcA为在放置字符轮廓前的空白间距,
abcB为字符轮廓本身的间距,abcC为字符轮廓右方的空白间距。abcA和abcC都可以是负值,用来指示缩进或者凸起。

要获得点阵字体(bitmap fonts)的宽度,可以使用GetCharWidth32函数。对指定字符范围内的每个字符,该函数返回一个字符宽度数组。

url: http://www.51lm.cn/p/templates/cn/show.php?cid=0&aid=255

 

电话400-878-1895, 传真:021-51561359 邮箱:sales@51LM.cn
sales@51LM.cn 上海徐汇区斜土路2601号嘉汇广场T1-11B