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. [js开源组件开发]localStorage-cache本地存储的缓存管理

    localStorage-cache本地存储的缓存管理 距离上次的组件开发有近三个月的时间了,最近一直在做一些杂事,无法静下心来写写代码,也是在学习emberjs,在emberjs中有一个很重要的东西 ...

  2. emberjs学习二(ember-data和localstorage_adapter)

    emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...

  3. 什么是闭包(closure),为什么要用它?

    闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部. ...

  4. [deviceone开发]-毛玻璃效果示例

    一.简介 do_Bitmap组件可以把图片加载为内存里的Bitmap对象,能够对这个对象做各种图形化处理.目前只有3种处理,圆角,毛玻璃,灰度.以后会添加更多. 二.效果图 三.相关下载 https: ...

  5. SharePoint 2013 修改表单认证登录页面

    前 言 之前的博客我们介绍了如何为SharePoint配置表单登陆,但是,登陆页面是丑.很丑.非常丑.特别非常丑!我们现在就介绍一下如何定制SharePoint表单登陆页面! SharePoint 表 ...

  6. Office 365 - SharePoint 2013 Online之添加App开发工具Napa

    1.新建一个网站集,模板选择开发人员模板,如下图: 2.确定以后,需要稍等一会儿; 3.点击网站内容,添加app,如下图: 4.进入SharePoint Store,选择Napa,如下图: 5.选择A ...

  7. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q52-Q55)

    Question 52You are responsible for rebranding the My Sites section of a corporate SharePoint 2010 fa ...

  8. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q84-Q87)

    Question  84You are designing a Web Part for SharePoint 2010 that must be able to be used on any sit ...

  9. Android Content Provider基础

    Android Content Provider基础 Content Providers Content providers管理对一个结构化的数据集合的访问.它们封装了数据,并且提供了保护数据安全性的 ...

  10. word第二讲(0806)

    word里的长度单位 绝对长度单位(厘米,英寸) 相对长度单位(字符,像素) 样式 问题: 如何设置多个部分的格式 多次选择,多次设置 多次选择,一次设置 应用格式刷(ctrl+shift+c,ctr ...