📄 5. 图形基础.txt
字号:
常数25.4用于把英寸转变为毫米。
这看起来是种不合逻辑的退步。毕竟,视讯显示器是可以用尺以毫米为单位的大小(至少是近似的)衡量的。但是Windows 98并不关心这个大小。相反,它以使用者选择的显示图素大小和系统字体大小为基础计算以毫米为单位的显示大小。更改显示的图素大小并根据GetDeviceCaps更改度量大小。这有什么意义呢?
这非常有意义。假定有一个17英寸的显示器。实际的显示大小大约是12英寸乘9英寸。假定在最小要求的640×480图素大小下执行Windows。这意味着实际的分辨率是每英寸53点。10点字体(在纸上便于阅读)在屏幕上从A的顶部到q的底部只有7个图素。这样的字体很难看而且不易读。(可问问那些在旧的Color Graphics Adapter上执行Windows的人们。)
现在,把您的计算机接上视讯投影机。投影的视讯显示器是4英尺宽,3英尺高。同样的640×480图素大小现在是大约每英寸13点的分辨率。在这种条件下试图显示10点的字体是很可笑的。
10点字体在视讯显示器上应是可读的,因为它在打印时是肯定可读的。所以10点字体就成为一个重要的参照。当Windows应用程序确保10点屏幕字体为平均大小时,就能够使用8点字体显示较小的文字(仍可读),或用大于10点的字体显示较大的文字。因而,视频分辨率(以每英寸的点数为单位)由10点字体的图素大小来确定是很有意义的。
然而,在Windows NT中,用老的方法定义HORZSIZE和VERTSIZE值。这种方法与Windows的16位版本一致。HORZRES和VERTRES值仍然表示水平和垂直图素的数值,LOGPIXELSX和LOGPIXELSY仍然与在「控制台」的「显示器」程序中选择的字体有关。在Windows 98中,LOGPIXELSX和LOGPIXELSY的典型值是96和120 dpi,这取决于您选择的是小字体还是大字体。
在Windows NT中的区别是HORZSIZE和VERTSIZE值固定表示标准显示器大小。对于普通的显示卡,取得的HORZSIZE和VERTSIZE值分别是320和240毫米。这些值是相同的,与选择的图素大小无关。因此,这些值与用HORZRES、VERTRES、LOGPIXELSX和LOGPIXELSY索引从GetDeviceCaps中得到的值不同。然而,可以用前面的公式计算在Windows 98下的HORZSIZE和VERTSIZE值。
如果程序需要实际的视讯显示大小该怎么办?也许最好的解决方法是用对话框让使用者输入它们。
最后,来自GetDeviceCaps的另三个值与视讯大小有关。ASPECTX、ASPECTY和ASPECTXY值是每一个图素的相对宽度、高度和对角线大小,四舍五入到整数。对于正方形图素,ASPECTX和ASPECTY值相同。无论如何,ASPECTXY值应等于ASPECTX与ASPECTY平方和的平方根,就像直角三角形一样。
关于色彩
如果视讯显示卡仅显示黑色图素和白色图素,则每个图素只需要内存中的一位。彩色显示器中每个图素需要多个位。位数越多,色彩越多,或者更具体地说,可以同时显示的不同色彩的数目等于2的位数次方。
「Full-Color」视讯显示器的分辨率是每个图素24位-8位红色、8位绿色以及8位蓝色。红、绿、蓝即「色光三原色」。混合这三种基本颜色可以生成许多其它的颜色,您通过放大镜看显示屏,就可以看出来。
「High-Color」显示分辨率是每个图素16位-5位红色、6位绿色以及5位蓝色。绿色多一位是因为人眼对绿色更敏感一些。
显示256种颜色的显示卡每个图素需要8位。然而,这些8位的值一般由定义实际颜色的调色盘组织的。我会在 第十六章详细地讨论它们。
最后,显示16种颜色的显示卡每个图素需要4位。这16种颜色一般固定分为暗的或亮的红、黑、蓝、青、紫、黄、两种灰色。这16种颜色要回溯到老式的IBM CGA。
祇有在某些怪异的程序中才需要知道视讯显示卡上的内存是如何组织的,但是GetDeviceCaps使程序写作者可以知道显示卡的储存组织以及它能够表示的色彩数目,下面的呼叫传回色彩平面的数目:
iPlanes = GetDeviceCaps (hdc, PLANES) ;
下面的呼叫传回每个图素的色彩位数:
iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL) ;
大多数彩色图形显示设备使用多个色彩平面或每图素有多个色彩位的设计,但是不能同时一齐使用这两种方式;换句话说,这两个呼叫必有一个传回1。显示卡能够表示的色彩数可以用如下公式来计算:
iColors = 1 << (iPlanes * iBitsPixel) ;
这个值与用NUMCOLORS参数得到的色彩数值可能一样,也可能不一样:
iColors = GetDeviceCaps (hdc, NUMCOLORS) ;
我提到过,256色的显示卡使用色彩调色盘。在那种情况下,以NUMCOLORS为参数时,GetDeviceCaps传回由Windows保留的色彩数,值为20,剩余的236种颜色可以由Windows程序用调色盘管理器设定。对于High-Color和True-Color显示分辨率,带有NUMCOLORS参数的GetDeviceCaps通常传回-1,这样就无法得到需要的信息,因此应该使用前面所示的带有PLANES和BITSPIXEL值的iColors公式。
在大多数GDI函数呼叫中,使用COLORREF值(只是一个32位的无正负号长整数)来表示一种色彩。COLORREF值按照红、绿和蓝色的亮度指定了一种颜色,通常叫做「RGB色彩」 。32位的COLORREF值的设定如图5-4所示。
图5-4 32位COLORREF值
注意最前面是标为0的8个位,并且每种原色都指定为一个8位的值。理论上,COLORREF可以指定二的二十四次方种或一千六百万种色彩。
这个无正负号长整数常常称为一个「RGB色彩」。Windows表头文件WINGDI.H提供了几种使用RGB色彩值的宏。RGB宏要求三个参数分别代表红、绿和蓝值,然后将它们组合为一个无正负号长整数:
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r) | \
((WORD)((BYTE)(g)) << 8)) | \
(((DWORD)(BYTE)(b)) << 16)))
注意三个参数的顺序是红、绿和蓝。因此,值:
RGB (255, 255, 0)
是0x0000FFFF,或黄色(红色和绿色的合成)。当所有三个参数设定为0时,色彩为黑色;当所有参数设定为255时,色彩为白色。GetRValue、GetGValue和GetBValue宏从COLORREF值中抽取出原色值。当您在使用传回RGB色彩值的Windows函数时,这些宏有时会很方便。
在16色或256色显示卡上,Windows可以使用「混色」来模拟设备能够显示的颜色之外的色彩。混色利用了由多种色彩的图素组成的图素图案。可以呼叫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) ;
这些属性在改变之前一直有效。
CS_OWNDC风格只影响GetDC和BeginPaint获得的设备内容,不影响其它函数(如GetWindowDC)获得的设备内容。以前不提倡使用CS_OWNDC风格,因为它需要内存;现在,在处理大量图形的Windows NT应用程序中,它可以提高性能。即使用了CS_OWNDC,您仍然应该在退出窗口消息处理程序之前释放设备内容。
某些情况下,您可能想改变某些设备内容属性,用改变后的属性进行绘图,然后恢复原来的设备内容。要简化这一过程,可以通过如下呼叫来保存设备内容的状态:
idSaved = SaveDC (hdc) ;
现在,可以改变一些属性,在想要回到呼叫SaveDC前存在的设备内容时,呼叫:
RestoreDC (hdc, idSaved) ;
您可以在呼叫RestoreDC之前呼叫SaveDC数次。
大多数程序写作者以不同的方式使用SaveDC和RestoreDC。然而,更像汇编语言中的PUSH和POP指令,当您呼叫SaveDC时,不需要保存传回值:
SaveDC (hdc) ;
然后,您可以更改某些属性并再次呼叫SaveDC。要将设备内容恢复到一个已经保存的状态,呼叫:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -