[翻译]-WinCE 程序设计 (3rd 版)--2.4 线条和形状

线条和形状
同Windows其它版本相比,Windows CE提供相当少的功能的领域之一就是基本线条绘制和形状绘制功能。用来创建复杂环形的Chord, Arc, 和Pie函数被去掉了。大部分使用"当前点[current point]"概念的函数也被去掉了。除了MoveToEx, LineTo和GetCurrentPositionEx外,处理当前点的其它GDI函数都不被Windows CE支持。因此想用 ArcTo、PolyBezierTo等函数来绘制一系列连接的直线和曲线是不可能了。不过即使在缺少很多图形函数的情况下,Windows CE依然提供了绘制直线和形状所需要的基本函数。

线条
简单调用Polyline就可以绘制一个或者多个线条了。函数原型如下:

BOOL Polyline (HDC hdc, const POINT *lppt, int cPoints);
第2个参数是指向POINT结构数组的指针,该结构定义如下:
typedef struct tagPOINT {
    LONG x;
    LONG y;
} POINT

每个X、Y结合起来描述一个从屏幕左上角开始的像素。第三个参数是数组里的point结构的数量。因此绘制一个从(0,0)到(50,100)的直线,代码看起来如下:
POINTS pts[2];
  
pts[0].x = 0;
pts[0].y = 0;
pts[1].x = 50;
pts[1].y = 100;
PolyLine (hdc, &pts, 2);

绘制该直线的另外一个方法是使用MoveToEx和LineTo函数。它们的原型如下:
BOOL WINAPI MoveToEx (HDC hdc, int X, int Y, LPPOINT lpPoint);
BOOL WINAPI LineTo (HDC hdc, int X, int Y);

要使用这两个函数绘制线条,首先要调用MoveToEx将当前点移动到线条的起始座标处,接下来用终点座标调用LineTo。调用代码如下:
MoveToEx (hdc, 0, 0, NULL);
LineTo (hdc, 50, 100);

要查询当前点,可调用函数GetCurrentPositionEx,原型如下:
WINGDIAPI BOOL WINAPI GetCurrentPositionEx (HDC hdc, LPPOINT pPoint);

和前面绘制文本的例子一样,这些代码片段对设备描述表的状态做了大量假设。例如,绘制的(0, 0)和(50, 100)之间的线条是什么样子?宽度和颜色是什么以及是实心线条吗?所有版本的Windows,包括Windows CE在内,都允许指定这些参数。

画笔
画笔(pen)是用于指定线条外观和形状轮廓的工具。画笔是另一个GDI对象,像本章里描述的其它GDI对象一样,画笔要被创建、选进设备描述表,使用、取消选择,最后被销毁。 同其它备用GDI对象一样,可以使用GetStockObject来检索备用画笔。该函数原型如下:
HGDIOBJ GetStockObject (int fnObject);
所有版本的Windows都提供三种备用画笔,每个1像素宽。这些备用画笔有3种颜色:白色、黑色和NULL。当您使用GetStockObject时,该函数分别使用参数WHITE_PEN, BLACK_PEN和NULL_PEN来检索这些画笔中的一个。与应用程序创建的标准图形对象不同,备用对象不应该被应用程序删除。相反,当画笔不再需要的时候,应用程序只应简单地将画笔从设备描述表中取消选择即可。

要在Windows下创建自定义画笔,可以使用以下两个函数。第一个是:
HPEN CreatePen (int fnPenStyle, int nWidth, COLORREF crColor);
fnPenStyle规定要绘制的线条的外观。例如,使用PS_DASH标志可以创建一个虚线。Windows CE只支持PS_SOLID、PS_DASH和PS_NULL这三个风格标志。nWidth参数规定画笔的宽度。最后,crColor规定画笔的颜色。crColor的参数类型是COLORREF,可以使用RGB宏来构造该类型。RGB宏定义如下:

COLORREF RGB (BYTE bRed, BYTE bGreen, BYTE bBlue);
因此要创建一个红色实心线条,代码看起来像这样:
hPen = CreatePen (PS_SOLID, 1, RGB (0xff, 0, 0));

另一种画笔创建函数如下:
HPEN CreatePenIndirect (const LOGPEN *lplgpn);
其中逻辑画笔结构LOGPEN定义如下:
typedef struct tagLOGPEN {
    UINT lopnStyle;
    POINT lopnWidth;
    COLORREF lopnColor;
} LOGPEN;
CreatePenIndirect用不同的形式为Windows提供了同样的参数。用CreatePenIndirect创建同样是1像素宽的红色画笔,代码如下:
LOGPEN lp;
HPEN hPen;
lp.lopnStyle = PS_SOLID;
lp.lopnWidth.x = 1;
lp.lopnWidth.y = 1;
lp.lopnColor = RGB (0xff, 0, 0);
  
hPen = CreatePenIndirect (&lp);

Windows CE不支持复杂画笔,比如宽度超过1像素的虚线。要确定支持什么,我们熟悉的GetDeviceCaps就派上用场了,给它的第2个参数取LINECAPS即可。具体可以参考Windows CE文档。

形状
线条很有用,不过Windows还提供了绘制形状的函数,包括填充和非填充的形状。Windows CE提供了Windows程序员大部分常见的函数。

Rectangle, RoundRect, Ellipse和Polygon都支持。

画刷
在讨论矩形和椭圆形之前,需要先讲述另一个曾经简要提到过的GDI对象--画刷(brush)。画刷通常是一个8*8像素的位图,用于填充形状。
Windows也用它来填充客户窗口地背景。Windows CE提供许多备用画刷,并提供从应用程序定义的图案创建画刷的能力。许多纯色备用画刷可以使用GetStockObject来检索。在众多可用画刷中,有一个是用于四色灰度级显示器的每个灰度的,四种灰度是:白色、浅灰色、深灰色和黑色。

要创建纯色画刷,可以调用以下函数:
HBRUSH CreateSolidBrush (COLORREF crColor);
crColor规定了画刷的颜色。颜色可以使用RGB宏来指定。
Windows CE下用Win32函数 CreateDIBPatternBrushPt创建自定义图案的画刷。函数原型如下:
HBRUSH CreateDIBPatternBrushPt (const void *lpPackedDIB, UINT iUsage);
第一个参数指向紧凑格式的DIB。这意味着指针指向一个缓冲区,包含有BITMAPINFO结构,并且紧随其后的是位图的位数据。您应该还记得BITMAPINFO结构实际上由BITMAPINFOHEADER结构及紧随其后的RGBQUAD格式的调色板构成,所以该缓冲区包含了创建DIB所需要的每个信息,即位图信息、调色板、位图的位数据。如果第二个参数设置为DIB_RGB_COLORS,则调色板在每个入口都包含有RGBQUAD值。对于每像素8位的位图来说,可以设置DIB_PAL_COLORS标志,但Windows CE会忽略位图的颜色表。

在Windows CE下CreateDIBPatternBrushPt更加重要,因为Windows CE下不支持阴影画刷,而其它版本的Windows使用CreateHatchBrush函数来支持阴影画刷。阴影画刷是由水平线条、垂直线条或斜线构成的画刷。这些画刷在灰度级显示器上特别有用,因为您可以使用不同阴影图案来突出图表的不同区域。不过,您可以使用CreateDIBPatternBrushPt和适当的位图图案来复制出这些画刷。本章后面的示例代码演示了在Windows CE下创建阴影画刷的方法。

默认情况下,画刷原点在窗口左上角。这并不总是你所希望的。例如,一个条形图使用阴影画刷填充从(100,100)到(125,220)的矩形。因为该矩形不能被8(画刷通常是8*8像素的正方形)整除,所以就用一个不怎么美观的局部画刷来填充条形图的左上角。为了避免这种情况,您可以移动画刷的原点,这样可以使用与形状的边角正确对齐的画刷来绘制各个形状了。用来完成这一调整的函数如下:BOOL SetBrushOrgEx (HDC hdc, int nXOrg, int nYOrg, LPPOINT lppt);
nXOrg和nYOrg允许将原点设置为0到7之间,这样就可以将原点定位在画刷8*8范围内任何一点。lppt填充的是画刷先前的版本,这样可以在必要的时候恢复先前的原点。

矩形
矩形函数绘制一个填充矩形或者一个中空矩形。该函数定义如下:
BOOL Rectangle (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
该函数使用当前选择的画笔绘制矩形外框,使用当前画刷填充内部。要绘制中空矩形,需要在调用Rectangle之前把空画刷选择进设备描述表中。

理解绘制边框的实际像素是很重要的。假定我们要在(0,0)处绘制一个5*7的矩形,函数调用如下:
Rectangle(0,0,5,7);
假设画笔是1像素宽,结果矩形如图2-6所示。
图2-6:放大后的用Rectangele绘制的矩形视图。
注意观察矩形右边界实际上是如何绘制到第4列的,底部边缘是如何绘制到第6行的。这是标准的Windows惯例。矩形在Rectangle函数指定的右边界和底边界以内进行绘制。如果选择的画笔宽度超过1个像素,右边界和底边界以边框矩形居中进行绘制。(Windows其它版本允许使用PS_INSIDEFRAME画笔风格忽略画笔宽度,强制在框架内绘制矩形)

圆和椭圆
可以用Ellipse函数绘制圆和椭圆,该函数原型如下:
BOOL Ellipse (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
使用传入的矩形作为边界矩形(bounding rectangle)来绘制椭圆,如图2-7所示。对于Rectangle函数,使用当前画刷来填充椭圆内部,使用当前画笔来绘制椭圆外框。
图2-7:展示了Ellipse使用传入的边界矩形绘制出的椭圆。

圆角矩形
RoundRect函数绘制一个圆角矩形,原型如下:
BOOL RoundRect (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nWidth, int nHeight);
最后两个参数给出了用于圆角的椭圆的宽度和高度,如图2-8所示。指定椭圆的高度和宽度可以让程序绘制出完全一样的均匀的圆角。缩短椭圆的高度,可以使矩形的两侧更平,而缩短椭圆的宽度可以使矩形的顶部和底部更平。
图2-8:椭圆的高度和宽度规定了由RoundRect绘制的矩形的圆角

多边形
最后,Polygon绘制了一个多边形,函数原型如下:BOOL Polygon (HDC hdc, const POINT *lpPoints, int nCount);
第2个参数是一个指向Point结构数组的指针,该数组定义了描述多边形的各个点。从最终形状上看,会被点数多一条边,因为函数自动把最后一个点和第一个点连接起来,绘制出多边形的最后一条边。

填充函数
前面提到的函数都是使用画刷和画笔的组合在设备描述表上绘制形状的。只填充区域而不涉及绘制形状轮廓的画笔的函数也是有的。这些函数中的第一个就如下所示:
int FillRect (HDC hDC, CONST RECT* lprc, HBRUSH hbr);
FillRect的参数设备描述表句柄、需要填充的矩形以及用来填充矩形的画刷。要在矩形区域里绘制一个纯色或者图案,使用FillRect函数会是一个快捷方便的方式。
虽然FillRect很方便,但GradientFill可能更棒一些。GradientFill函数填充一个矩形区域,使用一个颜色从一边开始绘制,并逐渐平滑过度到另外一个颜色直到另外一边。图2-9展示了一个客户区使用GradientFill进行绘制的窗口。书中印刷的黑白插图不能看出颜色的效果,但即使在这样的图上,依然能轻易的看到平滑过度的样子。
图2-9:使用GradientFill函数绘制的窗口。
GradientFill函数原型如下:
BOOL GradientFill (HDC hdc, PTRIVERTEX pVertex, ULONG dwNumVertex, PVOID pMesh, ULONG dwNumMesh, ULONG dwMode);
第一个参数依旧是设备描述表。pVertex是指向TRIVERTEX结构数组的指针,dwNumVertex是TRIVERTEX数组中入口的数量。TRIVERTEX结构定义如下:
struct _TRIVERTEX {   
    LONG        x;
    Long        y;
    COLOR16     Red;
    COLOR16     Green;
    COLOR16     Blue;
    COLOR16     Alpha;s
} TRIVERTEX;
TRIVERTEX结构的各个域描述了设备描述表里的一个点和一个RGB颜色。这些点应该是要填充的矩形的左上角和右下角。pMesh是指向GRADIENT_RECT结构的指针,该结构定义如下:
struct _GRADIENT_RECT
{
    ULONG UpperLeft;
    ULONG LowerRight;
} GRADIENT_RECT;
GRADIENT_RECT结构简单指出TRIVERTEX结构中的哪些入口是描述左上角或者是右下角的。最后,dwNumMesh参数包含GRADIENT_RECT结构的数量。dwMode结构包含标志位,指出是从左到右(GRADIENT_FILL_RECT_H)填充还是从上到下(GRADIENT_FILL_RECT_V)填充。GradientFill函数实际上比表面上看到的要更复杂,因为在桌面系统里,它还执行三角形填充,这种填充方式在Windows CE下不支持。下面是创建图2-9的窗口的代码片段:
TRIVERTEX vert[2];
GRADIENT_RECT gRect;
  
vert [0] .x       =  prect->left;
vert [0] .y       =  prect->top;
vert [0] .Red     =  0x0000;
vert [0] .Green   =  0x0000;
vert [0] .Blue    =  0xff00;
vert [0] .Alpha   =  0x0000;
  
vert [1] .x       =  prect->right;
vert [1] .y       =  prect->bottom;
vert [1] .Red     =  0x0000;
vert [1] .Green   =  0xff00;
vert [1] .Blue    =  0x0000;
vert [1] .Alpha   =  0x0000;
  
gRect.UpperLeft = 0;
gRect.LowerRight = 1;
  
GradientFill(hdc,vert,2,&gRect,1,GRADIENT_FILL_RECT_H);
Shapes示例程序
清单2-3中,Shapes程序演示了很多相关函数。在Shapes中,绘制了4个图形,每个使用不同的画刷进行了填充。Listing 2-3: Shapes示例程序
Shapes.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.
};
  
//----------------------------------------------------------------------
// Defines used by MyCreateHatchBrush
//
typedef struct {
    BITMAPINFOHEADER bmi;
    COLORREF dwPal[2];
    BYTE bBits[64];
} BRUSHBMP;
  
#define HS_HORIZONTAL       0       /* ----- */
#define HS_VERTICAL         1       /* ||||| */
#define HS_FDIAGONAL        2       /* ///// */
#define HS_BDIAGONAL        3       /* ///// */
#define HS_CROSS            4       /* +++++ */
#define HS_DIAGCROSS        5       /* xxxxx */
  
//----------------------------------------------------------------------
// 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);

Shapes.cpp
//======================================================================
// Shapes- Brush and shapes demo for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h>                 // For all that Windows stuff
#include "shapes.h"                  // Program-specific stuff
  
//----------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("Shapes");
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;
    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;
  
    // Save program instance handle in global variable.
    hInst = hInstance;
  
#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
                         szAppName,           // Window class
                         TEXT("Shapes"),      // Window title
                         WS_VISIBLE,          // Style flags
                         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 (!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);
}
//----------------------------------------------------------------
// MyCreateHatchBrush - Creates hatched brushes
//
HBRUSH MyCreateHatchBrush (INT fnStyle, COLORREF clrref) {
    BRUSHBMP brbmp;
    BYTE *pBytes;
    int i;
    DWORD dwBits[6][2] = {
        {0x000000ff,0x00000000}, {0x10101010,0x10101010},
        {0x01020408,0x10204080}, {0x80402010,0x08040201},
        {0x101010ff,0x10101010}, {0x81422418,0x18244281},
    };
  
    if ((fnStyle < 0) || (fnStyle > dim(dwBits)))
        return 0;
    memset (&brbmp, 0, sizeof (brbmp));
  
    brbmp.bmi.biSize = sizeof (BITMAPINFOHEADER);
    brbmp.bmi.biWidth = 8;
    brbmp.bmi.biHeight = 8;
    brbmp.bmi.biPlanes = 1;
    brbmp.bmi.biBitCount = 1;
    brbmp.bmi.biClrUsed = 2;
    brbmp.bmi.biClrImportant = 2;
  
    // Initialize the palette of the bitmap.
    brbmp.dwPal[0] = PALETTERGB(0xff,0xff,0xff);
    brbmp.dwPal[1] = PALETTERGB((BYTE)((clrref >> 16) & 0xff),
                                (BYTE)((clrref >> 8) & 0xff),
                                (BYTE)(clrref & 0xff));
  
    // Write the hatch data to the bitmap. 
    pBytes = (BYTE *)&dwBits[fnStyle];
    for (i = 0; i < 8; i++)
        brbmp.bBits[i*4] = *pBytes++;
  
    // Return the handle of the brush created.
    return CreateDIBPatternBrushPt (&brbmp, DIB_RGB_COLORS);
}
//----------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam,
                     LPARAM lParam) {
    PAINTSTRUCT ps;
    RECT rect;
    HDC hdc;
    POINT ptArray[6];
    HBRUSH hBr, hOldBr;
    TCHAR szText[128];
  
    GetClientRect (hWnd, &rect);
    hdc = BeginPaint (hWnd, &ps);
  
    // Draw ellipse.
    hBr = (HBRUSH) GetStockObject (DKGRAY_BRUSH);
    hOldBr = (HBRUSH) SelectObject (hdc, hBr);
    Ellipse (hdc, 10, 50, 90, 130);
    SelectObject (hdc, hOldBr);
  
    // Draw round rectangle.
    hBr = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
    hOldBr = (HBRUSH) SelectObject (hdc, hBr);
    RoundRect (hdc, 95, 50, 150, 130, 30, 30);
    SelectObject (hdc, hOldBr);
  
    // Draw hexagon using Polygon.
    hBr = (HBRUSH) GetStockObject (WHITE_BRUSH);
    hOldBr = (HBRUSH) SelectObject (hdc, hBr);
    ptArray[0].x = 192;
    ptArray[0].y = 50;
    ptArray[1].x = 155;
    ptArray[1].y = 75;
    ptArray[2].x = 155;
    ptArray[2].y = 105;
    ptArray[3].x = 192;
    ptArray[3].y = 130;
    ptArray[4].x = 230;
    ptArray[4].y = 105;
    ptArray[5].x = 230;
    ptArray[5].y = 75;
  
    Polygon (hdc, ptArray, 6);
    SelectObject (hdc, hOldBr);
  
    hBr = (HBRUSH) MyCreateHatchBrush (HS_DIAGCROSS, RGB (0, 0, 0));
    hOldBr = (HBRUSH) SelectObject (hdc, hBr);
    Rectangle (hdc, 10, 145, 225, 210);
    SelectObject (hdc, hOldBr);
    DeleteObject (hBr);
  
    SetBkMode (hdc, OPAQUE);
    lstrcpy (szText, TEXT ("Opaque background"));
    ExtTextOut (hdc, 20, 160, 0, NULL,
                szText, lstrlen (szText), NULL);
  
    SetBkMode (hdc, TRANSPARENT);
    lstrcpy (szText, TEXT ("Transparent background"));
    ExtTextOut (hdc, 20, 185, 0, NULL,
                szText, lstrlen (szText), NULL);
  
    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;
}
在Shapes里,OnPaintMain使用先前讨论过的那些不同的函数绘制了四个图形。对每个形状,都创建了一个不同的画刷,并选择进设备描述表,绘制图形,最后从设备描述表中取消选择。第一个图形使用纯灰色阴影来填充,用的是GetStockObject函数来装载这些纯色画刷的。最后一个图形则是使用由CreateDIBPatternBrushPt函数创建的画刷来填充的。该画刷的创建过程封装到MyCreateHatchBrush函数里了,该函数模拟了Windows CE下不支持的CreateHatchBrush函数。通过填充一个位图结构和设置构成阴影图案的位数据,创建了一个黑白位图,用于创建阴影画刷。这个位图是一个由CreateDIBPatternBrushPta函数指定的8*8的位图。因为该位图是单色的,所以包括调色板和头在内,它的总大小只有大约100字节。注意,因为位图的每个扫描行必须是双字对齐的,所以每个一字节扫描行的最后3个字节是没有使用的。

最后,程序写两行文字到最后的矩形里。文本进一步演示了opaque和transparent这两个系统绘图模式之间的差异。在本例中,文本绘制时使用opaque模式会更适合实际情况,因为在transparent模式下,阴影线会使字母变的模糊。图2-10展示了Shapes窗口。
图2-10演示了如何绘制不同填充的图形。

为了使事情简单一些,例子Shapes假设其运行在至少240像素宽的显示器上。这使得Shapes程序在手持式PC和Pocket PC上都能很好的运行。我只是浅显的描述了Windows CE中GWE的GDI部分,本章的目标并不是为GDI编程的各方面提供完整的描述。相反,我想去演示Windows CE下用于基本图画和文本的方法。在本书其它章节里,扩展了本章涉及的技术。通常,在演示如何在代码中使用的时候,我会谈论这些新技术和新引入的函数。为了扩展您的知识,我推荐《Windows 程序设计》(Programming Windows, 5th edition, by Charles Petzold)(Microsoft Press, 1998)一书作为学习Windows GDI的最佳资料。

既然我们已经看了输出,那么是时候把注意力转到系统输出--键盘和触摸板方面了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章