在开始绘图之前,让我们比第四章更精确地讨论一下设备内容。
当您想在一个图形输出设备(诸如屏幕或者打印机)上绘图时,您首先必须获得一个设备内容(或者DC)的句柄。将句柄传回给程序时,Windows就给了您使用设备的权限。然后您在GDI函数中将这个句柄作为一个参数,向Windows标识您想在其上进行绘图的设备。
设备内容中包含许多确定GDI函数如何在设备上工作的目前「属性」,这些属性允许传递给GDI函数的参数只包含起始坐标或者尺寸信息,而不必包含Windows在设备上显示对象时需要的所有其它信息。例如,呼叫TextOut时,您只需要在函数中给出设备内容句柄、起始坐标、文字和文字的长度。您不必指定字体、文字颜色、文字后面的背景色彩以及字符间距,因为这些属性都是设备内容的一部分。当您想改变这些属性之一时,您呼叫一个可以改变设备内容中属性的函数,以后针对该设备内容的TextOut呼叫来使用改变后的属性。
取得设备内容句柄
Windows提供了几种取得设备内容句柄的方法。如果在处理一个消息时取得了设备内容句柄,应该在退出窗口函数之前释放它(或者删除它)。一旦释放了句柄,它就不再有效了。对于打印机设备内容句柄,规则就没有这么严格。在第十三章会讨论打印。
最常用的取得并释放设备内容句柄的方法是,在处理WM_PAINT消息时,使用BeginPaint和EndPaint呼叫:
hdc = BeginPaint (hwnd, &ps) ; 其它行程序 EndPaint (hwnd, &ps) ;
变量ps是型态为PAINTSTRUCT的结构,该结构的hdc字段是BeginPaint传回的设备内容句柄。 PAINTSTRUCT结构又包含一个名为rcPaint的RECT(矩形)结构,rcPaint定义一个包围窗口显示区域无效范围的矩形。使用从BeginPaint获得的设备内容句柄,只能在这个区域内绘图。BeginPaint呼叫使该区域有效。
Windows程序还可以在处理非WM_PAINT消息时取得设备内容句柄:
hdc = GetDC (hwnd) ; 其它行程序 ReleaseDC (hwnd, hdc) ;
这个设备内容适用于窗口句柄为hwnd的显示区域。这些呼叫与BeginPaint和EndPaint的组合之间的基本区别是,利用从GetDC传回的句柄可以在整个显示区域上绘图。当然, GetDC和ReleaseDC不使显示区域中任何可能的无效区域变成有效。
Windows程序还可以取得适用于整个窗口(而不仅限于窗口的显示区域)的设备内容句柄:
hdc = GetWindowDC (hwnd) ; 其它行程序 ReleaseDC (hwnd, hdc) ;
这个设备内容除了显示区域之外,还包括窗口的标题列、菜单、滚动条和框架(frame)。GetWindowDC函数很少使用,如果想尝试用一用它,则必须拦截处理WM_NCPAINT消息,Windows使用该消息在窗口的非显示区域上绘图。
BeginPaint、GetDC和GetWindowDC获得的设备内容都与视讯显示器上的某个特定窗口相关。取得设备内容句柄的另一个更通用的函数是CreateDC:
hdc = CreateDC (pszDriver, pszDevice, pszOutput, pData) ; 其它行程序 DeleteDC (hdc) ;
例如,您可以通过下面的呼叫来取得整个屏幕的设备内容句柄:
hdc = CreateDC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
在窗口之外写入画面一般是不恰当的,但对于一些不同寻常的应用程序来说,这样做很方便(您还可通过在呼叫GetDC时使用一个NULL参数,从而取得整个屏幕的设备内容句柄,不过这在文件中已经提到了)。在 第十三章中,我们将使用CreateDC函数来取得一个打印机设备内容句柄。
有时您只是需要取得关于某设备内容的一些信息而并不进行任何绘画,在这种情况下,您可以使用CreateIC来取得一个「信息内容」的句柄,其参数与CreateDC函数相同,例如:
hdc = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
您不能用这个信息内容句柄往设备上写东西。
使用位图时,取得一个「内存设备内容」有时是有用的:
hdcMem = CreateCompatibleDC (hdc) ; 其它行程序 DeleteDC (hdcMem) ;
您可以将位图选进内存设备内容,然后使用GDI函数在位图上绘画。我将在第十四章讨论这些技术。
前面已经提到过,metafile是一些GDI呼叫的集合,以二进制形式编码。您可以通过取得metafile设备内容来建立metafile:
hdcMeta = CreateMetaFile (pszFilename) ; 其它行程序 hmf = CloseMetaFile (hdcMeta) ;
在metafile设备内容有效期间,任何用hdcMeta所做的GDI呼叫都变成metafile的一部分而不会显示。在呼叫CloseMetaFile之后,设备内容句柄变为无效,函数传回一个指向metafile(hmf)的句柄。我会在 第十八章讨论metafile。
取得设备内容信息
一个设备内容通常是指一个实际显示设备,如视讯显示器和打印机。通常,您需要取得有关该设备的信息,包括显示器的大小(单位为图素或者实际长度单位)和色彩显示能力。您可以通过呼叫GetDeviceCaps(「取得设备功能」)函数来取得这些信息:
iValue = GetDeviceCaps (hdc, iIndex) ;
其中,参数iIndex取值为WINGDI.H表头文件中定义的29个标识符之一。例如,iIndex为HORZRES时将使GetDeviceCaps传回设备的宽度(单位为图素);iIndex为VERTRES时将让GetDeviceCaps传回设备的高度(单位为图素)。如果hdc是打印机设备内容的句柄,则GetDeviceCaps传回打印机显示区域的高度和宽度,它们也是以图素为单位的。
还可以使用GetDeviceCaps来确定设备处理不同型态图形的能力,这对于视讯显示器并不很重要,但是对于打印设备却是非常重要。例如,大多数绘图机不能画位图图像,GetDeviceCaps就可以将这一情况告诉您。
DEVCAPS1程序
程序5-1所示的DEVCAPS1程序显示了以一个视讯显示器的设备内容为参数时,可以从 GetDeviceCaps函数中获得的部分信息(该程序的另一个扩充版本DEVCAPS2将在第十三章给出,用于取得打印机信息)。
DEVCAPS1.C /*------------------------------------------------------------------------ DEVCAPS1.C -- Device Capabilities Display Program No. 1 (c) Charles Petzold, 1998 ----------------------------------------------------------------------*/ #include <windows.h> #define NUMLINES ((int) (sizeof devcaps / sizeof devcaps [0])) struct { int iIndex ; TCHAR *szLabel ; TCHAR *szDesc ; } devcaps [] = { HORZSIZE, TEXT ("HORZSIZE"),TEXT ("Width in millimeters:"), VERTSIZE, TEXT ("VERTSIZE"),TEXT ("Height in millimeters:"), HORZRES, TEXT ("HORZRES"), TEXT ("Width in pixels:"), VERTRES, TEXT ("VERTRES"), TEXT ("Height in raster lines:"), BITSPIXEL, TEXT ("BITSPIXEL"),TEXT ("Color bits per pixel:"), PLANES, TEXT ("PLANES"), TEXT ("Number of color planes:"), NUMBRUSHES, TEXT ("NUMBRUSHES"), TEXT ("Number of device brushes:"), NUMPENS, TEXT ("NUMPENS"), TEXT ("Number of device pens:"), NUMMARKERS, TEXT ("NUMMARKERS"), TEXT ("Number of device markers:"), NUMFONTS, TEXT ("NUMFONTS"), TEXT ("Number of device fonts:"), NUMCOLORS, TEXT ("NUMCOLORS"), TEXT ("Number of device colors:"), PDEVICESIZE, TEXT ("PDEVICESIZE"),TEXT ("Size of device structure:"), ASPECTX, TEXT ("ASPECTX"), TEXT ("Relative width of pixel:"), ASPECTY, TEXT ("ASPECTY"), TEXT ("Relative height of pixel:"), ASPECTXY, TEXT ("ASPECTXY"), TEXT ("Relative diagonal of pixel:"), LOGPIXELSX, TEXT ("LOGPIXELSX"), TEXT ("Horizontal dots per inch:"), LOGPIXELSY, TEXT ("LOGPIXELSY"), TEXT ("Vertical dots per inch:"), SIZEPALETTE, TEXT ("SIZEPALETTE"),TEXT ("Number of palette entries:"), NUMRESERVED, TEXT ("NUMRESERVED"),TEXT ("Reserved palette entries:"), COLORRES, TEXT ("COLORRES"), TEXT ("Actual color resolution:") } ; LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("DevCaps1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc= WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName= NULL ; wndclass.lpszClassName= szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Device Capabilities"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxChar, cxCaps, cyChar ; TCHAR szBuffer[10] ; HDC hdc ; int i ; PAINTSTRUCT ps ; TEXTMETRIC tm ; switch (message) { case WM_CREATE: hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ; cxChar= tm.tmAveCharWidth ; cxCaps= (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ; cyChar= tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; for (i = 0 ; i < NUMLINES ; i++) { TextOut ( hdc, 0, cyChar * i, devcaps[i].szLabel, lstrlen (devcaps[i].szLabel)) ; TextOut ( hdc, 14 * cxCaps, cyChar * i, devcaps[i].szDesc, lstrlen (devcaps[i].szDesc)) ; SetTextAlign (hdc, TA_RIGHT | TA_TOP) ; TextOut (hdc, 14*cxCaps+35*cxChar, cyChar*i, szBuffer, wsprintf (szBuffer, TEXT ("%5d"), GetDeviceCaps (hdc, devcaps[i].iIndex))) ; SetTextAlign (hdc, TA_LEFT | TA_TOP) ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
可以看到,这个程序非常类似第四章的SYSMETS1。为了保持程序代码的短小,我没有使用滚动条,因为我知道信息可以在一个画面上显示出来。在256色,640×480的VGA上显示的结果如图5-1所示。
图5-1 256色,640×480VGA上的DEVCAPS1显示 |