一 .取得设备内容句柄(4种方法)
1. WM_PAINT消息
最常用的取得并释放设备内容句柄的方法是,在处理WM_PAINT消息时,使用
变量ps是型态为PAINTSTRUCT的结构,该结构的hdc字段是BeginPaint传回的设备内容句柄。 PAINTSTRUCT结构又包含一个名为rcPaint的RECT(矩形)结构,rcPaint定义一个包围窗口显示区域无效范围的矩形。使用从BeginPaint获得的设备内容句柄,只能在这个区域内绘图。BeginPaint呼叫使该区域有效。
2.非WM_PAINT消息
GetDC和ReleaseDC不使显示区域中任何可能的无效区域变成有效
3 Windows程序还可以取得适用于整个窗口(而不仅限于窗口的显示区域)的设备内容句柄
这个设备内容除了显示区域之外,还包括窗口的标题列、菜单、滚动条和框架(frame)。GetWindowDC函数很少使用,如果想尝试用一用它,则必须拦截处理WM_NCPAINT消息,Windows使用该消息在窗口的非显示区域上绘图。
4
二 取得设备内容信息
iValue = GetDeviceCaps (hdc, iIndex) ;
如果hdc是打印机设备内容的句柄,则GetDeviceCaps传回打印机显示区域的高度和宽度,它们也是以图素为单位的。
设备内容属性
关于色彩
但是GetDeviceCaps使程序写作者可以知道显示卡的储存组织以及它能够表示的色彩数目,下面的呼叫传回色彩平面的数目:
iPlanes = GetDeviceCaps (hdc, PLANES) ;
下面的呼叫传回每个图素的色彩位数:
iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL) ;大多数彩色图形显示设备使用多个色彩平面或每图素有多个色彩位的设计,但是不能同时一齐使用这两种方式;换句话说,这两个呼叫必有一个传回1。显示卡能够表示的色彩数可以用如下公式来计算:
iColors = 1 << (iPlanes * iBitsPixel) ;这个值与用NUMCOLORS参数得到的色彩数值可能一样,也可能不一样:
iColors = GetDeviceCaps (hdc, NUMCOLORS) ; 可以呼叫GetNearestColor来决定与某一色彩最接近的纯色:crPureColor = GetNearestColor (hdc, crColor) ;
前面已经提到过,Windows使用设备内容来保存控制GDI函数在显示器上如何操作的「属性」。例如,在用TextOut函数显示文字时,程序写作者不必指定文字的色彩和字体,Windows从设备内容取得这个信息。
程序取得一个设备内容的句柄时,Windows用默认值设定所有的属性(在下一节会看到如何取代这种设定)。表5-1列出了Windows 98支持的设备内容属性,程序可以改变或者取得任何一种属性。
表5-1 |
设备内容属性 | 默认值 | 修改该值的函数 | 取得该值的函数 |
Mapping Mode | MM_TEXT | SetMapMode | GetMapMode |
Window Origin | (0, 0) | SetWindowOrgEx OffsetWindowOrgEx | GetWindowOrgEx |
Viewport Origin | (0, 0) | SetViewportOrgEx OffsetViewportOrgEx | GetViewportOrgEx |
Window Extents | (1, 1) | SetWindowExtEx SetMapMode ScaleWindowExtEx | GetWindowExtEx |
Viewport Extents | (1, 1) | SetViewportExtEx SetMapMode ScaleViewportExtEx | GetViewportExtEx |
Pen | BLACK_PEN | SelectObject | SelectObject |
Brush | WHITE_BRUSH | SelectObject | SelectObject |
Font | SYSTEM_FONT | SelectObject | SelectObject |
Bitmap | None | SelectObject | SelectObject |
Current Position | (0, 0) | MoveToEx LineTo PolylineTo PolyBezierTo | GetCurrentPositionEx |
Background Mode | OPAQUE | SetBkMode | GetBkMode |
Background Color | White | SetBkColor | GetBkColor |
Text Color | Black | SetTextColor | GetTextColor |
Drawing Mode | R2_COPYPEN | SetROP2 | GetROP2 |
Stretching Mode | BLACKONWHITE | SetStretchBltMode | GetStretchBltMode |
Polygon Fill Mode | ALTERNATE | SetPolyFillMode | GetPolyFillMode |
Intercharacter Spacing | 0 | SetTextCharacterExtra | GetTextCharacterExtra |
Brush Origin | (0, 0) | SetBrushOrgEx | GetBrushOrgEx |
Clipping Region | None | SelectObject SelectClipRgn IntersectClipRgn OffsetClipRgn ExcludeClipRect SelectClipPath | GetClipBox |
保存设备内容
通常,在您呼叫GetDC或BeginPaint时,Windows用默认值建立一个新的设备内容,
用ReleaseDC或EndPaint呼叫释放时,都会丢失。
如果您的程序需要使用非内定的设备内容属性,则您必须在每次取得设备内容句柄时初始化设备内容:
case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; 设备内容属性 绘制窗口显示区域 EndPaint (hwnd, &ps) ; return 0 ;
以便在下一次呼叫GetDC和BeginPaint时它们仍然能够起作用。为此,可在登录窗口类别时,将CS_OWNDC旗标纳入窗口类别的一部分:wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;依据这个窗口类别所建立的每个窗口都将拥有自己的设备内容,它一直存在,直到窗口被删除。如果使用了CS_OWNDC风格,就只需初始化设备内容一次,可以在处理WM_CREATE消息处理期间完成这一操作:case WM_CREATE: hdc = GetDC (hwnd) ; 初始化设备内容属性 ReleaseDC (hwnd, hdc) ;
这些属性在改变之前一直有效。可以通过如下呼叫来保存设备内容的状态:
idSaved = SaveDC (hdc) ;现在,可以改变一些属性,在想要回到呼叫SaveDC前存在的设备内容时,呼叫:
RestoreDC (hdc, idSaved) ;您可以在呼叫RestoreDC之前呼叫SaveDC数次。当您呼叫SaveDC时,不需要保存传回值:
SaveDC (hdc) ;然后,您可以更改某些属性并再次呼叫SaveDC。要将设备内容恢复到一个已经保存的状态,呼叫:
RestoreDC (hdc, -1) ; 这就将设备内容恢复到最近由SaveDC函数保存的状态中
画点和线
增加一个消息case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;case WM_PAINT:hdc = BeginPaint (hwnd, &ps) ;
MoveToEx (hdc, 0, cyClient / 2, NULL) ;
LineTo (hdc, cxClient, cyClient / 2) ;EndPaint(hdc,&ps);return 0 ;![]()
边界框函数
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
//画一个矩形:
Rectangle (hdc, cxClient / 8, cyClient / 8,
7 * cxClient / 8, 7 * cyClient / 8) ;
//直线
MoveToEx (hdc, 0, 0, NULL) ;
LineTo (hdc, cxClient, cyClient) ;
//画椭圆
Ellipse (hdc, cxClient / 8, cyClient / 8,
7 * cxClient / 8, 7 * cyClient / 8) ;
//画圆角矩形的函数使用与函数Rectangle及Ellipse函数相同的边界框,还包含另外两个参数:
RoundRect (hdc, cxClient / 4, cyClient / 4,
3 * cxClient / 4, 3 * cyClient / 4,
cxClient / 4, cyClient / 4) ;
贝塞尔曲线(略)
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON || wParam & MK_RBUTTON)
要画一条或多条连接的贝塞尔曲线,只需呼叫:
PolyBezier (hdc, apt, iCount) ;
或
PolyBezierTo (hdc, apt, iCount) ; //自己写个函数
使用现有画笔(Stock Pens)
HPEN hPen ;呼叫GetStockObject,可以获得现有画笔的句柄。例如,假设您想使用名为WHITE_PEN的现有画笔,可以如下取得画笔的句柄:
hPen = GetStockObject (WHITE_PEN) ;现在必须将画笔选进设备内容:
SelectObject (hdc, hPen) ; 或者 SelectObject (hdc, GetStockObject (WHITE_PEN)) ;-------------------------------------------------------------如果启动一个新的设备内容并呼叫hPen = SelectObject (hdc, GetStockobject (WHITE_PEN)) ;-------------------------------------------------------则设备内容中的目前画笔将为WHITE_PEN,变量hPen将会是BLACK_PEN的句柄。以后通过呼叫
SelectObject (hdc, hPen) ;画笔的建立、选择和删除1使用画笔等GDI对象时,应该遵守以下三条规则:
- 最后要删除自己建立的所有GDI对象。
- 当GDI对象正在一个有效的设备内容中使用时,不要删除它。
- 不要删除现有对象。
static HPEN hPen1, hPen2, hPen3 ;在处理WM_CREATE期间,您可以建立这三种画笔:
hPen1 = CreatePen (PS_SOLID, 1, 0) ; hPen2 = CreatePen (PS_SOLID, 3, RGB (255, 0, 0)) ; hPen3 = CreatePen (PS_DOT, 0, 0) ;在处理WM_PAINT期间,或者是在拥有一个设备内容有效句柄的任何时间里,您都可以将这三个画笔之一选进设备内容并用它来画线:
SelectObject (hdc, hPen2) ;画线函数
SelectObject (hdc, hPen1) ;其它画线函数
在处理WM_DESTROY期间,您可以删除您建立的三种画笔:
DeleteObject (hPen1) ; DeleteObject (hPen2) ; DeleteObject (hPen3) ;为此,您可能想要在每个WM_PAINT消息处理期间建立画笔,并在呼叫EndPaint之后删除它们(您可以在呼叫EndPaint之前删除它们,但是要小心,不要删除设备内容中目前选择的画笔)。----------------------------------------------2 随时建立画笔,并将CreatePen和SelectObject呼叫组合到同一个叙述中:
SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ; //删除红色画笔在画完红色虚线之后,可以删除画笔。糟了!由于没有保存画笔句柄,怎么才能删除这些画笔呢?不要紧,请记住,SelectObject将传回设备内容中上一次选择的画笔句柄。所以,您可以通过呼叫SelectObject将BLACK_PEN选进设备内容,并删除从SelectObject传回的值:DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ;或者是hPen = SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ;hPen是在取得设备内容之后第一次呼叫SelectObject,则hPen是BLACK_PEN对象的句柄。现在,可以将hPen选进设备内容,并删除所建立的画笔(第二次SelectObject呼叫传回的句柄),只要一道叙述即可:
DeleteObject (SelectObject (hdc, hPen))3 如果有一个画笔的句柄,就可以通过呼叫GetObject取得LOGPEN结构各个成员的值:
GetObject (hPen, sizeof (LOGPEN), (LPVOID) &logpen) ;如果需要目前选进设备内容的画笔句柄,可以呼叫:
hPen = GetCurrentObject (hdc, OBJ_PEN) ;
画刷下面是建立逻辑画刷的第一个函数:
hBrush = CreateSolidBrush (crColor) ; 建立影线画刷的函数为:矩形、区域和剪裁(略)hBrush = CreateHatchBrush (iHatchStyle, crColor) ; iHatchStyle参数描述影线标记的外观。图5-18显示了六种可用的影线标记风格。您也可以使用CreatePatternBrush和CreateDIBPatternBrushPt建立自己的位图画刷。
建立逻辑画刷的第五个函数包含其它四个函数:
hBrush = CreateBrushIndirect (&logbrush) ; 一旦您取得到了画刷句柄,就可以使用SelectObject将该画刷选进设备内容:SelectObject (hdc, hBrush) ;然后,您可以使用DeleteObject函数删除所建立的画刷:
DeleteObject (hBrush) ;但是,不要删除目前选进设备内容的画刷。
如果您需要取得画刷的信息,可以呼叫GetObject:
GetObject (hBrush, sizeof (LOGBRUSH), (LPVOID) &logbrush) ;case WM_PAINT:MM_ANISOTROPIC:根据需要放缩图像
hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
SetMapMode (hdc, MM_ANISOTROPIC) ;
SetWindowExtEx (hdc, 1, 1, NULL) ;
SetViewportExtEx (hdc, cxChar, cyChar, NULL) ;//没有必要去深入的东西