14.4.7 在位图上绘图

(1)在内存设备环境中绘图(与真实DC不同的是,内存DC的显示表面是个位图)

(2)GetTextExtentPoint32函数:用于确定文本字符串的像素大小。(此大小就是与视频显示兼容的位图的尺寸)。

参数

说明

hdc

设备环境句柄

lpString

文本字符串,如szText

cbString

文本字符串中字符的个数。如lstrlen(szText)

lpSize

指向一个结构体,用来存放结果

(3)当显示器的颜色深度和大小改变时,windows会自动改变内存设备环境的彩色分辩率。也就是内存设备环境与视频设备环境仍然会保持兼容。所以在WM_DISPLAYCHANGE消息中不用去关心这个问题。

【HelloBit程序】

效果图

  

/*------------------------------------------------------------
HELLOBIT.C -- Bitmap Demostration
(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;
WNDCLASSEX wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hIconSm = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = szAppName;
wndclass.lpszClassName = szAppName;
if (!RegisterClassEx(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("HelloBit"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
static HDC hdcMem;
PAINTSTRUCT ps;
static int cxBitmap, cyBitmap, cxClient, cyClient, iSize = IDM_BIG;
static HBITMAP hBitmap;
static TCHAR* szText = TEXT("Hello,World!");
SIZE size;
HMENU hMenu;
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, , , szText, lstrlen(szText));
return ;
case WM_COMMAND:
hMenu = GetMenu(hwnd);
switch (LOWORD(wParam)) //菜单ID
{
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 ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); switch (iSize)
{
case IDM_BIG:
StretchBlt(hdc, , , cxClient, cyClient,
hdcMem, , , cxBitmap, cyBitmap, SRCCOPY);
break;
case IDM_SMALL:
for (int y = ; y < cyClient; y += cyBitmap)
for (int x = ; x < cxClient; x += cxBitmap)
{
BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMem, , , SRCCOPY);
}
break;
} EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
DeleteDC(hdcMem);
DeleteObject(hBitmap);
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by HelloBit.rc
//
#define IDM_BIG 40001
#define IDM_SMALL 40002 // Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40003
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

//HelloBit.rc

//Microsoft Developer Studio generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32 #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Menu
// HELLOBIT MENU DISCARDABLE
BEGIN
POPUP "&Size"
BEGIN
MENUITEM "&Big", IDM_BIG, CHECKED
MENUITEM "&Small", IDM_SMALL
END
END #endif // English (U.S.) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
14.4.8 阴影位图

(1)EnumDisplaySettings:该函数用来获取显示设备的参数信息,要想获取所有显示设备的所有信息,就要循环使用该函数。

参数

说明

lpszDeviceName

设备的名称,以NULL结尾的设备名称字符串。

当NULL表示当前使用的显示设备

iModeNum

①获取设备类型信息。该值可以是索引号或也可以使下值之一:

ENUM_CURRENT_SETTINGS:获取当前显示设备的配置

ENUM_REGISTRY_SETTINGS:注册表中所有当前存储的显示设备的配置。

②如果从0开始,要获得所有显示设备的物理模式,就要循环调用EnumDisplaySettings函数,直到返回返回值为FALSE。

③当以iModeNum为0而调用函数时,操作系统将初始化和填充相关显示设备信息。

④当以iModeNum为非0而调用函数时,操作系统将返回最后一次填充的信息。

lpDevMode

该参数是一个结构体用来接收物理模式的信息参数,在使用此结构体前要先初始化该结构体。函数会获取到以下五个字段的值:dmBitsPerpel、dmPelsWidth、dmPelsHeight(设备宽度和高度,以像素为单位,不会随分辨率而改变,可以用来测量显示器的大小!)、dmDisplayFlags、dmDisplayFrequency。

(2)【Sketch程序】——先将图绘到内存设备,再绘到显示设备的DC环境。
效果图

/*------------------------------------------------------------
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;
WNDCLASSEX wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hIconSm = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClassEx(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("Sketch"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图
//如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况.
if (hwnd == NULL)
{
MessageBox(NULL, TEXT("Not enough memory to create bitmap!"),
szAppName, MB_ICONERROR);
return ;
}
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//获得最大可显示的位图
void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap)
{
DEVMODE devmode;
int iModeNum = ;
*pcxBitmap = *pcyBitmap = ;
ZeroMemory(&devmode, sizeof(DEVMODE));
devmode.dmSize = sizeof(DEVMODE); //这个要记得设置!
//EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。
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:
hdc = GetDC(hwnd);
GetLargestDisplayMode(&cxBitmap, &cyBitmap);
hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
hdcMem = CreateCompatibleDC(hdc);
ReleaseDC(hwnd, hdc);
SelectObject(hdcMem, hBitmap);
PatBlt(hdcMem, , , cxBitmap, cyBitmap, WHITENESS); //给显示表面涂上白色
return ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
//左键用来画图(用黑色画)
case WM_LBUTTONDOWN:
if (!fRightButtonDown) //因为当右键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
SetCapture(hwnd);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
fLeftButtonDown = TRUE;
return ;
case WM_LBUTTONUP:
if (fLeftButtonDown)
//SetCapture(NULL);
ReleaseCapture();//课本用SetCapture(NULL); fLeftButtonDown = FALSE;
return ; //右键用来擦图(用白色绘图,等于在擦图)
case WM_RBUTTONDOWN:
if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
SetCapture(hwnd);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
fRightButtonDown = TRUE;
return ;
case WM_RBUTTONUP:
if (fRightButtonDown)
//SetCapture(NULL);
ReleaseCapture();
fRightButtonDown = FALSE;
return ;
case WM_MOUSEMOVE:
//己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键,
//重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture
//了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过,
//所以仍会执行后面的代码
if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出
return ;
hdc = GetDC(hwnd);
//左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔
SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
//同时在屏幕dc与内存dc中绘图
MoveToEx(hdc, xMouse, yMouse, NULL);
MoveToEx(hdcMem, xMouse, yMouse, NULL);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
LineTo(hdc, xMouse, yMouse);
LineTo(hdcMem, xMouse, yMouse);
ReleaseDC(hwnd, hdc);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); //这里的图形来自内存设备环境
BitBlt(hdc, , , cxClient, cyClient,
hdcMem, , , SRCCOPY); EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

14.4.9 在菜单中使用位图

(1)GetMenuItemInfo(hMenu,uMenuItem,fByPosition,lpmii);

①uMenuItem:菜单ID或位置索引(看fByPositon是否是TRUE)

②lpmii,指一个MENUITEMINFO结构体

MENUITEMINFO结构体

字段

说明

cbSize

结构的大小(字节)

fMask

MIIM_CHECKMARKS:获取或设置hbmpChecked和hbmpUnchecked成员

MIIM_DATA:获取或设置dwItemData成员

MIIM_ID :获取或设置wID成员

MIIM_STATE: 获取或设置fState成员

MIIM_SUBMENU: 获取或设置hSubMenu成员

MIIM_TYPE :获取或设置fType和dwTypeData成员

fType

菜单项类型

MFT_BITMAP:使用一个位图显示菜单项.dwTypeData低位字是该位图的句柄.并且cch被忽视

MFT_MENUBARBREAK:放置菜单项在新行上(适用于菜单栏)或在新列内

MFT_SEPARATOR:指定那个菜单项是一个分隔条

MFT_STRING:用一个文本字符串显示菜单项.dwTypeData成员指示一个以NULL结尾的字符串

MFT_RADIOCHECK:如果hbmpChecked成员是NULL,显示选中的菜单项使用一个单选按钮来代替一个复选标记

fState

菜单项的状态

MFS_CHECKED:复选的菜单项.至于更多关于菜单项选中的信息,看hbmpChecked成员

MFS_DISABLED:菜单项无效并变灰使得它不能被选择.等效于MFS_GRAYED

MFS_ENABLED: 激活菜单项使它可以被选择

MFS_GRAYED:菜单项无效并变灰使得它不能被选择.等效于MFS_DISABLED

MFS_HILITE:菜单项高亮显示

MFS_UNCHECKED:取消复选菜单项

MFS_UNHILITE:移除菜单项的高亮显示,这是默认状态

wID

菜单项ID, 只有在设置了fMask的MIIM_ID时才能使用

hSubMenu

菜单项相关联的下拉菜单或子菜单的的句柄。如果菜单项不是一个打开的下拉菜单或子菜单,那这个成员是NULL, 该项只有在设置了fMask的MIIM_SUBMENU时才能使用

hbmpChecked

菜单项被选中时显示在一侧的位图的句柄。

hbmpUnchecked

菜单项没有被选中时显示在一侧的位图的句柄

dwItemData

应用程序定义的菜单项相关联的值,该项只有在设置了fMask的MIIM_DATA时才能使用

dwTypeData

菜单项的内容,它的具体意义依赖于fTYPE值,并且它只能在fMask设置了MIIM_TYPE标记时才能被使用;

要获取一个MFT_STRING类型的菜单项,首先要得到该字符串的大小,通过设置MENUITEMINFO结构的dwTypeData值为空并调用函数GetMenuItemInfo得到的cch值就是字符串的大小,然后分配一个字符串大小的缓冲区,把指向缓冲区的指针存赋给dwTypeData并再次调用GetMenuItemInfo函数用字符串来填充缓冲区。

如果获取其它类型的菜单项,GetMenuItemInfo函数会赋给dwTypeData一个类型由fType成员指定的值。当使用SetMenuItemInfo函数时,dwTypeData必须包含一个类型由fType成员指定的值,该项只有在设置了fMask成员的MIIM_STRING标记时才能使用。

cch

当检索一个MFT_STRING类型菜单项的信息时,为菜单项文本(TCHAR)的长度

hbmpItem

菜单项上显示位图的句柄,它可能是以下标记中的一个,该项只有在设置了fMask成员的MIIM_BITMAP标记时才能使用。

HBMMENU_CALLBACK:一个由拥有该菜单的窗口绘制的位图

HBMMENU_MBAR_CLOSE:菜单栏的关闭按钮

HBMMENU_MBAR_MINIMIZE:菜单栏的最小化按钮

HBMMENU_MBAR_RESTORE:菜单栏的还原按钮

……

(2)使用位图做菜单项时的注意事项

①Windows会调整菜单栏高度来适应最高的位图,其他位图(包括文字)和菜单上沿顶端对齐。

②如果把一个位图放入顶级菜单,那么SM_CYMENU调用和GetSystemMetrics得到的菜单高度值不同。

③当菜单包含文本时,Windows会自动添加键盘接口。但一旦加入位图,就没有键盘接口了。可以使用WM_MENUCHAR消息,在按下Alt键以及没有映射到任何菜单项的某个字母键时,Windows会向应用程序发送一个WM_MENUCHAR消息。wParam为被按下键的ASCII码。如果和某个菜单项对应,就要向Windows返回一个双字(即return的返回值),并将高位字设为2(即MNC_EXECUTE),将低位字设成对应菜单项的索引。Windows会向窗口发送WM_COMMAND消息。

【GrafMenu程序】
效果图

/*------------------------------------------------------------
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;
WNDCLASSEX wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, szAppName);
wndclass.hIconSm = LoadIcon(hInstance, szAppName);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClassEx(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return ;
} hwnd = CreateWindow(szAppName, // window class name
TEXT("Sketch"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图
//如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况.
if (hwnd == NULL)
{
MessageBox(NULL, TEXT("Not enough memory to create bitmap!"),
szAppName, MB_ICONERROR);
return ;
}
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd); while (GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//获得最大可显示的位图
void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap)
{
DEVMODE devmode;
int iModeNum = ;
*pcxBitmap = *pcyBitmap = ;
ZeroMemory(&devmode, sizeof(DEVMODE));
devmode.dmSize = sizeof(DEVMODE); //这个要记得设置!
//EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。
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:
hdc = GetDC(hwnd);
GetLargestDisplayMode(&cxBitmap, &cyBitmap);
hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
hdcMem = CreateCompatibleDC(hdc);
ReleaseDC(hwnd, hdc);
SelectObject(hdcMem, hBitmap);
PatBlt(hdcMem, , , cxBitmap, cyBitmap, WHITENESS); //给显示表面涂上白色
return ;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return ;
//左键用来画图(用黑色画)
case WM_LBUTTONDOWN:
if (!fRightButtonDown) //因为当右键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
SetCapture(hwnd);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
fLeftButtonDown = TRUE;
return ;
case WM_LBUTTONUP:
if (fLeftButtonDown)
//SetCapture(NULL);
ReleaseCapture();//课本用SetCapture(NULL); fLeftButtonDown = FALSE;
return ; //右键用来擦图(用白色绘图,等于在擦图)
case WM_RBUTTONDOWN:
if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
SetCapture(hwnd);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
fRightButtonDown = TRUE;
return ;
case WM_RBUTTONUP:
if (fRightButtonDown)
//SetCapture(NULL);
ReleaseCapture();
fRightButtonDown = FALSE;
return ;
case WM_MOUSEMOVE:
//己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键,
//重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture
//了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过,
//所以仍会执行后面的代码
if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出
return ;
hdc = GetDC(hwnd);
//左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔
SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
//同时在屏幕dc与内存dc中绘图
MoveToEx(hdc, xMouse, yMouse, NULL);
MoveToEx(hdcMem, xMouse, yMouse, NULL);
xMouse = LOWORD(lParam);
yMouse = HIWORD(lParam);
LineTo(hdc, xMouse, yMouse);
LineTo(hdcMem, xMouse, yMouse);
ReleaseDC(hwnd, hdc);
return ;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); //这里的图形来自内存设备环境
BitBlt(hdc, , , cxClient, cyClient,
hdcMem, , , SRCCOPY); EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by GrafMenu.rc
//
#define IDM_FONT_COUR 101
#define IDM_FONT_ARIAL 102
#define IDM_FONT_TIMES 103
#define IDM_HELP 104
#define IDM_EDIT_UNDO 40005
#define IDM_EDIT_CUT 40006
#define IDM_EDIT_COPY 40007
#define IDM_EDIT_PASTE 40008
#define IDM_EDIT_CLEAR 40009
#define IDM_FILE_NEW 40010
#define IDM_FILE_OPEN 40011
#define IDM_FILE_SAVE 40012
#define IDM_FILE_SAVE_AS 40013 // Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 108
#define _APS_NEXT_COMMAND_VALUE 40014
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 105
#endif
#endif

//GrafMenu.rc

//Microsoft Developer Studio generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32 #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Menu
// MENUFILE MENU DISCARDABLE
BEGIN
MENUITEM "&New", IDM_FILE_NEW
MENUITEM "&Open...", IDM_FILE_OPEN
MENUITEM "&Save", IDM_FILE_SAVE
MENUITEM "Save &As...", IDM_FILE_SAVE_AS
END MENUEDIT MENU DISCARDABLE
BEGIN
MENUITEM "&Undo", IDM_EDIT_UNDO
MENUITEM SEPARATOR
MENUITEM "Cu&t", IDM_EDIT_CUT
MENUITEM "&Copy", IDM_EDIT_COPY
MENUITEM "&Paste", IDM_EDIT_PASTE
MENUITEM "De&lete", IDM_EDIT_CLEAR
END /////////////////////////////////////////////////////////////////////////////
//
// Bitmap
// BITMAPFONT BITMAP DISCARDABLE "Fontlabl.bmp"
BITMAPHELP BITMAP DISCARDABLE "Bighelp.bmp"
BITMAPEDIT BITMAP DISCARDABLE "Editlabl.bmp"
BITMAPFILE BITMAP DISCARDABLE "Filelabl.bmp"
#endif // English (U.S.) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

第14章 位图和位块传输_14.4 GDI位图对象(2)的更多相关文章

  1. 第14章 位图和位块传输_14.4 GDI位图对象(3)

    14.4.10 非矩形的位图图像 (1)“掩码”位图——单色位图,要显示的像素对应的掩码置1,不显示置0(2)光栅操作(点这里,见此文分析) (3)MaskBlt函数 ①MaskBlt(hdcDest ...

  2. 第14章 位图和位块传输_14.4 GDI位图对象(1)

    14.4.1 创建DDB (1)创建 HBITMAP= CreateBitmap(cx,cy,cPlanes,cBitsPixel,lpBits); 参数 说明 cx,cy 指定位图宽度和高度,单位为 ...

  3. ASM:《X86汇编语言-从实模式到保护模式》第14章:保护模式下的特权保护和任务概述

    ★PART1:32位保护模式下任务的隔离和特权级保护  这一章是全书的重点之一,这一张必须要理解特权级(包括CPL,RPL和DPL的含义)是什么,调用门的使用,还有LDT和TSS的工作原理(15章着重 ...

  4. 【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第56章       STM32H7的DMA2D应用之刷色块, ...

  5. Linux就这个范儿 第14章 身在江湖

    Linux就这个范儿 第14章 身在江湖 “有人的地方就有江湖”,如今的计算机世界就像一个“江湖”.且不说冠希哥有多么无奈,把微博当QQ的局长有多么失败,就说如此平凡的你我什么时候就成了任人摆布的羔羊 ...

  6. 【RL-TCPnet网络教程】第14章 RL-TCPnet之TCP客户端

    第14章      RL-TCPnet之TCP客户端 本章节为大家讲解RL-TCPnet的TCP客户端实现,学习本章节前,务必要优先学习第12章TCP传输控制协议基础知识.有了这些基础知识之后,再搞本 ...

  7. 【二代示波器教程】第14章 uCOS-III操作系统版本二代示波器实现

    第14章      uCOS-III操作系统版本二代示波器实现 本章教程为大家讲解uCOS-III操作系统版本的二代示波器实现.主要讲解RTOS设计框架,即各个任务实现的功能,任务间的通信方案选择,任 ...

  8. Java核心技术卷一基础知识-第14章-多线程-读书笔记

    第 14 章 多线程 本章内容: * 什么是线程 * 中断线程 * 线程状态 * 线程属性 * 同步 * 阻塞队列 * 线程安全的集合 * Collable与Future * 执行器 * 同步器 * ...

  9. MySQL性能调优与架构设计——第 14 章 可扩展性设计之数据切分

    第 14 章 可扩展性设计之数据切分 前言 通过 MySQL Replication 功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时候,我 ...

随机推荐

  1. go语言 安装版 Windows7安装截图

    这个比较简单的 一路next. 查看:解压版安装go. //http://www.cnblogs.com/osfipin/

  2. go语言最新版本 下载地址

    国内官方网站无法打开.放在了百度云中,定期会更新: 链接:http://pan.baidu.com/s/1dD59duh 密码:46ek 备用地址:http://pan.baidu.com/s/1hq ...

  3. iOS UISlider的使用

    UISlider是一个方便的控件,让用户能够以可视化的方式设置指定范围内的值. 和按钮一样,滑块也能响应事件,还可像文本框一样被读取.如果希望用户对滑块的调整立刻影响应用程序,则需要让他触发操作. 下 ...

  4. js基本算法:冒泡排序,二分查找

    知识扩充: 时间复杂度:算法的时间复杂度是一个函数,描述了算法的运行时间.时间复杂度越低,效率越高. 自我理解:一个算法,运行了几次时间复杂度就为多少,如运行了n次,则时间复杂度为O(n). 1.冒泡 ...

  5. linux命令学习使用记录

    1.文件批量重命名:把所有.xml文件重命名.txt,第一个参数为文件名中字符串,第二个参数为替换后文件名,第三个为当前目录文件列表 rename .xml .txt *.xml 2.解压不显示过程: ...

  6. 【Openlayers3】在地图上添加highcharts图表

    今天试用了一下ol3,效果很是不错! ol3中有个ol.Overlay,使用这个类我们可以在地图中添加各种html要素. 下面我们在地图中添加一个饼图 html中添加一个div容器: <div ...

  7. 2016春招Android开发实习生(网易传媒)笔试

    一.单选题 1.下列不属于网络层协议的为 TCP IP IPX ICMP 2.关于activity的状态恢复,错误的是 onSaveInstanceState中,activity会自动收集恢复view ...

  8. iOS runtime的理解和应用

    项目中经常会有一些的功能模块用到runtime,最近也在学习它.对于要不要阅读runtime的源码,我觉得仅仅是处理正常的开发,那真的没有必要,只要把常用的一些函数看下和原理理解下就可以了. 但是如果 ...

  9. Mark一下,Android ListView的上下间隙

    困扰很久的问题,怎么给ListView上下加padding,可以跟随滚动的那种 android:paddingTop="10dp" android:paddingBottom=&q ...

  10. Xcode 8.1 : Unable to read from device

    今天升级了Xcode 8.1,准备在iOS10.0.2的iPhone 6 Plus上调试,提示:Unable to read from device. 查看文件路径:"~/Library/D ...