第十四章 位图和Bitblt(GDI 位图对象2)
2011-04-22 01:42
363 查看
程序14-6 HELLOBIT
程序从呼叫GetTextExtentPoint32确定字符串的图素尺寸开始。这些尺寸将成为与视讯显示兼容的位图尺寸。当此位图被选进内存设备 内容(也与视讯显示兼容)后,再呼叫TextOut将文字显示在位图上。内存设备内容在程序执行期间保留。在处理WM_DESTROY信息期间, HELLOBIT删除了位图和内存设备内容。
HELLOBIT中的一条菜单选项允许您显示位图尺寸,此尺寸或者是显示区域中水平和垂直方向平铺的实际尺寸,或者是缩放成显示区域大小的尺寸,如图14-4所示。正与您所见到的一样,这不是显示大尺寸字符的好方法!它只是小字体的放大版,并带有放大时产生的锯齿线。
您可能想知道一个程序,例如HELLOBIT,是否需要处理WM_DISPLAYCHANGE消息。只要使用者(或者其它应用程序)修改了视讯显示 大小或者颜色深度,应用程序就接收到此讯息。其中颜色深度的改变会导致内存设备内容和视讯设备内容不兼容。但这并不会发生,因为当显示模式修改后, Windows自动修改了内存设备内容的颜色分辨率。选进内存设备内容的位图仍然保持原样,但不会造成任何问题。
阴影位图
在内存设备内容绘图(也就是位图)的技术是执行「阴影位图(shadow bitmap)」的关键。此位图包含窗口显示区域中显示的所有内容。这样,对WM_PAINT消息的处理就简化到简单的BitBlt。
阴影位图在绘画程序中最有用。程序14-7所示的SKETCH程序并不是一个最完美的绘画程序,但它是一个开始。
要想在SKETCH中画线,请按下鼠标左键并拖动鼠标。要擦掉画过的东西(更确切地说,是画白线),请按下鼠标右键并拖动鼠标。要清空整个窗口, 请…结束程序,然后重新加载,一切从头再来。图14-5中显示的SKETCH程序图样表达了对频果公司的麦金塔计算机早期广告的敬意。
此阴影位图应多大?在本程序中,它应该大到能包含最大化窗口的整个显示区域。这一问题很容易根据GetSystemMetrics信息计算得出,但 如果使用者修改了显示设定后再显示,进而扩大了最大化时窗口的尺寸,这时将发生什么呢?SKETCH程序在EnumDisplaySettings函数的 帮助下解决了此问题。此函数使用DEVMODE结构来传回全部有效视讯显示模式的信息。第一次呼叫此函数时,应将EnumDisplaySettings 的第二参数设为0,以后每次呼叫此值都增加。EnumDisplaySettings传回FALSE时完成。
与此同时,SKETCH将建立一个阴影位图,它比目前视讯显示模式的表面还多四倍,而且需要几兆字节的内存。由于如此,SKETCH将检查位图是否建立成功了,如果没有建立,就从WM_CREATE传回-1,以表示错误。
在WM_MOUSEMOVE消息处理期间,按下鼠标左键或者右键,并在内存设备内容和显示区域设备内容中画线时,SKETCH拦截鼠标。如果画线方式更复杂的话,您可能想在一个函数中实作,程序将呼叫此函数两次-一次画在视讯设备内容上,一次画在内存设备内容上。
下面是一个有趣的实验:使SKETCH窗口小于全画面尺寸。随着鼠标左键的按下,将鼠标拖出窗口的右下角。因为SKETCH拦截鼠标,所以它继续接收并处理WM_MOUSEMOVE消息。现在扩大窗口,您将看到阴影位图包含您在SKETCH窗口外所画的内容。
在菜单中使用位图
您也可以用位图在菜单上显示选项。如果您联想起菜单中文件夹、剪贴簿和资源回收筒的图片,那么不要再想那些图片了。您应该考虑一下,菜单上显示位图对画图程序用途有多大,想象一下在菜单中使用不同字体和字体大小、线宽、阴影图案以及颜色。
GRAFMENU是展示图形菜单选项的范例程序。此程序顶层菜单如图14-6所示。放大的字母来自于40×16图素的单色位图文件,该文件在Visual C++ Developer Studio建立。从菜单上选择「FONT」将弹出三个选择项-「Courier New」、「 Arial」和「Times New Roman」。它们是标准的Windows TrueType字体,并且每一个都按其相关的字体显示,如图14-7所示。这些位图在程序中用内存设备内容建立。
最后,在拉下系统菜单时,您将获得一些「辅助」信息,用「HELP」表示了新使用者的在线求助项目(参见图14-8)。此64×64图素的单色位图是在Developer Studio中建立的。
GRAFMENU程序,包括四个Developer Studio中建立的位图,如程序14-8所示。
程序14-8 GRAFMENU
HELLOBIT.C /*----------------------------------------------------------------------- HELLOBIT.C -- Bitmap Demonstration (c) Charles Petzold, 1998 -------------------------------------------------------------------------*/ #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName [] = TEXT ("HelloBit") ; 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 = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("HelloBit"), 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 HBITMAP hBitmap ; static HDC hdcMem ; static int cxBitmap, cyBitmap, cxClient, cyClient, iSize = IDM_BIG ; static TCHAR * szText = TEXT (" Hello, world! ") ; HDC hdc ; HMENU hMenu ; int x, y ; PAINTSTRUCT ps ; SIZE size ; switch (message) { case WM_CREATE: hdc = GetDC (hwnd) ; hdcMem = CreateCompatibleDC (hdc) ; GetTextExtentPoint32 (hdc, szText, lstrlen (szText), &size) ; cxBitmap = size.cx ; cyBitmap = size.cy ; hBitmap = CreateCompatibleBitmap (hdc, cxBitmap, cyBitmap) ; ReleaseDC (hwnd, hdc) ; SelectObject (hdcMem, hBitmap) ; TextOut (hdcMem, 0, 0, szText, lstrlen (szText)) ; return 0 ; case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_COMMAND: hMenu = GetMenu (hwnd) ; switch (LOWORD (wParam)) { case IDM_BIG: case IDM_SMALL: CheckMenuItem (hMenu, iSize, MF_UNCHECKED) ; iSize = LOWORD (wParam) ; CheckMenuItem (hMenu, iSize, MF_CHECKED) ; InvalidateRect (hwnd, NULL, TRUE) ; break ; } return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; switch (iSize) { case IDM_BIG: StretchBlt (hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY) ; break ; case IDM_SMALL: for (y = 0 ; y < cyClient ; y += cyBitmap) for (x = 0 ; x < cxClient ; x += cxBitmap) { BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY) ; } break ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: DeleteDC (hdcMem) ; DeleteObject (hBitmap) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
HELLOBIT.RC (摘录) //Microsoft Developer Studio generated resource script. #include "resource.h" #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// // Menu HELLOBIT MENU DISCARDABLE BEGIN POPUP "&Size" BEGIN MENUITEM "&Big", IDM_BIG, CHECKED MENUITEM "&Small", IDM_SMALL END END
RESOURCE.H (摘录) // Microsoft Developer Studio generated include file. // Used by HelloBit.rc #define IDM_BIG 40001 #define IDM_SMALL 40002
程序从呼叫GetTextExtentPoint32确定字符串的图素尺寸开始。这些尺寸将成为与视讯显示兼容的位图尺寸。当此位图被选进内存设备 内容(也与视讯显示兼容)后,再呼叫TextOut将文字显示在位图上。内存设备内容在程序执行期间保留。在处理WM_DESTROY信息期间, HELLOBIT删除了位图和内存设备内容。
HELLOBIT中的一条菜单选项允许您显示位图尺寸,此尺寸或者是显示区域中水平和垂直方向平铺的实际尺寸,或者是缩放成显示区域大小的尺寸,如图14-4所示。正与您所见到的一样,这不是显示大尺寸字符的好方法!它只是小字体的放大版,并带有放大时产生的锯齿线。
图14-4 HELLOBIT的屏幕显示 |
阴影位图
在内存设备内容绘图(也就是位图)的技术是执行「阴影位图(shadow bitmap)」的关键。此位图包含窗口显示区域中显示的所有内容。这样,对WM_PAINT消息的处理就简化到简单的BitBlt。
阴影位图在绘画程序中最有用。程序14-7所示的SKETCH程序并不是一个最完美的绘画程序,但它是一个开始。
程序14-7 SKETCH SKETCH.C /*------------------------------------------------------------------------- SKETCH.C -- Shadow Bitmap Demonstration (c) Charles Petzold, 1998 ---------------------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName [] = TEXT ("Sketch") ; 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 ("Sketch"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; if (hwnd == NULL) { MessageBox ( NULL, TEXT ("Not enough memory to create bitmap!"), szAppName, MB_ICONERROR) ; return 0 ; } ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } void GetLargestDisplayMode (int * pcxBitmap, int * pcyBitmap) { DEVMODE devmode ; int iModeNum = 0 ; * pcxBitmap = * pcyBitmap = 0 ; ZeroMemory (&devmode, sizeof (DEVMODE)) ; devmode.dmSize = sizeof (DEVMODE) ; while (EnumDisplaySettings (NULL, iModeNum++, &devmode)) { * pcxBitmap = max (* pcxBitmap, (int) devmode.dmPelsWidth) ; * pcyBitmap = max (* pcyBitmap, (int) devmode.dmPelsHeight) ; } } LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) { static BOOL fLeftButtonDown, fRightButtonDown ; static HBITMAP hBitmap ; static HDC hdcMem ; static int cxBitmap, cyBitmap, cxClient, cyClient, xMouse, yMouse ; HDC hdc ; PAINTSTRUCT ps ; switch (message) { case WM_CREATE: GetLargestDisplayMode (&cxBitmap, &cyBitmap) ; hdc = GetDC (hwnd) ; hBitmap = CreateCompatibleBitmap (hdc, cxBitmap, cyBitmap) ; hdcMem = CreateCompatibleDC (hdc) ; ReleaseDC (hwnd, hdc) ; if (!hBitmap) // no memory for bitmap { DeleteDC (hdcMem) ; return -1 ; } SelectObject (hdcMem, hBitmap) ; PatBlt (hdcMem, 0, 0, cxBitmap, cyBitmap, WHITENESS) ; return 0 ; case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_LBUTTONDOWN: if (!fRightButtonDown) SetCapture (hwnd) ; xMouse = LOWORD (lParam) ; yMouse = HIWORD (lParam) ; fLeftButtonDown = TRUE ; return 0 ; case WM_LBUTTONUP: if (fLeftButtonDown) SetCapture (NULL) ; fLeftButtonDown = FALSE ; return 0 ; case WM_RBUTTONDOWN: if (!fLeftButtonDown) SetCapture (hwnd) ; xMouse = LOWORD (lParam) ; yMouse = HIWORD (lParam) ; fRightButtonDown = TRUE ; return 0 ; case WM_RBUTTONUP: if (fRightButtonDown) SetCapture (NULL) ; fRightButtonDown = FALSE ; return 0 ; case WM_MOUSEMOVE: if (!fLeftButtonDown && !fRightButtonDown) return 0 ; hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (fLeftButtonDown ? BLACK_PEN : WHITE_PEN)) ; SelectObject (hdcMem, GetStockObject (fLeftButtonDown ? BLACK_PEN : WHITE_PEN)) ; MoveToEx (hdc, xMouse, yMouse, NULL) ; MoveToEx (hdcMem, xMouse, yMouse, NULL) ; xMouse = (short) LOWORD (lParam) ; yMouse = (short) HIWORD (lParam) ; LineTo (hdc, xMouse, yMouse) ; LineTo (hdcMem, xMouse, yMouse) ; ReleaseDC (hwnd, hdc) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; BitBlt (hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: DeleteDC (hdcMem) ; DeleteObject (hBitmap) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
要想在SKETCH中画线,请按下鼠标左键并拖动鼠标。要擦掉画过的东西(更确切地说,是画白线),请按下鼠标右键并拖动鼠标。要清空整个窗口, 请…结束程序,然后重新加载,一切从头再来。图14-5中显示的SKETCH程序图样表达了对频果公司的麦金塔计算机早期广告的敬意。
图14-5 SKETCH的屏幕显示 |
与此同时,SKETCH将建立一个阴影位图,它比目前视讯显示模式的表面还多四倍,而且需要几兆字节的内存。由于如此,SKETCH将检查位图是否建立成功了,如果没有建立,就从WM_CREATE传回-1,以表示错误。
在WM_MOUSEMOVE消息处理期间,按下鼠标左键或者右键,并在内存设备内容和显示区域设备内容中画线时,SKETCH拦截鼠标。如果画线方式更复杂的话,您可能想在一个函数中实作,程序将呼叫此函数两次-一次画在视讯设备内容上,一次画在内存设备内容上。
下面是一个有趣的实验:使SKETCH窗口小于全画面尺寸。随着鼠标左键的按下,将鼠标拖出窗口的右下角。因为SKETCH拦截鼠标,所以它继续接收并处理WM_MOUSEMOVE消息。现在扩大窗口,您将看到阴影位图包含您在SKETCH窗口外所画的内容。
在菜单中使用位图
您也可以用位图在菜单上显示选项。如果您联想起菜单中文件夹、剪贴簿和资源回收筒的图片,那么不要再想那些图片了。您应该考虑一下,菜单上显示位图对画图程序用途有多大,想象一下在菜单中使用不同字体和字体大小、线宽、阴影图案以及颜色。
GRAFMENU是展示图形菜单选项的范例程序。此程序顶层菜单如图14-6所示。放大的字母来自于40×16图素的单色位图文件,该文件在Visual C++ Developer Studio建立。从菜单上选择「FONT」将弹出三个选择项-「Courier New」、「 Arial」和「Times New Roman」。它们是标准的Windows TrueType字体,并且每一个都按其相关的字体显示,如图14-7所示。这些位图在程序中用内存设备内容建立。
图14-6 GRAFMENU程序的顶层菜单 |
图14-7 GRAFMENU程序弹出的「FONT」菜单 |
图14-8 GRAFMENU程序系统菜单 |
程序14-8 GRAFMENU
GRAFMENU.C /*---------------------------------------------------------------------------- GRAFMENU.C -- Demonstrates Bitmap Menu Items (c) Charles Petzold, 1998 -----------------------------------------------------------------------------*/ #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; void AddHelpToSys (HINSTANCE, HWND) ; HMENU CreateMyMenu (HINSTANCE) ; HBITMAP StretchBitmap (HBITMAP) ; HBITMAP GetBitmapFont (int) ; void DeleteAllBitmaps (HWND) ; TCHAR szAppName[] = TEXT ("GrafMenu") ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { 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 ("Bitmap Menu Demonstration"), 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 ; }
相关文章推荐
- 第十四章 位图和Bitblt(GDI 位图对象1)
- 第14章 位图和位块传输_14.4 GDI位图对象(2)
- 第14章 位图和位块传输_14.4 GDI位图对象(3)
- Delphi GDI对象之脱屏位图(Offscreen Bitmaps)
- Delphi GDI对象之绘制位图
- Delphi GDI对象之位图与调色板
- 【笨嘴拙舌WINDOWS】GDI对象之位图
- Delphi GDI对象之脱屏位图(Offscreen Bitmaps),也叫内存位图
- 第十四章 位图和Bitblt(位块传输)
- 第十四章 位图和Bitblt(位图入门,位图尺寸)
- Windows程序设计核心总结(GDI位图对象-2018.5.8)
- 14.4 GDI 位图对象 (II)
- Delphi GDI对象之绘制位图
- 第14章 位图和位块传输_14.4 GDI位图对象(1)
- 14.4 GDI 位图对象 (I)
- 内核对象与用户对象/GDI对象
- GDI对象的初始化
- VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)
- 【win32】day07-图形绘制/GDI绘图对象-画笔/画刷
- Windows程序设计 读书笔记 - 位图和BitBlt。