第十四章 位图和Bitblt(GDI 位图对象2)

来源:互联网 发布:田径女神走红网络 编辑:程序博客网 时间:2024/06/05 11:09
程序14-6 HELLOBIT
        
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的屏幕显示

您可能想知道一个程序,例如HELLOBIT,是否需要处理WM_DISPLAYCHANGE消息。只要使用者(或者其它应用程序)修改了视讯显示 大小或者颜色深度,应用程序就接收到此讯息。其中颜色深度的改变会导致内存设备内容和视讯设备内容不兼容。但这并不会发生,因为当显示模式修改后, Windows自动修改了内存设备内容的颜色分辨率。选进内存设备内容的位图仍然保持原样,但不会造成任何问题。

阴影位图

在内存设备内容绘图(也就是位图)的技术是执行「阴影位图(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的屏幕显示

此阴影位图应多大?在本程序中,它应该大到能包含最大化窗口的整个显示区域。这一问题很容易根据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所示。这些位图在程序中用内存设备内容建立。

 

 

图14-6 GRAFMENU程序的顶层菜单

 

 

图14-7 GRAFMENU程序弹出的「FONT」菜单

最后,在拉下系统菜单时,您将获得一些「辅助」信息,用「HELP」表示了新使用者的在线求助项目(参见图14-8)。此64×64图素的单色位图是在Developer Studio中建立的。

 

 

图14-8 GRAFMENU程序系统菜单

 

GRAFMENU程序,包括四个Developer Studio中建立的位图,如程序14-8所示。

程序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 ;

}


原创粉丝点击