Windows GDI 教程(一) 一个简单的绘图程序

http://www.tuicool.com/articles/jeMBZ3v

常见的图形编程库,除了 GDI 外还有 GDI+、OpenGL、DirectX等等,GDI 是其中最基础的一个库。所以 GDI 注定了不会有高级应用,有兴趣的就当刷低级怪吧。

在教程的最开始,需要简单的说明一些前置条件。

开发环境与前言

首先是标明开发环境:

操作系统:win7 (xp应该可以,win8未测试)

使用工具:visual studio 2010(或更高)

窗口创建

以前代码的前置问题,首先本教程内的 GDI 画图,在最开始部分主要是 在窗口内部绘制 (为避免混乱窗口外部,也就是整个桌面的绘制会在很后面的地方讨论)。因此,这里需要对于创建窗口一定的了解。为了让大家可以直接复制完代码就可以在一个文件里面运行,

博主准备的代码是手动动态创建窗口的代码,所以 这里创建窗口的代码有点长,不过大家不要怕,我们要关注的只是 中间的一小部分 。这里博主先把代码贴上:

#include <windows.h>

// 用于注册的窗口类名
const char g_szClassName[] = "myWindowClass";

/*
 * 第四步,窗口过程
 */
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
// 窗口绘制消息
case WM_PAINT:
/*
* 我们只需要在这里调用我们的 GDI 绘制函数就可以,其他地方可以先无视
*/
break;
// 窗口关闭消息
case WM_CLOSE:
DestroyWindow(hwnd);
break;
// 窗口销毁消息
case WM_DESTROY:
PostQuitMessage(0); // 发送离开消息给系统
break;
// 其他消息
default:
// pass 给系统,咱不管
return DefWindowProc(hwnd, msg, wParam, lParam); 
}
return 0;
}

/*
 * 第一步,注册窗口类
 */
void RegisterMyWindow(HINSTANCE hInstance)
{
WNDCLASSEX wc;

// 1)配置窗口属性
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc   = MyWindowProc; // 设置第四步的窗口过程回调函数
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor   = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm   = LoadIcon(NULL, IDI_APPLICATION);

// 2)注册
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "窗口注册失败!", "错误", MB_ICONEXCLAMATION | MB_OK);
exit(0); // 进程退出
}
}

HWND CreateMyWindow(HINSTANCE hInstance, int nCmdShow)
{
HWND hwnd;
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"我的窗口名称",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, // 出现坐标 x,y 默认分配 窗口宽 400 高 300
NULL, NULL, hInstance, NULL);

if(hwnd == NULL)
{
MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONEXCLAMATION | MB_OK);
exit(0); // 进程退出
}

// 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

return hwnd;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG Msg;

// 第一步,注册窗口类
RegisterMyWindow(hInstance);
// 第二步:创建窗口
hwnd =  CreateMyWindow(hInstance, nCmdShow);
// 第三步:消息循环
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
运行效果图:

draw_line
这个创建窗口的代码很长,看起来有点吓人,但是学习 GDI 的过程中,这其中几乎是完全不需要记忆的,只要有一定的了解,然后会 copy 就可以了。当然如果你能懂也是更好,以上代码的出处为 《Windows SDK 教程(三) 一些细节以及动态创建控件》 ,有兴趣的

可以去看看。

那么博主也说过了,最开始的时候,这段长长的代码其实注意一个地方就可以了,就是其中的第四步 窗口过程 中的一个小 case。

/*
 * 第四步,窗口过程
 */
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
// 窗口绘制消息
case WM_PAINT:
/*
* 只有这一个 case 是我们 GDI 入门中需要注意的
*
* 当程序执行到这个地方的时候,意味着系统像我们的程序发送了 WM_PAINT 消息
* 也就是告诉我们的程序,可以开始绘制窗口的内容了。
*
*/
break;
// 其余略...
}
return 0;
}
这样看来,貌似我们要注意的地方确实很小吧。那么我接着往下走。

PS:默认情况下,系统只会向我们的程序发送一次 WM_PAINT 消息。如果想要再来一次,需要使用 SendMessage 函数,来自己向自己手动发送该消息。

坐标系

GDI 的绘图坐标系与普通的数学坐标系不同,原点 (0,0) 位于左上角。如图:

坐标系
设备上下文(DC)

DC (设备上下文, Device Contexts)是 GDI 编程中一个很基础同时也很重要的概念。博主以前看过不少网上的资料以及书上的描述,总感觉他们说的都很奇怪。这里博主为了方便大家理解就说说自己的看法:

大家只要把 DC 当成一个保存图像的内存对象即可。当我们使用 GDI 提供的函数去操作 DC 的时候,也就意味着在使用函数去修改这块内存上的图像。

BeginPaint 与 EndPaint

用于从目标窗口获取可画图的 DC,以及关闭这个 DC。

函数原型

HDC BeginPaint(
  _In_   HWND hwnd,   // 传入想要获取 DC 的窗口句柄
  _Out_  LPPAINTSTRUCT lpPaint  // 保存目标窗口的绘图信息
);

BOOL EndPaint(
  _In_  HWND hWnd, // 目标窗口的句柄
  _In_  const PAINTSTRUCT *lpPaint  // 目标窗口的绘图信息
);
SelectObject

设置目标 DC 选中指定的对象(如笔、刷子、图片等等)。

函数原型

HGDIOBJ SelectObject(
  _In_  HDC hdc,        // 目标 DC 的句柄
  _In_  HGDIOBJ hgdiobj // 被选中的对象
);
CreatePen

创建一个画笔(pen)对象。

函数原型

HPEN CreatePen(
  _In_  int fnPenStyle, // 样式
  _In_  int nWidth, // 宽度
  _In_  COLORREF crColor // 颜色
);
MoveToEx

移动绘制的初始位置。未移动则默认是 (0,0)。

函数原型

BOOL MoveToEx(
  _In_   HDC hdc, // 操作目标DC的句柄
  _In_   int X, // x 坐标
  _In_   int Y, // y 坐标
  _Out_  LPPOINT lpPoint // 保存移动后的当前坐标
);
LineTo

使用当前选中的对象(selected object、通常是笔)从当前位置绘制一条直线到目标坐标。

函数原型

BOOL LineTo(
  _In_  HDC hdc, // 目标DC句柄
  _In_  int nXEnd, // 结束 x 坐标
  _In_  int nYEnd // 结束 y 坐标
);
绘制直线实例

#include <windows.h>

// 用于注册的窗口类名
const char g_szClassName[] = "myWindowClass";

void Paint(HWND hwnd) 
{
// paint struct 绘图结构体,存储目标窗口可以绘图的客户端区域(client area)
PAINTSTRUCT ps;
HDC hdc;   // DC(可画图的内存对象) 的句柄
HPEN hpen; // 画笔

// 通过窗口句柄获取该窗口的 DC
hdc = BeginPaint(hwnd, &ps);
// 创建画笔
hpen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
// DC 选择画笔
SelectObject(hdc,hpen);
// (画笔)从初始点移动到 50,50
MoveToEx(hdc, 50, 50, NULL);
// (画笔)从初始点画线到 100,100
LineTo(hdc, 150, 100);

EndPaint(hwnd, &ps);
}

/*
 * 第四步,窗口过程
 */
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
// 窗口绘制消息
case WM_PAINT:
Paint(hwnd); // 调用我们的 GDI 绘制函数
break;
// 其余略...
}
return 0;
}

// 其余略
运行效果图:
========

Windows GDI

https://msdn.microsoft.com/en-us/library/windows/desktop/dd145203(v=vs.85).aspx

The topic you requested is included in another documentation set. For convenience, it's displayed below. Choose Switch to see the topic in its original location.

Purpose

The Microsoft Windows graphics device interface (GDI) enables applications to use graphics and formatted text on both the video display and the printer. Windows-based applications do not access the graphics hardware directly. Instead,

GDI interacts with device drivers on behalf of applications.
Where applicable

GDI can be used in all Windows-based applications.
Developer audience

This API is designed for use by C/C++ programmers. Familiarity with the Windows message-driven architecture is required.
Run-time requirements

For information on which operating systems are required to use a particular function, see the Requirements section of the documentation for the function.
In this section

Bitmaps
Brushes
Clipping
Colors
Coordinate Spaces and Transformations
Device Contexts
Filled Shapes
Fonts and Text
Lines and Curves
Metafiles
Multiple Display Monitors
Painting and Drawing
Paths
Pens
Printing and Print Spooler
Rectangles
Regions
Related topics

DirectX
GDI+
OpenGL
Windows Image Acquisition 
========

Windows GDI和GDI+编程实例剖析

http://dev.yesky.com/255/2190255.shtml

1.基本概念   

GDI在Windows中定义为Graphics Device Interface,即图形设备接口,是Windows API(Application Programming Interface)的一个重要组成部分。它是Windows图形显示程序与实际物理设备之间的桥梁,GDI使得用户无需关心具体设备的细节,而只需在一个虚拟的

环境(即逻辑设备)中进行操作。

它的桥梁作用体现在:   

(1)用户通过调用GDI函数将逻辑空间的操作转化为具体针对设备驱动程序的调用。为实现图形设备无关性,Windows 的绘图操作在一个设备描述表上进行。用户拥有自己的"逻辑坐标"系统,它独立于实际的物理设备,与"设备坐标"相对应。开发Windows应用程序时,

程序员关心的是逻辑坐标,我们在逻辑坐标系上绘图,利用GDI将逻辑窗口映射到物理设备上。       (2)GDI能检测具体设备的能力,并依据具体的设备以最优方式驱动这些设备,完成真实的显示。

GDI函数大致可分类为:

-->设备上下文函数(如GetDC、CreateDC、DeleteDC);

-->画线函数(如LineTo、Polyline、Arc)

-->填充画图函数(如Ellipse、FillRect、Pie)

-->画图属性函数(如SetBkColor、SetBkMode、SetTextColor)

-->文本

-->字体函数(如TextOut、GetFontData)

-->位图函数(如SetPixel、BitBlt、StretchBlt)

-->坐标函数(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)

-->映射函数(如SetMapMode、SetWindowExtEx、SetViewportExtEx)

-->元文件函数(如PlayMetaFile、SetWinMetaFileBits)

-->区域函数(如FillRgn、FrameRgn、InvertRgn)

-->路径函数(如BeginPath、EndPath、StrokeAndFillPath)

-->裁剪函数(如SelectClipRgn、SelectClipPath)等。   

GDI虽然使程序员得到了一定程度的解脱,但是其编程方式仍很麻烦。譬如,显示一张位图,程序员需要进行"装入位图―读取位图文件头信息―启用设备场景―调色板变换"等一连串操作。而有了GDI+,这些问题便迎刃而解了。顾名思义,GDI+是GDI的增强版。它是

微软在Windows 2000以后操作系统中提供的新接口,其通过一套部署为托管代码的类来展现,这套类被称为GDI+的"托管类接口"。

GDI+主要提供了以下三类服务:   

(1) 二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;   

(2) 图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了Bitmap、Image等类,它们可用于显示、操作和保存BMP、JPG、GIF等图像格式。   

(3) 文字显示:GDI+支持使用各种字体、字号和样式来显示文本。GDI接口是基于函数的,而GDI+是基于C++类的对象化的应用程序编程接口,因此使用起来比GDI要方便。   

2.例程简述   单击此处下载本文例程源代码。   

本文后续的讲解都基于这样的一个例子工程(例程的开发环境为Visual C++6.0,操作系统为Windows XP),它是一个基于对话框的MFC应用程序,包括2个父菜单:   

(1) GDI   GDI父菜单下包括一个子菜单: ID:IDM_GDI_DRAW_LINE caption:画线单击事件:在窗口绘制正旋曲线   

(2) GDI+   DIB位图父菜单下包括两个子菜单:

-->a. ID:IDM_GDIP_DRAW_LINE caption:画线 单击事件:在窗口绘制正旋曲线

-->b. caption:新增功能,其下又包括下列子菜单:

(ⅰ)ID:IDM_Gradient_Brush caption:渐变画刷 单击事件:在窗口演示GDI+的渐变画刷功能

(ⅱ)ID:IDM_Cardinal_Spline caption:基数样条  单击事件:在窗口演示GDI+的基数样条函数功能   

(ⅲ)ID:IDM_Transformation_Matrix caption:变形和矩阵对象   单击事件:在窗口演示GDI+的变形和矩阵对象功能   

(ⅳ)ID:IDM_Scalable_Region caption:可伸缩区域   单击事件:在窗口演示GDI+的可伸缩区域功能   

(ⅴ)ID:IDM_IMAGE caption:图像   单击事件:在窗口演示GDI+的多种图像格式支持功能   

(ⅵ)ID:IDM_Alpha_Blend caption:Alpha混合   单击事件:在窗口演示GDI+的Alpha混合功能   

(ⅶ)ID:IDM_TEXT caption:文本   单击事件:在窗口演示GDI+的强大文本输出能力

后续篇章将集中在对上述菜单单击事件消息处理函数的讲解,下面的代码是整个对话框类CGdiexampleDlg的消息映射:

BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog) //{{AFX_MSG_MAP(CGdiexampleDlg)   
ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_COMMAND(IDM_GDI_DRAW_LINE, OnGdiDrawLine) ON_COMMAND(IDM_GDIP_DRAW_LINE, OnGdipDrawLine) ON_COMMAND(IDM_Gradient_Brush, OnGradientBrush) ON_COMMAND(IDM_Cardinal_Spline,

OnCardinalSpline) ON_COMMAND(IDM_Transformation_Matrix, OnTransformationMatrix) ON_COMMAND(IDM_Scalable_Region, OnScalableRegion) ON_COMMAND(IDM_IMAGE, OnImage) ON_COMMAND(IDM_Alpha_Blend, OnAlphaBlend) ON_COMMAND(IDM_TEXT, OnText) //}}

AFX_MSG_MAP END_MESSAGE_MAP()  
3.GDI编程   "GDI"菜单下的"画线"子菜单单击事件消息处理函数的代码如下:

void CGdiexampleDlg::OnGdiDrawLine() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //逻辑坐标与设备坐标变换  CRect rect;    
GetClientRect(&rect);    
dc.SetMapMode(MM_ANISOTROPIC);    
dc.SetWindowOrg(0, 0);    
dc.SetWindowExt(rect.right, rect.bottom);  dc.SetViewportOrg(0, rect.bottom / 2);  dc.SetViewportExt(rect.right, - rect.bottom);  //创建绘制正旋曲线的pen并将其选入设备上下文    
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));    
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());  //绘制正旋曲线    
dc.MoveTo(0, 0);    
for (int i = 0; i < rect.right; i++)  {   dc.LineTo(i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI));  }  //创建绘制x轴的pen并将其选入设备上下文    
CPen penx(PS_SOLID, 1, RGB(0, 0, 255));  dc.SelectObject(penx.GetSafeHandle());  //绘制X轴    
dc.MoveTo(0, 0);    
dc.LineTo(rect.right, 0);  //恢复原先的pen  dc.SelectObject(oldObject);   
}  
  单击这个按钮,会出现如图1所示的效果,我们来对此进行解读。

图1 绘制正旋曲线
  前文提到,GDI编程需进行设备坐标和逻辑坐标的转化。而屏幕上的设备坐标通常会按客户坐标给出,客户坐标依赖于窗口的客户区域,其起始位置位于客户区域的左上角。为示区别,图2给出了设备坐标和用户逻辑坐标的示例。

图2 设备坐标与逻辑坐标
  设备坐标与逻辑坐标的转换关系如下:

  公式中的<Xvorg, Yvorg>是设备空间中视口的原点,而< Xworg, Yworg >是逻辑空间中窗口的原点。 Xwext/Xvext和Ywext/Yvext分别是窗口与视口水平和垂直范围的比例。   因此,经过程序中的dc.SetWindowOrg (0,0) 和dc.SetViewportOrg (0,

rect.bottom/2)语句我们设置了视口和窗口的原点;而经过程序中的dc.SetWindowExt (rect.right,rect.bottom) 和dc.SetViewportExt (rect.right,-rect.bottom) 语句我们设置了视口和窗口的范围。由于视口和窗口的纵坐标方向相反,设置视口的垂直范围为负值

。这样我们得到了一个逻辑坐标原点为客户区水平方向最左边和垂直方向居中的坐标系,我们在这个坐标系上直接绘制正旋曲线,不需要再理睬Windows对话框客户区坐标了。   void CGdiexampleDlg::OnGdiDrawLine()函数中未指定逻辑设备和物理设备的映射模式,

则为缺省的MM_TEXT。在这种模式下,一个逻辑单位对应于一个像素点。映射模式是GDI中的一个重要概念,其它的映射模式还有MM_LOENGLlSH、MM_HIENGLISH、MM_LOMETRIC和MM_HIMETRIC等。我们可以通过如下语句指定映射模式为MM_TEXT:

dc.SetMapMode(MM_TEXT);

  值得一提的是,从上述代码可以看出:在GDI编程中,几乎所有的操作都围绕设备上下文dc展开。的确,这正是GDI编程的特点!设备上下文是Windows 使用的一种结构,所有GDI操作前都需取得特定设备的上下文,函数中的CClientDC dc (this) 语句完成这一功能。

  归纳可得,利用GDI进行图形、图像处理的一般操作步骤为:   1. 取得指定窗口的DC;   2. 确定使用的坐标系及映射方式;   3. 进行图形、图像或文字处理;   4. 释放所使用的DC。   4.GDI+编程   "GDI+"菜单下的"画线"子菜单单击事件消息处

理函数的代码如下:

void CGdiexampleDlg::OnGdipDrawLine() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //逻辑坐标与设备坐标变换  CRect rect;    
GetClientRect(&rect);    
dc.SetMapMode(MM_ANISOTROPIC);    
dc.SetWindowOrg(0, 0);    
dc.SetWindowExt(rect.right, rect.bottom);  dc.SetViewportOrg(0, rect.bottom / 2);  dc.SetViewportExt(rect.right, - rect.bottom);  //创建Graphics对象    
Graphics graphics(dc);  //创建pen    
Pen myPen(Color::Red);    
myPen.SetWidth(1);  //画正旋曲线    
for (int i = 0; i < rect.right; i++)  {   graphics.DrawLine(&myPen, i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI), i + 1, 100 *sin(2 *((i + 1) / (rect.right / 5.0)) *PI));  }  //画X轴  
  myPen.SetColor(Color::Blue);   
 graphics.DrawLine(&myPen, 0, 0, rect.right, 0); }  
  由于我们使用的是Visual C++6.0而非VS.NET,我们需要下载微软的GDIPLUS支持包。在微软官方网站下载时需认证Windows为正版,我们可从这个地址下载:http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一个完整的GDI+支持包至少包括如下文件:   

(1)头文件:gdiplus.h   (2)动态链接库的.lib文件:gdiplus.lib   (3)动态链接库的.dll文件:gdiplus.dll   少了(1)、(2)程序不能编译,少了(3)程序能以共享DLL的方式编译但是不能运行,运行时找不到.dll文件。   为使得Visual C++6.0支持GDI+,我

们需要在使用GDI+对象的文件的开头添加如下代码:

#define UNICODE #ifndef ULONG_PTR #define ULONG_PTR unsigned long* #endif #include "c:/gdiplus/includes/gdiplus.h" using namespace Gdiplus; #pragma comment(lib, "c://gdiplus//lib//gdiplus.lib")  
  在Visual C++中使用GDI+必须先进行GDI+的初始化,我们在CWinApp派生类的InitInstance函数中进行此项工作是最好的:

/ // CGdiexampleApp initialization BOOL CGdiexampleApp::InitInstance() {    
AfxEnableControlContainer();  // Standard initialization  #ifdef _AFXDLL   Enable3dControls(); // Call this when using MFC in a shared DLL    
#else     
Enable3dControlsStatic(); // Call this when linking to MFC statically    
#endif  //初始化gdiplus的环境  GdiplusStartupInput gdiplusStartupInput;  ULONG_PTR gdiplusToken;  // 初始化GDI+.  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);    
CGdiexampleDlg dlg;    
m_pMainWnd = &dlg;    
int nResponse = dlg.DoModal();    
if (nResponse == IDOK){}    
else if (nResponse == IDCANCEL){}  //关闭gdiplus的环境    
GdiplusShutdown(gdiplusToken);    
return FALSE; }  
  单击"GDI+"菜单下的"画线"子菜单,也会出现如图1所示的效果。观察void CGdiexampleDlg::OnGdipDrawLine() 函数,我们发现用GDI+进行图形、图像操作的步骤为:   (1)创建 Graphics 对象:Graphics 对象表示GDI+绘图表面,是用于创建图形图像的对象;

  (2)使用 Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。   Graphics 对象是GDI+的核心,GDI中设备上下文dc和Graphics 对象的作用相似,但在GDI中使用的是基于句柄的编程模式,而GDI+中使用的则是基于对象的编程模式。Graphics封装了GDI+

绘图面,而且此类无法被继承,它的所有成员函数都不是虚函数。   下面,我们来逐个用实际代码实现GDI+的新增功能,这些新增功能包括:渐变的画刷(Gradient Brushes)、基数样条函数(Cardinal Splines)、持久的路径对象(Persistent Path Objects)、变形和

矩阵对象(Transformations &Matrix Object)、可伸缩区域(Scalable Regions)、Alpha混合(Alpha Blending)和丰富的图像格式支持等。 渐变的画刷   GDI+提供了用于填充图形、路径和区域的线性渐变画刷和路径渐变画刷。   线性渐变画刷使用渐变颜色来填充

图形。   当用路径渐变画刷填充图形时,可指定从图形的一部分移至另一部分时画刷颜色的变化方式。例如,我们可以只指定图形的中心颜色和边缘颜色,当画刷从图形中间向外边缘移动时,画刷会逐渐从中心颜色变化到边缘颜色。

void CGdiexampleDlg::OnGradientBrush() {  // TODO: Add your command handler code here  
  CClientDC dc(this);    
CRect rect;    
GetClientRect(&rect);  //创建Graphics对象  Graphics graphics(dc);  //创建渐变画刷  LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);  //填充    
graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom); }  
  本程序使用线性渐变画刷,当画刷从客户区左上角移向客户区右下角的过程中,颜色逐渐由蓝色转变为绿色。

图3 GDI+渐变画刷
  基数样条函数   GDI+支持基数样条,基数样条指的是一连串单独的曲线,这些曲线连接起来形成一条较大的曲线。样条由点(Point结构体)的数组指定,并通过该数组中的每一个点。基数样条平滑地穿过数组中的每一个点(不出现尖角),因此比用直线连接创建的

路径精确。

void CGdiexampleDlg::OnCardinalSpline() {  // TODO: Add your command handler code here  
  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);    
Point points[] =  {     
Point(0, 0),   
Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 00)  };  //直接画线    
for (int i = 0; i < 4; i++)  {   graphics.DrawLine(&Pen(Color::Blue, 3), points[i], points[i + 1]);  }  //利用基数样条画线  
  graphics.DrawCurve(&Pen(Color::Red, 3), points, 5); }  
  图4演示了直接连线和经过基数样条平滑拟合后的线条的对比,后者的曲线(Curve)没有尖角。这个工作我们在中学的数学课上把离散的点连接成曲线时做过。

图4 GDI+基数样条
持久的路径对象   在GDI中,路径隶属于一个设备上下文,一旦设备环境指针超过它的生存期,路径也会被删除。利用GDI+,可以创建并维护与Graphics对象分开的GraphicsPath 对象,它不依赖于Graphics对象的生存期。   变形和矩阵对象   GDI+提供了Matrix

对象,它是一种可以使变形(旋转、平移、缩放等) 简易灵活的强大工具,Matrix对象需与要被变形的对象联合使用。对于GraphicsPath类,我们可以使用其成员函数Transform接收Matrix参数用于变形。

void CGdiexampleDlg::OnTransformationMatrix() {  // TODO: Add your command handler code here  
  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);    
GraphicsPath path;    
path.AddRectangle(Rect(250, 20, 70, 70));  graphics.DrawPath(&Pen(Color::Black, 1), &path); // 在应用变形矩阵之前绘制矩形  // 路径变形   
 Matrix matrix1, matrix2;    
matrix1.Rotate(45.0f); //旋转顺时针45度  path.Transform(&matrix1); //应用变形  graphics.DrawPath(&Pen(Color::Red, 3), &path);  matrix2.Scale(1.0f, 0.5f); //转化成为平行四边形法则  path.Transform(&matrix2); //应用变形  

graphics.DrawPath(&Pen(Color::Blue, 3), &path); }  
  图5演示了正方形经过旋转和拉伸之后的效果:黑色的为原始图形,红色的为旋转45度之后的图形,蓝色的为经过拉伸为平行四边形后的图形。

图5 GDI+变形和矩阵对象
  可伸缩区域   GDI+通过对区域(Region)的支持极大地扩展了GDI。在GDI 中,区域存储在设备坐标中,可应用于区域的唯一变形是平移。但是在GDI +中,区域存储在全局坐标(世界坐标)中,可对区域利用变形矩阵进行变形(旋转、平移、缩放等)。

void CGdiexampleDlg::OnScalableRegion() {  // TODO: Add your command handler code here  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建GraphicsPath  GraphicsPath path;  path.AddLine(100, 100, 150, 150);  

path.AddLine(50, 150, 150, 150);  path.AddLine(50, 150, 100, 100);  //创建Region  Region region(&path);  //填充区域  graphics.FillRegion(&SolidBrush(Color::Blue), ®ion);  //区域变形    
Matrix matrix;    
matrix.Rotate(10.0f); //旋转顺时针20度  matrix.Scale(1.0f, 0.3f); //拉伸  region.Transform(&matrix); //应用变形  //填充变形后的区域    
graphics.FillRegion(&SolidBrush(Color::Green), ®ion); }  
  上述程序中以蓝色填充一个三角形区域,接着将此区域旋转和拉伸,再次显示,其效果如图6。

图6 GDI+区域变形
丰富的图像格式支持   GDI +提供了Image、Bitmap 和Metafile 类,方便用户进行图像格式的加载、操作和保存。GDI+支持的图像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,几乎涵盖了所有的常用图像格式。

void CGdiexampleDlg::OnImage() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  Image image(L "d://1.jpg");  //在矩形区域内显示jpg图像  Point destPoints1[3] =  {   Point(10, 10), Point(220, 10), Point(10, 290)  };  graphics.DrawImage(&image,

destPoints1, 3);  //在平行四边形区域内显示jpg图像    
Point destPoints2[3] =  {   Point(230, 10), Point(440, 10), Point(270, 290)  };  graphics.DrawImage(&image, destPoints2, 3); }  
  上述程序将D盘根目录下文件名为"1.jpg"的jpg图像以矩阵和平行四边形两种方式显示,效果如图7。

图7 GDI+多种图像格式支持
  由此我们可以看出,GDI+在图像显示和操作方面的确比GDI简单许多。回忆我们在《Visual C++中DDB与DIB位图编程全攻略》一文中所介绍的用GDI显示位图的方式,其与GDI+图像处理的难易程度真是有天壤之别。 Alpha混合   Alpha允许将两个物体混合起来显示

,在3D气氛和场景渲染等方面有广泛应用。它能"雾化"图像,使得一个图像着色在另一个半透明的图像上,呈现一种朦胧美。我们知道,一个像素可用R,G,B三个维度来表示,我们可以再加上第4个即:Alpha维度(channel),表征透明程度。

void CGdiexampleDlg::OnAlphaBlend() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建ColorMatrix  ColorMatrix ClrMatrix =  {     
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,   0.0f, 1.0f, 0.0f, 0.0f, 0.0f,   0.0f, 0.0f, 1.0f, 0.0f, 0.0f,   0.0f, 0.0f, 0.0f, 0.5f, 0.0f,   0.0f, 0.0f, 0.0f, 0.0f, 1.0f  };  //将ColorMatrix赋给  
ImageAttributes  ImageAttributes ImgAttr;  ImgAttr.SetColorMatrix(&ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);  //在矩形区域内显示jpg图像    
Image img1(L "d://1.jpg");    
Point destPoints1[3] =  {   Point(10, 10), Point(220, 10), Point(10, 290)  };  graphics.DrawImage(&img1, destPoints1, 3);  //Alpha混合    
Image img2(L "d://2.jpg");    
int width, height;    
width = img2.GetWidth();    
height = img2.GetHeight();    
graphics.DrawImage(&img2, RectF(10, 10, 210, 280), 0, 0, width, height,UnitPixel, &ImgAttr);  //在平行四边形区域内显示jpg图像    
Point destPoints2[3] =  {   Point(230, 10), Point(440, 10), Point(270, 290)  };  graphics.DrawImage(&img1, destPoints2, 3);  //Alpha混合    
graphics.DrawImage(&img2, destPoints2, 3, 0, 0, width, height, UnitPixel,&ImgAttr); }  
  上述程序中将D盘根目录下文件名为"1.jpg"的图像以矩阵和平行四边形两种方式显示,然后将文件名为为"2.jpg"的图像与之进行混合,其效果如图8。

图8 GDI+ Alpha混合
  为了能进行Alpha混合,我们需要使用ImageAttributes类和ColorMatrix矩阵,ImageAttributes可以进行颜色、灰度等调整从而达到控制图像着色方式的目的。ColorMatrix是ImageAttributes类大多数函数的参数,它包含了Alpha、Red、Green、Blue维度的值,以及

另一维w,顺序为RGBaw。   CGdiexampleDlg::OnAlphaBlend()函数中ColorMatrix的实例ClrMatrix中元素(4,4)的值为0.5,表示Alpha度的值为0.5(即半透明)。在ColorMatrix中,元素(5,5)的值恒定为1.0。我们把ClrMatrix的元素(0,0)修改为0.0,即使得图像2.jpg

的红色维度全不显示,再看效果,为图9。列位读者,我们以前在豪杰超级解霸中调整R,G,B值从而控制图像输出颜色的时候,调的就是这个东东!图9的效果很像破旧彩色电视机,红色电子枪"嗝"了。刚大学毕业时,俺那个叫穷啊,就买了这么个电视机,还看得很爽,

真是往事不堪回首!

图9 GDI+中的ColorMatrix
强大的文字输出   GDI+拥有极其强大的文字输出处理能力,输出文字的颜色、字体、填充方式都可以直接作为Graphics类DrawString成员函数的参数进行设置,其功能远胜过GDI设备上下文的TextOut函数。

void CGdiexampleDlg::OnText() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建20号"楷体"字体  FontFamily fontFamily1(L "楷体_GB2312"); // 定义"楷体"字样    
Font font1(&fontFamily1, 20, FontStyleRegular, UnitPoint);  //定义输出UNICODE字符串    
WCHAR string[256];    
wcscpy(string, L "天极网的读者朋友,您好!");  //以蓝色画刷和20号"楷体"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 10),&SolidBrush(Color::Blue));  //定义字符串显示画刷  LinearGradientBrush linGrBrush(Point(30, 50), Point(100, 50), Color(255, 255,0, 0), Color(255, 0, 0, 255));  //

以线性渐变画刷和创建的20号"楷体"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 50),&linGrBrush);  //创建20号"华文行楷"字体    
FontFamily fontFamily2(L "华文行楷"); // 定义"楷体"字样    
Font font2(&fontFamily2, 20, FontStyleRegular, UnitPoint);  //以线性渐变画刷和20号"华文行楷"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 90),&linGrBrush);  //以图像创建画刷    
Image image(L "d://3.jpg");    
TextureBrush tBrush(&image);  //以图像画刷和20号"华文行楷"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 130),&tBrush);  //创建25号"华文中宋"字体    
FontFamily fontFamily3(L "华文中宋"); // 定义"楷体"字样    
Font font3(&fontFamily2, 25, FontStyleRegular, UnitPoint);  //以图像画刷和20号"华文行楷"显示字符串  graphics.DrawString(string, (INT)wcslen(string), &font3, PointF(30, 170),&tBrush); }  
  上述代码的执行效果如图10所示,字体、颜色和填充都很丰富!

图10 GDI+文本输出
5.GDI与GDI+的比较   GDI+相对GDI而言主要在编程方式上发生了巨大的改变。   GDI的核心是设备上下文,GDI函数都依赖于设备上下文句柄,其编程方式是基于句柄的;GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个Graphics 对象,就可以用面向对象

的方式调用其成员函数进行图形操作,编程方式是基于对象的。   GDI在使用设备上下文绘制线条之前,必须先调用SelectObject 以使钢笔对象和设备上下文关联。其后,在设备上下文中绘制的所有线条均使用该钢笔,直到选择另一支不同的钢笔为止。

CGdiexampleDlg::OnGdiDrawLine函数中的下列语句完成的就是这个功能:

//创建绘制正旋曲线的pen并将其选入设备上下文   
CPen pen(PS_SOLID,1,RGB(255,0,0));   
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle()); … //创建绘制x轴的pen并将其选入设备上下文 CPen penx  
(PS_SOLID,1,RGB(0,0,255)); dc.SelectObject(penx.GetSafeHandle()); … //恢复原先的pen dc.SelectObject(oldObject);  
  但是,在GDI+中,只需将Pen对象直接作为参数传递给Graphics类的DrawLine等方法即可,而不必使Pen对象与Graphics对象关联,例如CGdiexampleDlg::OnGdipDrawLine函数中的下列语句:

Pen myPen(Color::Red); myPen.SetWidth(1); … graphics.DrawLine(&myPen,i,100*sin(2*(i/(rect.right/5.0))*PI), i+1,100*sin(2*((i+1)/(rect.right/5.0))*PI)); … graphics.DrawLine(&myPen,0,0,rect.right,0);  
  GDI中有当前位置的概念,所以在使用GDI绘制线条前应该先使用MoveTo移动当前位置,再使用LineTo画线,例如:

[cpp] view plain copy print?
//绘制正旋曲线 dc.MoveTo(0,0) ;   
for(int i=0;i<rect.right;i++) {  dc.LineTo(i,100*sin(2*(i/(rect.right/5.0))*PI)); }  
  而GDI+中则没有当前位置的概念,画线函数中可以直接指定起点和终点,例如:

[cpp] view plain copy print?
graphics.DrawLine(&myPen,0,0,rect.right,0);  
  6.结论   鉴于GDI+良好的易用性和其具有的强大功能,我们建议尽快抛弃GDI编程方式,因为我们没有必要将时间浪费在无意义的重复代码的设计上。GDI+对GDI的增强,某种意义上类似于MFC对Windows API的整理和封装。作为一种良好的"生产工具",它必将大大

地促进开发时的"生产力"。
========

Windows GDI和GDI+编程实例剖析

http://blog.csdn.net/tangcheng_ok/article/details/6311187
http://dev.yesky.com/255/2190255.shtml

1.基本概念   

GDI在Windows中定义为Graphics Device Interface,即图形设备接口,是Windows API(Application Programming Interface)的一个重要组成部分。它是Windows图形显示程序与实际物理设备之间的桥梁,GDI使得用户无需关心具体设备的细节,而只需在一个虚拟的

环境(即逻辑设备)中进行操作。

它的桥梁作用体现在:   

(1)用户通过调用GDI函数将逻辑空间的操作转化为具体针对设备驱动程序的调用。为实现图形设备无关性,Windows 的绘图操作在一个设备描述表上进行。用户拥有自己的"逻辑坐标"系统,它独立于实际的物理设备,与"设备坐标"相对应。开发Windows应用程序时,

程序员关心的是逻辑坐标,我们在逻辑坐标系上绘图,利用GDI将逻辑窗口映射到物理设备上。       (2)GDI能检测具体设备的能力,并依据具体的设备以最优方式驱动这些设备,完成真实的显示。

GDI函数大致可分类为:

-->设备上下文函数(如GetDC、CreateDC、DeleteDC);

-->画线函数(如LineTo、Polyline、Arc)

-->填充画图函数(如Ellipse、FillRect、Pie)

-->画图属性函数(如SetBkColor、SetBkMode、SetTextColor)

-->文本

-->字体函数(如TextOut、GetFontData)

-->位图函数(如SetPixel、BitBlt、StretchBlt)

-->坐标函数(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)

-->映射函数(如SetMapMode、SetWindowExtEx、SetViewportExtEx)

-->元文件函数(如PlayMetaFile、SetWinMetaFileBits)

-->区域函数(如FillRgn、FrameRgn、InvertRgn)

-->路径函数(如BeginPath、EndPath、StrokeAndFillPath)

-->裁剪函数(如SelectClipRgn、SelectClipPath)等。   

GDI虽然使程序员得到了一定程度的解脱,但是其编程方式仍很麻烦。譬如,显示一张位图,程序员需要进行"装入位图―读取位图文件头信息―启用设备场景―调色板变换"等一连串操作。而有了GDI+,这些问题便迎刃而解了。顾名思义,GDI+是GDI的增强版。它是

微软在Windows 2000以后操作系统中提供的新接口,其通过一套部署为托管代码的类来展现,这套类被称为GDI+的"托管类接口"。

GDI+主要提供了以下三类服务:   

(1) 二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;   

(2) 图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了Bitmap、Image等类,它们可用于显示、操作和保存BMP、JPG、GIF等图像格式。   

(3) 文字显示:GDI+支持使用各种字体、字号和样式来显示文本。GDI接口是基于函数的,而GDI+是基于C++类的对象化的应用程序编程接口,因此使用起来比GDI要方便。   

2.例程简述   单击此处下载本文例程源代码。   

本文后续的讲解都基于这样的一个例子工程(例程的开发环境为Visual C++6.0,操作系统为Windows XP),它是一个基于对话框的MFC应用程序,包括2个父菜单:   

(1) GDI   GDI父菜单下包括一个子菜单: ID:IDM_GDI_DRAW_LINE caption:画线单击事件:在窗口绘制正旋曲线   

(2) GDI+   DIB位图父菜单下包括两个子菜单:

-->a. ID:IDM_GDIP_DRAW_LINE caption:画线 单击事件:在窗口绘制正旋曲线

-->b. caption:新增功能,其下又包括下列子菜单:

(ⅰ)ID:IDM_Gradient_Brush caption:渐变画刷 单击事件:在窗口演示GDI+的渐变画刷功能

(ⅱ)ID:IDM_Cardinal_Spline caption:基数样条  单击事件:在窗口演示GDI+的基数样条函数功能   

(ⅲ)ID:IDM_Transformation_Matrix caption:变形和矩阵对象   单击事件:在窗口演示GDI+的变形和矩阵对象功能   

(ⅳ)ID:IDM_Scalable_Region caption:可伸缩区域   单击事件:在窗口演示GDI+的可伸缩区域功能   

(ⅴ)ID:IDM_IMAGE caption:图像   单击事件:在窗口演示GDI+的多种图像格式支持功能   

(ⅵ)ID:IDM_Alpha_Blend caption:Alpha混合   单击事件:在窗口演示GDI+的Alpha混合功能   

(ⅶ)ID:IDM_TEXT caption:文本   单击事件:在窗口演示GDI+的强大文本输出能力

后续篇章将集中在对上述菜单单击事件消息处理函数的讲解,下面的代码是整个对话框类CGdiexampleDlg的消息映射:

BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog) //{{AFX_MSG_MAP(CGdiexampleDlg)   
ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_COMMAND(IDM_GDI_DRAW_LINE, OnGdiDrawLine) ON_COMMAND(IDM_GDIP_DRAW_LINE, OnGdipDrawLine) ON_COMMAND(IDM_Gradient_Brush, OnGradientBrush) ON_COMMAND(IDM_Cardinal_Spline,

OnCardinalSpline) ON_COMMAND(IDM_Transformation_Matrix, OnTransformationMatrix) ON_COMMAND(IDM_Scalable_Region, OnScalableRegion) ON_COMMAND(IDM_IMAGE, OnImage) ON_COMMAND(IDM_Alpha_Blend, OnAlphaBlend) ON_COMMAND(IDM_TEXT, OnText) //}}

AFX_MSG_MAP END_MESSAGE_MAP()  
3.GDI编程   "GDI"菜单下的"画线"子菜单单击事件消息处理函数的代码如下:

void CGdiexampleDlg::OnGdiDrawLine() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //逻辑坐标与设备坐标变换  CRect rect;    
GetClientRect(&rect);    
dc.SetMapMode(MM_ANISOTROPIC);    
dc.SetWindowOrg(0, 0);    
dc.SetWindowExt(rect.right, rect.bottom);  dc.SetViewportOrg(0, rect.bottom / 2);  dc.SetViewportExt(rect.right, - rect.bottom);  //创建绘制正旋曲线的pen并将其选入设备上下文    
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));    
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());  //绘制正旋曲线    
dc.MoveTo(0, 0);    
for (int i = 0; i < rect.right; i++)  {   dc.LineTo(i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI));  }  //创建绘制x轴的pen并将其选入设备上下文    
CPen penx(PS_SOLID, 1, RGB(0, 0, 255));  dc.SelectObject(penx.GetSafeHandle());  //绘制X轴    
dc.MoveTo(0, 0);    
dc.LineTo(rect.right, 0);  //恢复原先的pen  dc.SelectObject(oldObject);   
}  
  单击这个按钮,会出现如图1所示的效果,我们来对此进行解读。

图1 绘制正旋曲线
  前文提到,GDI编程需进行设备坐标和逻辑坐标的转化。而屏幕上的设备坐标通常会按客户坐标给出,客户坐标依赖于窗口的客户区域,其起始位置位于客户区域的左上角。为示区别,图2给出了设备坐标和用户逻辑坐标的示例。

图2 设备坐标与逻辑坐标
  设备坐标与逻辑坐标的转换关系如下:

  公式中的<Xvorg, Yvorg>是设备空间中视口的原点,而< Xworg, Yworg >是逻辑空间中窗口的原点。 Xwext/Xvext和Ywext/Yvext分别是窗口与视口水平和垂直范围的比例。   因此,经过程序中的dc.SetWindowOrg (0,0) 和dc.SetViewportOrg (0,

rect.bottom/2)语句我们设置了视口和窗口的原点;而经过程序中的dc.SetWindowExt (rect.right,rect.bottom) 和dc.SetViewportExt (rect.right,-rect.bottom) 语句我们设置了视口和窗口的范围。由于视口和窗口的纵坐标方向相反,设置视口的垂直范围为负值

。这样我们得到了一个逻辑坐标原点为客户区水平方向最左边和垂直方向居中的坐标系,我们在这个坐标系上直接绘制正旋曲线,不需要再理睬Windows对话框客户区坐标了。   void CGdiexampleDlg::OnGdiDrawLine()函数中未指定逻辑设备和物理设备的映射模式,

则为缺省的MM_TEXT。在这种模式下,一个逻辑单位对应于一个像素点。映射模式是GDI中的一个重要概念,其它的映射模式还有MM_LOENGLlSH、MM_HIENGLISH、MM_LOMETRIC和MM_HIMETRIC等。我们可以通过如下语句指定映射模式为MM_TEXT:

dc.SetMapMode(MM_TEXT);

  值得一提的是,从上述代码可以看出:在GDI编程中,几乎所有的操作都围绕设备上下文dc展开。的确,这正是GDI编程的特点!设备上下文是Windows 使用的一种结构,所有GDI操作前都需取得特定设备的上下文,函数中的CClientDC dc (this) 语句完成这一功能。

  归纳可得,利用GDI进行图形、图像处理的一般操作步骤为:   1. 取得指定窗口的DC;   2. 确定使用的坐标系及映射方式;   3. 进行图形、图像或文字处理;   4. 释放所使用的DC。   4.GDI+编程   "GDI+"菜单下的"画线"子菜单单击事件消息处

理函数的代码如下:

void CGdiexampleDlg::OnGdipDrawLine() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //逻辑坐标与设备坐标变换  CRect rect;    
GetClientRect(&rect);    
dc.SetMapMode(MM_ANISOTROPIC);    
dc.SetWindowOrg(0, 0);    
dc.SetWindowExt(rect.right, rect.bottom);  dc.SetViewportOrg(0, rect.bottom / 2);  dc.SetViewportExt(rect.right, - rect.bottom);  //创建Graphics对象    
Graphics graphics(dc);  //创建pen    
Pen myPen(Color::Red);    
myPen.SetWidth(1);  //画正旋曲线    
for (int i = 0; i < rect.right; i++)  {   graphics.DrawLine(&myPen, i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI), i + 1, 100 *sin(2 *((i + 1) / (rect.right / 5.0)) *PI));  }  //画X轴  
  myPen.SetColor(Color::Blue);   
 graphics.DrawLine(&myPen, 0, 0, rect.right, 0); }  
  由于我们使用的是Visual C++6.0而非VS.NET,我们需要下载微软的GDIPLUS支持包。在微软官方网站下载时需认证Windows为正版,我们可从这个地址下载:http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一个完整的GDI+支持包至少包括如下文件:   

(1)头文件:gdiplus.h   (2)动态链接库的.lib文件:gdiplus.lib   (3)动态链接库的.dll文件:gdiplus.dll   少了(1)、(2)程序不能编译,少了(3)程序能以共享DLL的方式编译但是不能运行,运行时找不到.dll文件。   为使得Visual C++6.0支持GDI+,我

们需要在使用GDI+对象的文件的开头添加如下代码:

#define UNICODE #ifndef ULONG_PTR #define ULONG_PTR unsigned long* #endif #include "c:/gdiplus/includes/gdiplus.h" using namespace Gdiplus; #pragma comment(lib, "c://gdiplus//lib//gdiplus.lib")  
  在Visual C++中使用GDI+必须先进行GDI+的初始化,我们在CWinApp派生类的InitInstance函数中进行此项工作是最好的:

/ // CGdiexampleApp initialization BOOL CGdiexampleApp::InitInstance() {    
AfxEnableControlContainer();  // Standard initialization  #ifdef _AFXDLL   Enable3dControls(); // Call this when using MFC in a shared DLL    
#else     
Enable3dControlsStatic(); // Call this when linking to MFC statically    
#endif  //初始化gdiplus的环境  GdiplusStartupInput gdiplusStartupInput;  ULONG_PTR gdiplusToken;  // 初始化GDI+.  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);    
CGdiexampleDlg dlg;    
m_pMainWnd = &dlg;    
int nResponse = dlg.DoModal();    
if (nResponse == IDOK){}    
else if (nResponse == IDCANCEL){}  //关闭gdiplus的环境    
GdiplusShutdown(gdiplusToken);    
return FALSE; }  
  单击"GDI+"菜单下的"画线"子菜单,也会出现如图1所示的效果。观察void CGdiexampleDlg::OnGdipDrawLine() 函数,我们发现用GDI+进行图形、图像操作的步骤为:   (1)创建 Graphics 对象:Graphics 对象表示GDI+绘图表面,是用于创建图形图像的对象;

  (2)使用 Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。   Graphics 对象是GDI+的核心,GDI中设备上下文dc和Graphics 对象的作用相似,但在GDI中使用的是基于句柄的编程模式,而GDI+中使用的则是基于对象的编程模式。Graphics封装了GDI+

绘图面,而且此类无法被继承,它的所有成员函数都不是虚函数。   下面,我们来逐个用实际代码实现GDI+的新增功能,这些新增功能包括:渐变的画刷(Gradient Brushes)、基数样条函数(Cardinal Splines)、持久的路径对象(Persistent Path Objects)、变形和

矩阵对象(Transformations &Matrix Object)、可伸缩区域(Scalable Regions)、Alpha混合(Alpha Blending)和丰富的图像格式支持等。 渐变的画刷   GDI+提供了用于填充图形、路径和区域的线性渐变画刷和路径渐变画刷。   线性渐变画刷使用渐变颜色来填充

图形。   当用路径渐变画刷填充图形时,可指定从图形的一部分移至另一部分时画刷颜色的变化方式。例如,我们可以只指定图形的中心颜色和边缘颜色,当画刷从图形中间向外边缘移动时,画刷会逐渐从中心颜色变化到边缘颜色。

void CGdiexampleDlg::OnGradientBrush() {  // TODO: Add your command handler code here  
  CClientDC dc(this);    
CRect rect;    
GetClientRect(&rect);  //创建Graphics对象  Graphics graphics(dc);  //创建渐变画刷  LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);  //填充    
graphics.FillRectangle(&lgb, 0, 0, rect.right, rect.bottom); }  
  本程序使用线性渐变画刷,当画刷从客户区左上角移向客户区右下角的过程中,颜色逐渐由蓝色转变为绿色。

图3 GDI+渐变画刷
  基数样条函数   GDI+支持基数样条,基数样条指的是一连串单独的曲线,这些曲线连接起来形成一条较大的曲线。样条由点(Point结构体)的数组指定,并通过该数组中的每一个点。基数样条平滑地穿过数组中的每一个点(不出现尖角),因此比用直线连接创建的

路径精确。

void CGdiexampleDlg::OnCardinalSpline() {  // TODO: Add your command handler code here  
  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);    
Point points[] =  {     
Point(0, 0),   
Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 00)  };  //直接画线    
for (int i = 0; i < 4; i++)  {   graphics.DrawLine(&Pen(Color::Blue, 3), points[i], points[i + 1]);  }  //利用基数样条画线  
  graphics.DrawCurve(&Pen(Color::Red, 3), points, 5); }  
  图4演示了直接连线和经过基数样条平滑拟合后的线条的对比,后者的曲线(Curve)没有尖角。这个工作我们在中学的数学课上把离散的点连接成曲线时做过。

图4 GDI+基数样条
持久的路径对象   在GDI中,路径隶属于一个设备上下文,一旦设备环境指针超过它的生存期,路径也会被删除。利用GDI+,可以创建并维护与Graphics对象分开的GraphicsPath 对象,它不依赖于Graphics对象的生存期。   变形和矩阵对象   GDI+提供了Matrix

对象,它是一种可以使变形(旋转、平移、缩放等) 简易灵活的强大工具,Matrix对象需与要被变形的对象联合使用。对于GraphicsPath类,我们可以使用其成员函数Transform接收Matrix参数用于变形。

void CGdiexampleDlg::OnTransformationMatrix() {  // TODO: Add your command handler code here  
  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);    
GraphicsPath path;    
path.AddRectangle(Rect(250, 20, 70, 70));  graphics.DrawPath(&Pen(Color::Black, 1), &path); // 在应用变形矩阵之前绘制矩形  // 路径变形   
 Matrix matrix1, matrix2;    
matrix1.Rotate(45.0f); //旋转顺时针45度  path.Transform(&matrix1); //应用变形  graphics.DrawPath(&Pen(Color::Red, 3), &path);  matrix2.Scale(1.0f, 0.5f); //转化成为平行四边形法则  path.Transform(&matrix2); //应用变形  

graphics.DrawPath(&Pen(Color::Blue, 3), &path); }  
  图5演示了正方形经过旋转和拉伸之后的效果:黑色的为原始图形,红色的为旋转45度之后的图形,蓝色的为经过拉伸为平行四边形后的图形。

图5 GDI+变形和矩阵对象
  可伸缩区域   GDI+通过对区域(Region)的支持极大地扩展了GDI。在GDI 中,区域存储在设备坐标中,可应用于区域的唯一变形是平移。但是在GDI +中,区域存储在全局坐标(世界坐标)中,可对区域利用变形矩阵进行变形(旋转、平移、缩放等)。

void CGdiexampleDlg::OnScalableRegion() {  // TODO: Add your command handler code here  CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建GraphicsPath  GraphicsPath path;  path.AddLine(100, 100, 150, 150);  

path.AddLine(50, 150, 150, 150);  path.AddLine(50, 150, 100, 100);  //创建Region  Region region(&path);  //填充区域  graphics.FillRegion(&SolidBrush(Color::Blue), ®ion);  //区域变形    
Matrix matrix;    
matrix.Rotate(10.0f); //旋转顺时针20度  matrix.Scale(1.0f, 0.3f); //拉伸  region.Transform(&matrix); //应用变形  //填充变形后的区域    
graphics.FillRegion(&SolidBrush(Color::Green), ®ion); }  
  上述程序中以蓝色填充一个三角形区域,接着将此区域旋转和拉伸,再次显示,其效果如图6。

图6 GDI+区域变形
丰富的图像格式支持   GDI +提供了Image、Bitmap 和Metafile 类,方便用户进行图像格式的加载、操作和保存。GDI+支持的图像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,几乎涵盖了所有的常用图像格式。

void CGdiexampleDlg::OnImage() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  Image image(L "d://1.jpg");  //在矩形区域内显示jpg图像  Point destPoints1[3] =  {   Point(10, 10), Point(220, 10), Point(10, 290)  };  graphics.DrawImage(&image,

destPoints1, 3);  //在平行四边形区域内显示jpg图像    
Point destPoints2[3] =  {   Point(230, 10), Point(440, 10), Point(270, 290)  };  graphics.DrawImage(&image, destPoints2, 3); }  
  上述程序将D盘根目录下文件名为"1.jpg"的jpg图像以矩阵和平行四边形两种方式显示,效果如图7。

图7 GDI+多种图像格式支持
  由此我们可以看出,GDI+在图像显示和操作方面的确比GDI简单许多。回忆我们在《Visual C++中DDB与DIB位图编程全攻略》一文中所介绍的用GDI显示位图的方式,其与GDI+图像处理的难易程度真是有天壤之别。 Alpha混合   Alpha允许将两个物体混合起来显示

,在3D气氛和场景渲染等方面有广泛应用。它能"雾化"图像,使得一个图像着色在另一个半透明的图像上,呈现一种朦胧美。我们知道,一个像素可用R,G,B三个维度来表示,我们可以再加上第4个即:Alpha维度(channel),表征透明程度。

void CGdiexampleDlg::OnAlphaBlend() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建ColorMatrix  ColorMatrix ClrMatrix =  {     
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,   0.0f, 1.0f, 0.0f, 0.0f, 0.0f,   0.0f, 0.0f, 1.0f, 0.0f, 0.0f,   0.0f, 0.0f, 0.0f, 0.5f, 0.0f,   0.0f, 0.0f, 0.0f, 0.0f, 1.0f  };  //将ColorMatrix赋给  
ImageAttributes  ImageAttributes ImgAttr;  ImgAttr.SetColorMatrix(&ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);  //在矩形区域内显示jpg图像    
Image img1(L "d://1.jpg");    
Point destPoints1[3] =  {   Point(10, 10), Point(220, 10), Point(10, 290)  };  graphics.DrawImage(&img1, destPoints1, 3);  //Alpha混合    
Image img2(L "d://2.jpg");    
int width, height;    
width = img2.GetWidth();    
height = img2.GetHeight();    
graphics.DrawImage(&img2, RectF(10, 10, 210, 280), 0, 0, width, height,UnitPixel, &ImgAttr);  //在平行四边形区域内显示jpg图像    
Point destPoints2[3] =  {   Point(230, 10), Point(440, 10), Point(270, 290)  };  graphics.DrawImage(&img1, destPoints2, 3);  //Alpha混合    
graphics.DrawImage(&img2, destPoints2, 3, 0, 0, width, height, UnitPixel,&ImgAttr); }  
  上述程序中将D盘根目录下文件名为"1.jpg"的图像以矩阵和平行四边形两种方式显示,然后将文件名为为"2.jpg"的图像与之进行混合,其效果如图8。

图8 GDI+ Alpha混合
  为了能进行Alpha混合,我们需要使用ImageAttributes类和ColorMatrix矩阵,ImageAttributes可以进行颜色、灰度等调整从而达到控制图像着色方式的目的。ColorMatrix是ImageAttributes类大多数函数的参数,它包含了Alpha、Red、Green、Blue维度的值,以及

另一维w,顺序为RGBaw。   CGdiexampleDlg::OnAlphaBlend()函数中ColorMatrix的实例ClrMatrix中元素(4,4)的值为0.5,表示Alpha度的值为0.5(即半透明)。在ColorMatrix中,元素(5,5)的值恒定为1.0。我们把ClrMatrix的元素(0,0)修改为0.0,即使得图像2.jpg

的红色维度全不显示,再看效果,为图9。列位读者,我们以前在豪杰超级解霸中调整R,G,B值从而控制图像输出颜色的时候,调的就是这个东东!图9的效果很像破旧彩色电视机,红色电子枪"嗝"了。刚大学毕业时,俺那个叫穷啊,就买了这么个电视机,还看得很爽,

真是往事不堪回首!

图9 GDI+中的ColorMatrix
强大的文字输出   GDI+拥有极其强大的文字输出处理能力,输出文字的颜色、字体、填充方式都可以直接作为Graphics类DrawString成员函数的参数进行设置,其功能远胜过GDI设备上下文的TextOut函数。

void CGdiexampleDlg::OnText() {  // TODO: Add your command handler code here    
CClientDC dc(this);  //创建Graphics对象  Graphics graphics(dc);  //创建20号"楷体"字体  FontFamily fontFamily1(L "楷体_GB2312"); // 定义"楷体"字样    
Font font1(&fontFamily1, 20, FontStyleRegular, UnitPoint);  //定义输出UNICODE字符串    
WCHAR string[256];    
wcscpy(string, L "天极网的读者朋友,您好!");  //以蓝色画刷和20号"楷体"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 10),&SolidBrush(Color::Blue));  //定义字符串显示画刷  LinearGradientBrush linGrBrush(Point(30, 50), Point(100, 50), Color(255, 255,0, 0), Color(255, 0, 0, 255));  //

以线性渐变画刷和创建的20号"楷体"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font1, PointF(30, 50),&linGrBrush);  //创建20号"华文行楷"字体    
FontFamily fontFamily2(L "华文行楷"); // 定义"楷体"字样    
Font font2(&fontFamily2, 20, FontStyleRegular, UnitPoint);  //以线性渐变画刷和20号"华文行楷"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 90),&linGrBrush);  //以图像创建画刷    
Image image(L "d://3.jpg");    
TextureBrush tBrush(&image);  //以图像画刷和20号"华文行楷"显示字符串    
graphics.DrawString(string, (INT)wcslen(string), &font2, PointF(30, 130),&tBrush);  //创建25号"华文中宋"字体    
FontFamily fontFamily3(L "华文中宋"); // 定义"楷体"字样    
Font font3(&fontFamily2, 25, FontStyleRegular, UnitPoint);  //以图像画刷和20号"华文行楷"显示字符串  graphics.DrawString(string, (INT)wcslen(string), &font3, PointF(30, 170),&tBrush); }  
  上述代码的执行效果如图10所示,字体、颜色和填充都很丰富!

图10 GDI+文本输出
5.GDI与GDI+的比较   GDI+相对GDI而言主要在编程方式上发生了巨大的改变。   GDI的核心是设备上下文,GDI函数都依赖于设备上下文句柄,其编程方式是基于句柄的;GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个Graphics 对象,就可以用面向对象

的方式调用其成员函数进行图形操作,编程方式是基于对象的。   GDI在使用设备上下文绘制线条之前,必须先调用SelectObject 以使钢笔对象和设备上下文关联。其后,在设备上下文中绘制的所有线条均使用该钢笔,直到选择另一支不同的钢笔为止。

CGdiexampleDlg::OnGdiDrawLine函数中的下列语句完成的就是这个功能:

//创建绘制正旋曲线的pen并将其选入设备上下文   
CPen pen(PS_SOLID,1,RGB(255,0,0));   
HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle()); … //创建绘制x轴的pen并将其选入设备上下文 CPen penx  
(PS_SOLID,1,RGB(0,0,255)); dc.SelectObject(penx.GetSafeHandle()); … //恢复原先的pen dc.SelectObject(oldObject);  
  但是,在GDI+中,只需将Pen对象直接作为参数传递给Graphics类的DrawLine等方法即可,而不必使Pen对象与Graphics对象关联,例如CGdiexampleDlg::OnGdipDrawLine函数中的下列语句:

Pen myPen(Color::Red); myPen.SetWidth(1); … graphics.DrawLine(&myPen,i,100*sin(2*(i/(rect.right/5.0))*PI), i+1,100*sin(2*((i+1)/(rect.right/5.0))*PI)); … graphics.DrawLine(&myPen,0,0,rect.right,0);  
  GDI中有当前位置的概念,所以在使用GDI绘制线条前应该先使用MoveTo移动当前位置,再使用LineTo画线,例如:

//绘制正旋曲线 dc.MoveTo(0,0) ;   
for(int i=0;i<rect.right;i++) {  dc.LineTo(i,100*sin(2*(i/(rect.right/5.0))*PI)); }  
  而GDI+中则没有当前位置的概念,画线函数中可以直接指定起点和终点,例如:

graphics.DrawLine(&myPen,0,0,rect.right,0);  
  6.结论   鉴于GDI+良好的易用性和其具有的强大功能,我们建议尽快抛弃GDI编程方式,因为我们没有必要将时间浪费在无意义的重复代码的设计上。GDI+对GDI的增强,某种意义上类似于MFC对Windows API的整理和封装。作为一种良好的"生产工具",它必将大大

地促进开发时的"生产力"。
========

GDI+

https://msdn.microsoft.com/en-us/library/ms533798(VS.85).aspx

Purpose
Windows GDI+ is a class-based API for C/C++ programmers. It enables applications to use graphics and formatted text on both the video display and the printer. Applications based on the Microsoft Win32 API do not access graphics hardware

directly. Instead, GDI+ interacts with device drivers on behalf of applications. GDI+ is also supported by Microsoft Win64.
Where applicable
GDI+ functions and classes are not supported for use within a Windows service. Attempting to use these functions and classes from a Windows service may produce unexpected problems, such as diminished service performance and run-time

exceptions or errors.
Note  When you use the GDI+ API, you must never allow your application to download arbitrary fonts from untrusted sources. The operating system requires elevated privileges to assure that all installed fonts are trusted.
Developer audience
The GDI+ C++ class-based interface is designed for use by C/C++ programmers. Familiarity with the Windows graphical user interface and message-driven architecture is required.
Run-time requirements
GDI+ can be used in all Windows-based applications. GDI+ was introduced in Windows XP and Windows Server 2003. For information about which operating systems are required to use a particular class or method, see the More Information

section of the documentation for the class or method. GDI+ is available as a redistributable for earlier operating systems. To download the latest redistributable, see http://go.microsoft.com/fwlink/?LinkID=20993.
Note  If you are redistributing GDI+ to a down-level platform or a platform that does not ship with that version of GDI+ natively, install Gdiplus.dll in your application directory. This puts it in your address space, but you should use

the linker's /BASE option to rebase the Gdiplus.dll to prevent address space conflict. For more information, see /BASE (Base Address).
In this section
Topic Description
Overview
General information about GDI+.
Using
Tasks and examples using GDI+.
Reference
Documentation of GDI+ C++ class-based API.
 
Related topics
Windows GDI
DirectX
Windows Image Acquisition
OpenGL
Windows Multimedia
  
Send comments about this topic to Microsoft
Build date: 3/6/2012
Community Additions
ADD
Hardware acceleration for GDI+?

Do you plan to
add
hardware support for GDI+
?
It's much ease API for WYSIWYG style then D2D.

23w
6/19/2015
Developer requirements
In order to use GDI+ classes you will need to:
- #include <Gdiplus.h>
- add Gdiplus.lib to the linker - Input - Additional Dependencies in the project properties

Raffaele Rialdi [MVP]
10/20/2011
Windows 7 + GdiPlus
Hallo,

ich habe eine MFC-GUI-Applikation mit GdiPlus mit VS 2008 entwickelt. Diese läuft unter Windows XP SP2 und 3 problemlos. Übertrage ich das Programm an einen Rechner mit Windows 7, so ist die Anzeige der Bilder verschwommen und mit einem

schwarzem Rahmen links und rechts versehen. Zum Enzeigen der Bilder wird die Funktion DrawImage genutzt. Zuvor wurde das Bild mit GetThumbnailImage an die

Fenstergröße angepasst. Wo liegt hier das Problem? Kann mir einer den Grund nennen.
========

Windows GDI绘图-入门篇

http://blog.csdn.net/xdrt81y/article/details/19546739

在窗口中绘图                                     
图形设备接口(GDI)与设备描述表(DC)
图形设备接口(GDI)
许多MS-DOS程序都直接往视频存储区或打印机端口输送数据, 
这种做法的不利之处在于需要对每种显示卡或打印机类型提供相应的驱动程序。
Windows则提供了一抽象的接口,称之为图形设备接口(GDI)。
Windows己经提供了各种显示卡及打印机的驱动程序,
这样我们的程序就可以不必关心与系统相连的显示卡及打印机的类型。
我们的程序可以通过调用GDI函数和硬件打交道,
而各种GDI函数会自动参考被称为设备环境(CDC)的数据结构。
Windows会自动将设备环境结构映射到相应的物理设备,并且会提供正确的输入输出指令。
设备描述表(DC)
在Windows环境中,各程序的输出必须限制在自己的窗口中。
GDI使用一种简单的机制保证在窗口中画图的各程序遵循这个规则。
这种机制即为设备描述表(DC);
当Windows程序在屏幕、打印机或其它设备上画图时,
它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表表示的逻辑意义上的"显示平面"上去。
设备描述表是深寓于Windows中的一种数据结构,
它包含GDI需要的所有关于显示平面情况的描述字段,包括相连的物理设备和各种各样的状态信息。
设备描述表对象
在使用MFC编制Windows程序时,设备描述表具有更加突出的作用。
除了可作为通往各种设备的桥梁之外,设备描述表对象还封装了程序用来产生输出的GDI函数。
在MFC中,你不用捕获设备描述表句柄和调用GDI输出函数,至少不必直接捕获和调用,
而是通过创建一设备描述表对象并调用它的成员函数来画图。
设备描述表句柄
在平面上画图之前,Windows程序从GDI获取设备描述表句柄,
并在每次调用完GDI输出函数时将句柄返回给GDI。
在MFC应用程序中获取设备描述表的一种方法是调用CWnd::GetDC,
它返回指向表示Windows设备描述表的CDC对象的指针。
在画图完毕,要用CWnd::ReleaseDC释放指针(注:在OnPaint处理程序中,不用显示调用这两个函数)
CDC派生类
为了避免获取和释放设备描述表所带来的麻烦,
MFC提供了一些CDC派生类,如CPaintDC,CClientDC,CWindowDC这些类被设计为可直接进行实例化。
各个类的构造函数和析构函数调用相应的函数捕获和释放设备描述表,
从而便得设备描述表的使用非常方便简捷。
例如:
CPaintDC dc(this);
//Do some drawing
传送给类构造函数的指针确定了设备描述表所属的窗口。
设备描述表属性
当使用CDC输出函数在屏幕上画图时,
输出的某些特性并没有在函数调用过程中规定(采用系统默认的属性画图),
但我们可以通过设备描述表自身获得或得新设置属性。
MFC提供了获得和设置这些属性的函数,我们可以用这些函数方便地改变画图时的默认属性。
例如:
CDC::SetTextColor//设置文本颜色
CDC::GetTextColor//获得文本颜色
CDC::SetBkColor//设置背景颜色
CDC::SetBkMode//设置背景模式
CDC::SetMapMode//设置映射模式
CDC::CDC::SetROP2//设置绘图模式
CDC::MoveTo//当前位置
CDCL::SelectObject//当前画笔,当前画刷,当前字体
(如果想忽略背景色,可将背景设置为"Transparent",dc.SetBkMode(TRANSPARENT);)
SelectObject函数
最常用来定义设备描述表属性的CDC函数是SelectObject。
例如:
CPen pen(PS_SOLID,2,RGB(0,192,0));
CPen* pOldPen = dc->SelectObject(&pen);//把新的CDC对象选入设备描述表,同时保存旧的CDC对象
dc->Ellipse(0,0,100,100);
dc.SelectObject(pOldPen);//
绘图模式与SetROP2
GDI将像素点输出到逻辑平面上时,它不只是简单地输出像素点颜色。
相反,它能过一系列的布尔运算将输出像素点的颜色和目标位置上像素点的颜色合成一起。
它所使用的逻辑关系由设备描述表当前的绘图模式确定。
使用CDC::SetROP2(Set Raster Operation To)可更改绘图模式。
默认绘图模式为R2_COPYPEN,它将将像素点复制到显示平面上。
映射模式
默认映射模式
固定比例映射模式
可编程映射模式
简单地说,映射模式是设备描述表的属性,用于确定从逻辑坐标值到设备坐标值的转换方式。
默认的映射模式
默认的映射模式使MM_TEXT,它使以象素为单位的。X轴向左为正,Y轴向下为正。默认的坐标原点在左上角。
固定比例映射模式
固定比例的映射模式有MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH、MM_TWIPS种。
它们默认的坐标原点都使在左上角。其区别在于每一个逻辑单位对应的物理大小不一样。
所对用的逻辑单位分别为0.1毫米,0.01毫米,0.01英寸,0.001英寸,1/1440英寸(0.0007英寸)。
可变比例映射模式(可编程映射模式)
对于可变比例的映射模式用户可以自己定义一个逻辑单位代表的大小,其大小可以任意。
也可以让这个大小随环境改变而改变。有MM_ISOTROPIC,MM_ANISOTROPIC这两种映射模式。
其逻辑单位的大小等于视口范围和窗口范围的比值。
两者的不同在于前者要求X轴和Y轴的度量单位必须相同,而后者没有这样的限制。
 
可编程映射模式
MM_ISOTROPIC和MM_ANISOTROPIC是由用户决定从逻辑坐标值转换成设备坐标值的方式,
即是由用户决定一个逻辑单位等于多少个设备单位(cm,mm,m等),而不是由Windows决定。
所以被称之为可编程映射模式;
MM_ISOTROPIC和MM_ANISOTROPIC映射模式最常用于根据窗口尺寸按比例自动调节画图的输出大小的场合。
MM_ISOTROPIC和MM_ANISOTROPIC映射模式的区别
前者中X方向和Y方向具有同一个缩放比例因子,而后者两个方向可以单独缩放;
例子:
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(500,500);//设置窗口范围,将窗口的逻辑尺寸高为500单位×500单位
dc.SetViewportExt(rect.Width(),rect.Height());//设置视口范围
dc.Ellipse(0,0,500,500);
注:窗口的尺寸以逻辑单位计算,视口的尺寸以设备单位或像素计算。
特别说明
MM_TEXT为默认映射模式,其原点在窗口的左上角,X轴的正向向右,Y轴的正向向下,
并且一个逻辑单位对应于设备坐标下的一个象素
其它映射模式则原点不变,只是Y轴会翻转使正向朝上,并且逻辑单位被按比例转换为实际距离大小,而不是像素数。
需要注意的是,使用公制映射模式(非默认映射模式)时,为使输出可见,Y坐标必须为负值。
例如:
dc.Rectangle(0,0,200,100);//默认模式下画图
dc.SetMapMode(MM_LOENGLISH);//改变映射模式
dc.Rectangle(0,0,200,-100);//画图
传送给CDC输出函数的是逻辑坐标值。
设备坐标值是指窗口中相应的像素点位置。
 
Windows中的几种坐标体系
1、屏幕坐标
屏幕坐标描述物理设备(显示器、打印机等)的一种坐标体系,
坐标原点在屏幕的左上角,X轴向右为正,Y轴向下为正。度量单位是象素。
原点、坐标轴方向、度量单位都是不能够改变的。
2、设备坐标(又称物理坐标)
设备坐标是描述在屏幕和打印机显示或打印的窗体的一种坐标体系。
默认的坐标原点是在其客户区的左上角。X轴向右为正,Y轴向下为正。
度量单位为象素。原点和坐标轴方向可以改变,但是度量单位不可以改变。
3、逻辑坐标
逻辑坐标是在程序中控制显示,打印使用的坐标体系。
该坐标系与定义的映射模式密切相关。默认的映射模式是MM_TEXT。
我们可以通过设置不同的映射模式来改变该坐标体系的默认行为。
注意:
首先,要记住两点:
1。设备坐标的(0, 0)点始终是客户区的左上角。 
2。我们在绘图时指定的点全部都是逻辑坐标点。
坐标转换
调用CDC::LPtoDP可以将逻辑坐标转换为设备坐标;
高用CDC::DPtoLP可以将设备坐标转换为逻辑坐标;
什么时候用什么坐标系
可以认为CDC的所有成员函数都以逻辑坐标作为参数
可以认为CWnd的所有成员函数都以设备坐标作为其参数
所有选中测试操作中都应该考虑设备坐标。区域的定义应采用设备坐标。
某些像CRect::PtInRect之类的函数只有在采用设备坐标参数时才会保证有正确的结果
将一些需要长期使用的值用逻辑坐标或物理坐标来保存。
如果用逻辑坐标来保存某点的坐标的话,那么只要用户对窗口进行一下滚动,该点的坐标就不再有效了。
鼠标单击事件下得到的坐标都是设备坐标!
移动原点
CDC::SetViewportOrg()移动视口的原点 
CDC::SetWindwosOrg()移动窗口的原点
正常情况下,只能使用其中之一,同时使用两个会搞提一团糟。
获取设备信息
获得系统分辨率
CClientDC dc(this);
int cx = dc.GetDeviceCaps(HORZRES);
int cy = dc.GetDeviceCaps(VERTRES);
用GDI绘图
MFC的CDC类将相关的GDI函数封装在类成员函数中。
在设备描述表对象中或通过指向设备描述表对象的指针可以方便地调用这些函数
画线
dc.MoveTo(0,0);//设置当前位置
dc.LineTo(100,100);//指定一个线的终点位置
画圆
dc.Ellipse(0,0,100,100);
CRect rect(0,0,100,100);
dc.Ellipse(rect);
画矩形
dc.Rectangle(0,0,100,100);//画一个带直角的矩形
GDI画笔和CPen类
Windows用当前选入设备描述表的画笔绘制直线和曲线,
并给用Rectangle,Ellipse以及其他图形生成函数画出的图形镶画边框。
默认画笔画出的是一个像素点宽的黑色实线。
如果要改变画线方式,则需创建一个GDI画笔,并由CDC::SelectOjbect将它选设备描述表
MFC用类CPen表示GDI画笔。
创建画笔的方法
创建画笔的最简单的方法是构造一个CPen对象并把定义画笔所用的参数都传送给该对象
CPen pen(PS_SOLID,1,RGB(255,0,0));
创建画笔的第二种方法是构造 一个汉有初始化的CPen对象并调用CPen::CreatePen:
CPen pen;
pen.CreatePen(PS_SOLID,1,RGB(255,0,0));
创建画笔的第三种方法是构造一个没有初始化的CPen对象,向LOGPEN结构中填充描述画笔特性的参数
,然后调用CPen::CreatePenIndirect生成画笔
CPen pen;
LOGPEN lp;
lp.lognStyle = PS_SOLID;//样式
lp.lopnWidth.x = 1;//宽度
lp.lopnColor = RGB(255,0,0);//颜色
pen.CreatePenIndirect(&lp);
从以上的画笔定义可以看出,定义画笔需要三个特性:样式,宽度和颜色。
画笔的样式说明请参见MSDN
GDI画刷和CBrush类
在默认情况下,则Rectangle,Ellipse以及其它CDC函数画出的封闭图形填充着白色像素点。
通过创建GDI画刷并在画图之前将它选入设备描述表,可以改变图形的填充颜色。
MFC的CBrush类封装了GDI画刷。
画刷有三种基本类型:单色、带阴影和带图案。
单色画刷填充的单色。
阴影线画刷采用预先定义好的交叉线图案填充图形。
图案画刷用位图来填充图形。
创建单色画刷的方法类似于创建画笔,如下所示:
CBrush brush(RGB(255,0,0));

CBrush brush;
brush.CreateSolidBrush(RGB(255,0,0));
创建带阴影线画刷
CBrush brush(HS_DIAGCROSS,RGB(255,0,0));//阴影索引,COLORREF值

CBrush brush;
brush.CreateHatchBrush(HS_DIAGCROSS,RGB(255,0,0));
HS_DIAGCROSS是可供选择的六种阴影线样式之一,其它样式请参见MSDN
注意:
在用阴影线画刷填充时,除非用CDC::SetBkColor改变设备描述表的当前背景色,
或用CDC::SetBkMode把背景模式OPAQUE改成TRANSPARENT,禁止背景填充,
否则WIndows就以默认的背景钯(白色)填充阴影色线间的空白处。
========

Windows GDI绘图-实践篇一

http://blog.csdn.net/xdrt81y/article/details/19547469

一、基础

  GDI的绘图函数基本上都是有状态的,所有的函数都要求一个HDC类型的句柄。这个HDC的获得有几个途径BeginPaint,GetWindowDC, GetDC.他们的参数都只需要一个HWND就差不多了。记得调用了BeginPaint后要调用EndPaint进行清理,调用GetWindowDC和GetDC后要调

ReleaseDC进行清理。在MFC代码中常常遇到的CDC CPaintDC CWindowDC CClientDC。在这里稍作解释。

  CDC :例如用GDI画矩形要Rectangle(hDC,...),而使用CDC则是dc.Rectangle(...),由此可见CDC主要是把原本需要HDC作为参数的GDI函数封装了一下,HDC成了它的一个成员变量。

  CPaintDC CWindowDC CClientDC:他们都是从CDC继承,分别是对上面所说的BeginPaint,GetWindowDC, GetDC调用对进行封装(CPaintDC构造时调用BeginPaint,析构时调用EndPaint,其余同理)。

  BeginPaint:一般用在对WM_PAINT的响应函数中使用

  GetWindowDC:可获得整个Window的HDC,而GetDC仅能获得客户区的HDC,区别就在于--

  前者有效地绘制区域是整个窗口(边框、标题栏、客户区的总和)。
  后者有效地绘制区域仅限于客户区。

  两者的坐标系都是相对坐标而非屏幕坐标,原点是(0,0)。即以自己可绘制区域的左上角作为原点。

  这里可以顺带的讲讲RECT了,RECT是一个结构,依次有4个成员left,top,right,bottom用来代表一个矩形区域。CRect从RECT继承,提供了一些常用的操作(例如说位移,缩小等等),其实就是改变4个成员的值。完全不用CRect也可以。许多GDI函数都要求一个RECT作

为参数,或者类似的用(x,y,cx,cy)作参数,其实也就是一个RECT变种,用了宽度和高度罢了。

二、实例教程

  基础知识介绍完毕,开始实例教程:

 我们以如何绘制一个具有平面风格的状态栏为例:

  首先从CStatusBar继承一个类:CStatusBarNew。(如果无法通过类向导做这件事,而你又对MFC的MESSAGEMAP等等东西不熟悉,可以从CStatusBarCtrl继承一个,待生成代码后,把所有的CStatusBarCtrl改为CStatusBar)

  在此,只需要重写WM_PAINT和WM_ERASEBKGND这两个消息的响应函数。

BOOL CStatusBarNew::OnEraseBkgnd(CDC* pDC) 
{
// TODO: Add your message handler code here and/or call default
CRect rect;
GetWindowRect(&rect);
ScreenToClient(&rect);
CBrush brush(0xf2f2f2);
pDC->FillRect(&rect, &brush);
return TRUE;
}

  上面函数把状态栏背景用0xf2f2f2这种颜色填充。

void CStatusBarNew::OnPaint() 
{
CPaintDC cDC(this); // device context for painting
// TODO: Add your message handler code here
CRect rcItem;
cDC.SetBkMode(TRANSPARENT);
cDC.SelectObject (::GetStockObject (NULL_BRUSH));//选入画刷

// 获取字体
CFont* pfont = GetFont();
CFont* def_font;
if (pfont)
def_font = cDC.SelectObject(pfont);//选入字体

CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(0xBD, 0xBA, 0xBD));
CPen* pOldPen = cDC.SelectObject(&pen);//选入画笔

CBrush br(0x00f2f2f2);
for ( int i = 0; i < m_nCount; i++ )
{
GetItemRect (i, rcItem);
//填充面板背景
cDC.FillRect(rcItem, &br);
rcItem.bottom--;
if(i == 0) rcItem.left += 2;

//对每个面板画圆角矩形
cDC.RoundRect(rcItem, CPoint(5, 5));

//画面板上的文字
UINT nNewStyle = GetPaneStyle(i);
//如果style为SBPS_DISABLED,则跳过不画
if ((nNewStyle & SBPS_DISABLED) != 0) continue;
CString text = GetPaneText(i);
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_TOP | DT_LEFT;
rcItem.left += 3;
rcItem.top += 3;
cDC.DrawText(text, rcItem, uFormat);

if (pfont)
cDC.SelectObject(def_font);//恢复字体

//画右下角小标志(这里画了六个小圆圈)
if (GetStyle() & SBARS_SIZEGRIP)
{
CRect rc;
GetClientRect(&rc);
rc.left = rcItem.right;
rc.right--;
rc.bottom--;
rc.left = rc.right - rc.Width() / 4;
rc.top = rc.bottom - rc.Width();
int w = rc.Width();
rc.top++;
rc.left++;
cDC.SelectObject(GetStockObject(GRAY_BRUSH));
cDC.Ellipse(&rc);
rc.OffsetRect(-w, -w);
cDC.Ellipse(&rc);
rc.OffsetRect(w, 0);
cDC.Ellipse(&rc);
rc.OffsetRect(-w, w);
cDC.Ellipse(&rc);
rc.OffsetRect(-w, 0);
cDC.Ellipse(&rc);
rc.OffsetRect(2 * w, -2 * w);
cDC.Ellipse(&rc);
}

cDC.SelectObject(pOldPen);//恢复画笔

}

  上面的函数我们可以多次看到SelectObject的调用,这就是前面所说的绘图函数基本上都是有状态的。这个状态保存在HDC中,而SelectObject则设置HDC的状态。通常称为选入。至于注释中的恢复是怎么回事呢?这要从CPen CBrush CFont等等说起了,它们是对GDI

对象的封装。GDI对象通过CreatePen CreateBrush CreateFont等等函数创建,返回一个HGDIOBJ。这些对象不使用的时候需要销毁,用DeleteObject函数,但是如果一个HGDIOBJ被选入到一个HDC中的时候,它就不能被销毁,这样就造成了GDI资源的泄漏。解决这一问题通

常有两种做法:

  第一种,就是上面代码中看到的:

  先保存原来的HGDIOBJ,def_font = cDC.SelectObject(pfont);

  用完了之后再恢复原来的 cDC.SelectObject(def_font);

  这样做,就保证了pfont能被正确销毁,至于原来的def_font能不能被销毁,就不关我们的事了。

  第二种,利用了系统的库存对象。库存GDI对象是windows系统预先创建的,不需要应用程序销毁。所以,不需要保存原来的HGDIOBJ,直接像这样

  SelectObject (hdc, ::GetStockObject (NULL_BRUSH));

  或者cDC.SelectStockObject(NULL_BRUSH);

  就可以保证HDC中没有被选入任何我们自己创建的画刷了。

  这两种方法各有好处,视情况选用。

  另外上面说大部分GDI函数都是有状态的,有一个例外就是FillRect函数,它靠一个传给他的画刷进行填充。

三、技巧

  实例讲述完毕,接下来有一些补充技巧:

  1. GDI绘图技巧的学习:通过阅读、运行、调试别人源代码获得经验这条路径是最快的。
  2.GDI程序的调试

  调试GDI一般来说比其他程序困难,但是掌握了一些技巧也就没什么障碍了。调试GDI的时候,将IDE和代调试的程序窗口在桌面上尽量分开排列,不要重叠在一起。这样你能通过单步执行,看到每一步的绘图效果。

  为配合上述策略,在应用程序初始化的时候加上下面一句:

  #ifdef _DEBUG 
  GdiSetBatchLimit(1);
  #endif

  这能保证调试时每一条GDI函数调用能马上产生效果。因为Windows为了性能优化,可能会分批处理GDI调用。

  3.内存绘图

  首先理解内存绘图,即把要绘制的东西先在内存中画好,然后一次性的画到屏幕上来。内存绘图经常用来防止闪烁。因为闪烁的原因是因为反差太大。例如你的绘图过程是先用白色擦除整个窗口,然后再将黑色的文字画到屏幕上来,这样在窗口重绘的时候,原本黑色

文字区域就会白光一闪,然后再出现文字,也就是我们说的闪烁了。而内存绘图的过程呢,是先创建一个内存DC,然后在这个DC上把要绘制的图形画好,之后一次性的填到屏幕上去。

  示例代码如下:

HDC hDestDC;
RECT rc;
//..此处得到目标的HDC和目标的RECT
HDC hdc = ::CreateCompatibleDC (hDestDC);
HBITMAP hBitmap = ::CreateCompatibleBitmap (hDestDC, rc.right, rc.bottom);
HBITMAP hOldBitmap = ::SelectObject (hDC, hBitmap);
//... 此处用hdc进行绘图
//...
::BitBlt (m_hDestDC, rc.left, rc.top, rc.Width(), rc.Height(), hDC, rc.left, rc.top, SRCCOPY);
::SelectObject (hDC, hOldBitmap);

  当然,这样用起来不太方便,可以将这些操作封装到一个叫CMemDC的对象中,利用构造和析构自动进行这些操作。直接使用CMemDC还有一个好处,调试GDI时,如果图形都在内存中绘制,那么还是看不到绘图过程。

  代码如果这样写:

CRect rc;
GetWindowRect(&rc);
#ifdef _DEBUG
CPaintDC dc;
#else
CPaintDC cdc;
CMemDC dc(cdc.m_hDC, &rc);
#endif

  那么就既能享受内存绘图的好处又能方便调试了。
========

GDI+Window是什么意思 GDI+Window错误的解决办法

http://www.jb51.net/os/other/58679.html

最近有网友阿豳问小编这样一个问题:GDI+Window是什么意思?为什么在我家电脑下边显示,但点不开,查属性它正在运行,不知道是什么原因
最近有网友“阿豳”问小编这样一个问题:GDI+Window是什么意思?为什么在我家电脑下边显示,但点不开,查属性它正在运行,不知道是什么原因。对于这个电脑故障问题其实比较少见,多数是因为系统出现故障或者某些软件版本不兼容导致的,在网上查阅相关资料

后发现,也有不少朋友遇到此类问题,下面小编汇总下解决办法。
其实GDI是微软的窗口显示模块,gdi+是它的扩充服务,GDI+Window就是Windows窗口服务,相当于打来一个窗口。这种问题一旦出现,很可能表示系统关键部位有问题,可能是软件问题或者系统问题,而这种窗口往往都不能直接关闭,一般需要进入任务管理器里面结束

掉。

GDI+Window是什么意思
⒈)产生GDI+Window故障可能是软件问题
比如出现这种问题前我们是否安装有什么软件,有没有什么软件升级过,或者电脑是否中病毒等等,解决方法是先进入任务管理器结束掉GDI+Window错误窗口,然后进行杀毒,最后不行可以尝试着卸载一些可疑软件试试。
下面给大家看一个网友遇到的因软件问题导致GDI+Window错误的例子:
 症状:电脑开机之后,当你单击用户名输入密码的时候,进入系统时,这时就出现错误了。弹出一个框“GDI+ Windows winlogon.exe错误”如果你点击【确定】按钮,这时就重启了。单击【取消】按钮就进入调试状态。这样你是可以进入系统的。但是进入系统时,你

启动的任何程序也都会错误,会弹出一个框提示“t_filemap::lnt失败;创建Mutex(filemap_mutex)失败;”你点开的任何程序都会弹出这样一个框,但是点击【确定】按钮之后,这个程序是可以正常运行的。
今天我搜索了一下这个问题,结果有人说是搜狗输入法引起的。具体的说是3.2版本引起的,因为我原来一直都在用搜狗输入法,一直没有出现过这样的问题。最近升级了就出现了这样的问题。接下来,我就把搜狗输入法卸载了。重起电脑,一切OK.

⒉)产生GDI+Window故障可能系统问题
除了一般软件问题容易导致GDI+Window,再一种可能就是系统问题了,一般可能是系统关系文件出现损坏导致的,比如电脑使用较久,出现硬盘坏道,很可能导致系统核心文件丢失,从而导致系统问题,解决办法,当然是建议采用重装系统了,对于方法一如果解决不了

,其实重装系统或者系统还原是最有效的解决办法,遇到此类故障的朋友不妨参考下。 
========

GDI图形绘制

http://www.cnblogs.com/yangdanny/p/4634536.html

上两篇我们学习了文本字符输出以及Unicode编写程序,知道如何用常见Win32输出文本字符串,这一篇我们来学习Windows编程中另一个非常重要的部分GDI图形绘图。Windows的GDI函数包含数百个API可供我们使用,本篇把最常用的GDI绘图做一个讲解。GDI可以绘制点、

直线曲线、填充封闭区域、位图以及文本,其中文本部分已经在上一篇中将了,请参考【Windows编程】系列第三篇:文本字符输出。

跟前面的GDI对象一样,本篇的这些绘图函数也必须要设备上下文句柄(HDC)作为函数参数,从前文我们知道,HDC可以在处理WM_PAINT的时候用BeginPaint函数获取,也可以从GetDC、GetWindowDC拿到。

既然是画图,就少不了颜色的描述,Windows中的颜色有几种表示,其中COLORREF在GDI绘制中用的最多,它实际上是一个无符号32为整型。其中红、绿、蓝各占一个字节,最高字节不使用,如下图所示:

该值可以用Windows提供的RGB宏来生成,Windows中RGB的定义为:

#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
除此之外,Windows还有结构体RGBQUAD也表示颜色,这种一般用于位图结构信息中。

画像素点
Windows提供了SetPixel和GetPixel函数来设定和获取像素点的颜色。函数原型为:

COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor);
COLORREF GetPixel(HDC hdc, int nXPos, int nYPos);

该函数并不常使用。

画笔画刷
在图形绘制之前,可以创建画笔给后续的画图使用,创建画笔的API函数为:

HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
HBRUSH CreateSolidBrush(COLORREF crColor);
HBRUSH CreatePatternBrush(HBITMAP hbmp);
HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref);

它可以指定画笔风格,宽度和颜色。风格可以是实线、虚线、点虚线等,具体参考MSDN说明的各种类型。

画线条
Windows提供的画线条函数有十几个,常用的直线绘制为LineTo,多条线段一般用Polyline、PolylineTo、PolyPolyine等,曲线可以画椭圆、椭圆弧、贝塞尔样条曲线。这些函数的原型请参考MSDN,后面我们将用实例来演示这些函数的用法。

封闭区域填充
Windows的绘图如果是一个封闭区,则内部是可以填充的,当然如果你不显示填充,系统会用默认颜色来填,比如窗口背景色。我们也可以在绘制封闭图形之前创建画刷,如果把创建的画刷选入设备环境中,系统将用画刷填充内部区。常见的会封闭的绘图API函数有画直

角矩形Rectangle、圆角矩形RoundRect、椭圆Ellipse、扇形图Pie以及弦割图Chord。

位图输出
Windows关于位图的输出内容很多,包括设备相关和设备无关位图、以及位块转移、透明、缩放等等,本文仅针对位图画刷进行实例演示,其他内容将来可单独写一篇介绍。用位图做画刷时先要使用LoadImage函数加载位图文件,然后用CreatePatternBrush创建一个模式

画刷即可。

文本输出
这个在前面已经讨论过了,请参考【Windows编程】系列第三篇:文本字符输出一文。

绘图属性
在绘制图形时,环境设备有5个属性会影响大多数绘图:

画笔位置:在画线条时,会从画笔所在的位置开始画,画笔位置可以用MoveToEx函数来设置。

画笔:绘图时会采用当前环境中的画笔进行绘制,如果显示不创建,将会用系统默认的画笔。

背景:某些GDI会有透明和不透明的设置。

背景颜色:比如文本输出的间隙颜色。

绘制模式:比如划线是可以设置实线、虚线等,填充时可能有不同的填充绘制模式。

下面我们通过一个完整的实例,来演示上面这些常见函数的具体运用以及实际使用效果。

#include <windows.h>

static TCHAR szAppName[] = TEXT("GDI Test");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

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,            // window class name
                          szAppName,           // window caption
                          WS_OVERLAPPEDWINDOW, // window style
                          CW_USEDEFAULT,       // initial x position
                          CW_USEDEFAULT,       // initial y position
                          400,                 // initial x size
                          300,                 // 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, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

return msg.wParam;
}

//绘制指定属性的直线
static void DrawLine(HDC hDC, int x0, int y0, int x1, int y1, int style, int width, COLORREF color)
{
HPEN hPen = CreatePen(style, width, color);
HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);

MoveToEx(hDC, x0, y0, NULL);
LineTo(hDC, x1, y1);

SelectObject(hDC, hOldPen);
DeleteObject(hPen);
}

//绘制实心圆
static void DrawCircle(HDC hDC, int x, int y, int len, COLORREF color)
{
HBRUSH hBrush = CreateSolidBrush(color);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

HPEN hPen = CreatePen(PS_SOLID, 1, color);
HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);

Ellipse(hDC, x-len/2, y-len/2, x+len/2, y+len/2);

SelectObject(hDC, hOldBrush);
DeleteObject(hPen);

SelectObject(hDC, hOldPen);
DeleteObject(hOldBrush);
}

//绘制填充矩形
static void DrawRect(HDC hDC, int left, int top, int width, int height, int style, COLORREF color)
{
HBRUSH hBrush = CreateHatchBrush(style, color);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

Rectangle(hDC, left, top, left+width, top+height);

SelectObject(hDC, hOldBrush);
DeleteObject(hOldBrush);
}

//绘制位图填充矩形
static void DrawBmpRect(HDC hDC, int left, int top, int width, int height, LPCTSTR file)
{
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
HBRUSH hBrush = CreatePatternBrush(hBitmap);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

Rectangle(hDC, left, top, left+width, top+height);

SelectObject(hDC, hOldBrush);
DeleteObject(hOldBrush);
DeleteObject(hBitmap);
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC         hDC;
PAINTSTRUCT ps;

switch (message)
{
case WM_CREATE:
return 0;

case WM_PAINT:
{
hDC = BeginPaint(hWnd, &ps);
for (int i=10; i<50; i+=4)
{
SetPixel(hDC, i, 10, RGB(0, 0, 0)); //绘制像素点
}
                        //绘制不同模式的直线
DrawLine(hDC, 120, 30, 200, 30, PS_SOLID, 2, RGB(0,0,0));
DrawLine(hDC, 120, 50, 200, 50, PS_DASH, 1, RGB(100,0,200));
DrawLine(hDC, 120, 70, 200, 70, PS_DASHDOT, 1, RGB(100,250,100));
                        //绘制弧线、弦割线、饼图
Arc(hDC, 10, 30, 40, 50, 40, 30, 10, 40);
Chord(hDC, 10, 60, 40, 80, 40, 60, 10, 70);
Pie(hDC, 10, 90, 40, 110, 40, 90, 10, 100);

POINT pt[4] = {{90,130},{60,40},{140,150},{160,80}};
                        //绘制椭圆、矩形
Ellipse(hDC,pt[0].x, pt[0].y, pt[1].x, pt[1].y);
Rectangle(hDC, pt[2].x, pt[2].y, pt[3].x, pt[3].y);

//绘制贝塞尔曲线
PolyBezier(hDC, pt, 4);
                        //标出贝塞尔曲线的四个锚点
DrawCircle(hDC, pt[0].x, pt[0].y, 8, RGB(0,255,0));
DrawCircle(hDC, pt[1].x, pt[1].y, 8, RGB(0,0,255));
DrawCircle(hDC, pt[2].x, pt[2].y, 8, RGB(0,0,0));
DrawCircle(hDC, pt[3].x, pt[3].y, 8, RGB(255,0,0));
                        //绘制圆
DrawCircle(hDC, 100, 180, 60, RGB(0, 250, 250));
                        //绘制不同填充模式的矩形
DrawRect(hDC, 220, 20, 60, 40, HS_BDIAGONAL, RGB(255,0,0));
DrawRect(hDC, 220, 80, 60, 40, HS_CROSS, RGB(0,255,0));
DrawRect(hDC, 290, 20, 60, 40, HS_DIAGCROSS, RGB(0,0,255));
DrawRect(hDC, 290, 80, 60, 40, HS_VERTICAL, RGB(0,0,0));
                        //绘制位图
DrawBmpRect(hDC, 180, 140, 180, 100, TEXT("chenggong.bmp"));
                        //绘制文本
TextOut(hDC, 20, 220, TEXT("GDI画图输出测试程序"), 11);
}
EndPaint(hWnd, &ps);
return 0;

case WM_DESTROY:
PostQuitMessage(0);
return 0 ;
}

return DefWindowProc (hWnd, message, wParam, lParam);
}

本实例运行结果如下图所示,图中可以看到线条不平滑,这是因为Win32的画图函数是没有抗锯齿功能的,图越小,锯齿越明显。可以使用微软提供的GDI+绘图函数,具有抗锯齿效果。

Windows的GDI基本绘制其实并不难掌握,只要仔细阅读MSDN上API的详细使用说明就一定能正确使用,但是在创建GDI对象并使用后,一定要记得释放。
========

深度解读 - Windows 7核心图形架构细致分析

http://www.win7china.com/html/2904.html

如现在大家所想的那样,Windows7其实是Windows Vista的改进版。Windows 7在Windows Vista的基础上进行了大量的完善工作,也加入了不少新特性。Vista与其上一代XP相比,提供了非常大的改进,然而一方面这些改进过于巨大,用户乃至相应软件厂商(如,DirectX

10应用开发商)一时无法完全接受,另一方面,由于特性的不完全具备,Vista的表现没有想象之中的那么好。到了Windows 7,包括操作系统本身、软件厂商和用户都已经做好了准备,因此反响比Vista更好也就不难理解了。

图形界面一直是Windows系统的核心,而从Windows Vista开始,Windows就开始将提供一个富图形化的桌面图形界面作为要目,不仅仅是因为Vista和7的桌面本身就是一个3D应用程序,而是因为 Vista和7可以更好地发挥图形加速硬件的作用。从Windows Vista到Windows7

,操作系统与GPU的结合越来越紧密。

虽然人们经常可以听到Windows7的大更新在于一个DirectX 11.0 API,然而对于Windows系统的图形架构来说,虽然DirectX也很重要,不过这还不是全部。一个图形架构包括了如何利用GPU加速各种各样的图形应用(2D、3D、打印等)、如何显示到最终显示设备上,以及

包括设备检测、控制。Window 7在图形架构方面的更新主要有如下方面:

WDDM 1.1:新的驱动模型
DirectX 11:更新的Direct3D 11,以及相关的新Direct2D API
DXVA-HD:高清视频回放加速
显示设备连接和配置
色彩管理
高DPI输出和可读性
多GPU系统
联合显示适配器(又叫联合渲染)

下面,会就这些改进进行简单的介绍。

Windows 7核心图形架构
Windows应用程序使用各种如GDI(Graphics Device Interface,2D时代系统的主要图形接口)、Direct3D、OpenGL这样的API和系统图形组件通信,而系统组件通过WDDM(Windows Display Driver Model,又名Longhorn Display Driver Model)与硬件交互,从Vista起

,Windows就采用了和XP使用的XPDM不同的新的驱动模型:WDDM,使用的驱动模型在很大程度上决定了一个系统的图形特性。Vista使用的WDDM版本为1.0,而Windows7使用的改进版本为WDDM 1.1。

WDDM 1.1带来的改动挺多的,下面我们先来看看WDDM 1.0相对XPDM的改动,也就是Vista相对XP的改动

XPDM:非合成模型
 
WDDM:合成模型
 
在垂直同步之谜 XP/Vista与3D性能测试中, 笔者解释过XP/Vista的模型的不同之处,在WDDM模型下,所有的应用程序生成的显示画面最后会在DWM(Desktop Windows Manager,桌面窗口管理器)内进行合成为单一的最后输出画面,因此获得了更好的显示效果(天生的

所有程序垂直同步)、额外效果(缩略图、缩放)以及 可以支持更大的左面,不过,原始的WDDM 1.0只是实现了这个大架构上的转换,细节上仍未够完美:
 
在Vista下,GDI(绘制通常的2D窗口的API,此外还有很多其他图形操作使用GDI)和DirectX(3D应用API)具有着不同的处理方 法:Direct3D是硬件加速的,而GDI则不是(GDI在XPDM时是硬件加速的),因此Vista用户在一些图形程序上会感到比XP慢;而且GDI 应用程

序先经过CPU软件处理到系统内存上,再传输到显卡驱动分配的GDI显存区域上(也在系统内存当中),再由DWM负责将画面合成输出到显示器

Windows 7当中,GDI获得了硬件加速(这个见下面)——不过在混合使用GDI和Direct3D API的时候,GDI仍然无法硬件加速,不过,GDI直接输出到驱动的GDI显存区域,减少了一个步骤,同时降低了内存消耗(同时对混用的 Direct3D API也有效);显然,混合食用API

来进行图形编程并不是一个好主意(当然,同时使用GDI和Direct3D意味着这个是一个老的、窗口模式的3D应 用程序,如Windows模式的老3D游戏)

老的WDDM 1.0处理GDI应用程序就如前面说过的一样

而在Windows 7 WDDM 1.1模型当中,单独的GDI将会通过WDDM与GPU进行硬件加速,同时经过GDI显存区域输出到DWM,表现在实际操作上,就是大部分窗口操作都变得 比Vista更流畅,日常工作更快捷(大部分2D应用程序都使用了GDI API,因为GDI包括的内容太多,因此

必须对其保持兼容;关于GDI,后面还有相关内容)

Windows XP的Direct3D/GDI驱动架构

Windows Vista核心图形架构,比起XP来要丰富了很多

Windows 7核心图形架构,老的GDI/GDI+仍被单独支持,不过,Windows 7提供了它们对应功能的新的实现方法相比Vista带的DirectX 10,Windows 7自带了DirectX 11,和WDDM 1.0到1.1的变化不同,DirectX 11的版本号表示其变化更大一些。Windows 7 DirectX 11改变

了以往的工作模式,将Direct3D 10.1升级到Direct3D 11,同时将以前Vista无法硬件加速的GDI/GDI+的工作重新划分、引入新的Direct2D和DirectWrite API来完成:

以前的图形操作大部分由Diredt3D和GDI/GDI+完成(此外还有一个WIC——Windows Image Component,管理扫描、打印和图形解码等工作),而在Vista当中,GDI是无法硬件加速的——因此Vista显得很慢,在Windows 7中,GDI的工作除了通过WDDM模型的改变获得硬件加

速之外,它们还按照面向的应用重新划分为Direct2D(2D加速)、 DirectWrite(文本处理)和DXGI 1.1(设备控制),这些新的API或者新分配到的API具有着硬件加速,例如,Direct2D就实际上是通过Direct3D 10来完成加速:

Direct2D:
硬件加速的即时模式2维图形API,它在2维几何图形、位图和文本方面有着较高的性能和质量。Direct2D API是设计与GDI、GDI+和Direct3D之间进行互操作的。

DirectWrite:
DirectWrite提供高质量的文本呈现、具有独立分辨率的轮廓字体、完整的Unicode文本以及布局支持。在使用Direct2D的时候,DirectWrite是硬件加速的。

当系统不支持Direct3D 10的时候,Windows 7会通过一个额外的10Level9软件层使用D3D9来完成工作,显然,为了最好的效率,你需要使用Direct3D10的显卡,因为Windows 7的DWM基于Direct3D 10

字体抗锯齿:DirectWrite效果

Direct3D 11
Windows 7带的Direct3D 11是D3D10的超集,它的特点是可以同时支持运行在Direct3D9和Direct3D10.1硬件上,实际上,DirectX 11会提供Vista的支持,而不是像Direct 10出来时那样,只支持Vista而无法兼容上一代系统,这导致了软件开发厂商们向DirectX 10转移的

缓慢。DirectX 11支持多个硬件平台和多种操作系统的 特性让其更容易被接受。同时Direc3D 11本身也具有这不少的改进,这一点会有专文讨论(计划中),这里先谈论Windows 7对整个图形操作的改进,通过更多地将GDI/GDI+纳入DirectX体系获得硬件加速,图形界面

的性能会越来越好(当然,这需要软件厂商们使用新的 Direct2D/DirectWrite进行编写)。微软希望将所有的图形操作都构建在DirectX之上。

通过Vista中缺失的GDI硬件加速纳入到DirectX体系中重新获得硬件加速,并且性能要更快;未来显卡厂商们将不再需要提供GDI加速

显示技术  
 
Windows 7支持一系列的显示技术,包括将数字输出作为原生输出(支持HDMI和DisplayPort,而老的VGA模拟输出被建议弃用),Windows 7还原生支持更多的内容来源,如Blu-Ray、JPEG-XR、HDR图片等。

Windows 7使用默认的sRGB色彩空间作为所有设备都统一的工作流,但是精度提升到30位——每一个色彩通道都增加了两位;Windows 7最高支持48位的scRGB模式,高精度+扩展范围+高动态

WDDM 1.1要求驱动支持BGRA,BGRA是最快的色彩格式
 
显示设备连接和配置 
 
在Windows 7中,所有的应用程序都在一个统一的虚拟桌面中显示,这个虚拟桌面分辨率为64Kx64K(65536x65536)
 
虚拟桌面按照显示器分为不同的视图,每一个视图对应一个适配器;一个适配器并不意味着一块显卡,一块显卡连接多个显示器的时候,它就含有多个适配器,类似地,SLI或者CrossFireX连接的单显示器就意味着其是一个单一的适配器(从Vista开始加入,叫做

Linked-Adapter特性)

不过,在Vista下,无法使用多块不同GPU厂商的显卡,而Windows 7则可以
 
在LDA配置下,程序界面在不同的输出中漫游基本上是透明的,叫做Dynamic adapter switching动态适配器切换

在多显示器下,Windows 7提供了一个新的全局快捷键功能:Win + P,可以快速设置多显示器工作状态,这个快捷键本来是特地为了投影仪应用而设计

总的来说,Windows 7的图形架构比起Vista来更加完善,应用也更加方便。最明显的就是GDI API的变迁了,现在GDI应用程序也获得了硬件加速,就如同XP下一样,比其Vista下只能处理器来进行处理,因此用户们可以感觉到日常应用会更加流畅。
========

Windows GDI:CGdiObject使用总结

http://blog.csdn.net/hisinwang/article/details/45752773

目录(?)[+]
一、Win32下的GdiObject操作

1、获取系统自带的GdiObject

HGDIOBJ GetStockObject(int fnObject) 
fnObject: 
BLACK_BRUSH — Black brush. 
HOLLOW_BRUSH — Hollow brush (equivalent to NULL_BRUSH). 
BLACK_PEN — Black pen. 
…… 
Note: It is not necessary (but it is not harmful) to delete stock objects by calling DeleteObject.

2、创建GdiObject

HPEN CreatePen(  
  int fnPenStyle,    // pen style
  int nWidth,        // pen width  
  COLORREF crColor   // pen color);
HBRUSH CreateSolidBrush(
  COLORREF crColor   // brush color value
);
HFONT CreateFont(...)

通过CreateXXX创建的GdiObject,在使用完之后一定要DeleteObject,否则会有资源泄露.

二、CGdiObject

class CGdiObject : public CObject
{
public:
    HGDIOBJ m_hObject;   //A HANDLE containing the HBITMAP, HPALETTE, HRGN, HBRUSH, HPEN, or HFONT attached to this object. 
    BOOL Attach(HGDIOBJ hObject);
    HGDIOBJ Detach();

static CGdiObject* FromHandle(HGDIOBJ hObject);
    static void DeleteTempMap( );
}

1、构造函数

CGdiObject::CGdiObject()

    m_hObject = NULL; 
}

2、析构函数

CGdiObject::~CGdiObject()
{
    DeleteObject(); 
}
BOOL CGdiObject::DeleteObject()
{
    if (m_hObject == NULL)
        return FALSE;
    return ::DeleteObject(Detach());
}

析构函数中有DeleteObject

3、Attach与Detach

原理同CDC

BOOL CGdiObject::Attach(HGDIOBJ hObject)
{
    if (hObject == NULL)
    {
        return FALSE;
    }

m_hObject = hObject;
    CHandleMap* pMap = afxMapHGDIOBJ(TRUE); // create map if not exist
    pMap->SetPermanent(m_hObject, this);

return TRUE;
}
HGDIOBJ CGdiObject::Detach()
{
    HGDIOBJ hObject = m_hObject;
    if (hObject != NULL)
    {
        CHandleMap* pMap = afxMapHGDIOBJ(); // don't create if not exist
        if (pMap != NULL)
            pMap->RemoveHandle(m_hObject);
    }
    m_hObject = NULL;

return hObject;
}

4、FromHandle

同CDC::FromHandle

CGdiObject* PASCAL CGdiObject::FromHandle(HGDIOBJ h)
{
    CHandleMap* pMap = afxMapHGDIOBJ(TRUE); //create map if not exist
    CGdiObject* pObject = (CGdiObject*)pMap->FromHandle(h);
    return pObject;
}

5、DeleteTempMap

Called automatically by the CWinApp idle-time handler, DeleteTempMap deletes any temporary CGdiObject objects created by FromHandle. 
DeleteTempMap detaches the Windows GDI object attached to a temporary CGdiObject object before deleting the CGdiObject object. 
这个和CDC::DeleteTempMap是一致的,也即只会delete temporary CGdiObject object,but 不会对关联的Windows GDI object调用::DeleteObject

6、CreateStockObject

BOOL CreateStockObject(
   int nIndex );

对应Win32SDK中的GetStockObject
========

<Win32 API> GDI和GDI+ 双缓冲模式绘图

http://blog.csdn.net/meta_cpp/article/details/44751369

目录(?)[+]
1. GDI双缓冲绘图
创建兼容DC, 在兼容DC上画好图后, 再通过BitBlt函数复制到所父DC.

void GdiDraw(HWND hwnd)  
{  
    RECT rc;  
    GetClientRect(hwnd, &rc);  
      
    rc.left += 25;  
    rc.right -= 25;  
    rc.top += 25;  
    rc.bottom -= 25;  
  
    int nWidth = rc.right - rc.left;  
    int nHeight = rc.bottom - rc.top;  
  
    wchar_t strText[] = TEXT("这是一个Gdi要显示的字符串");  
  
    HDC hdc = GetDC(hwnd);  
    SIZE sz;  
    HDC memDc = CreateCompatibleDC(hdc);          
  
    HBITMAP memBmp = CreateCompatibleBitmap(memDc, nWidth, nHeight);  
    HBITMAP oldBmp = (HBITMAP)SelectObject(memDc, memBmp);  
  
    RECT fillRc;  
    fillRc.left = 0;  
    fillRc.top = 0;  
    fillRc.bottom = nHeight;  
    fillRc.right = nWidth;  
  
    LOGBRUSH lb;  
    lb.lbColor = RGB(255, 255, 255);  
    lb.lbStyle = BS_SOLID;  
    lb.lbHatch = 0;  
    HBRUSH brush = CreateBrushIndirect(&lb);  
  
    FillRect(memDc, &fillRc, brush);  
  
    // 创建字体  
    HGDIOBJ font = CreateFont(28, // nHeight   
        0, // nWidth   
        0, // nEscapement   
        0, // nOrientation   
        FW_NORMAL, // nWeight   
        FALSE, // bItalic   
        FALSE, // bUnderline   
        0, // cStrikeOut   
        DEFAULT_CHARSET, // nCharSet   
        OUT_DEFAULT_PRECIS, // nOutPrecision   
        CLIP_DEFAULT_PRECIS, // nClipPrecision   
        ANTIALIASED_QUALITY, // nQuality   
        DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily   
        TEXT("微软雅黑")); // lpszFac  
      
    HGDIOBJ pen = ExtCreatePen(PS_SOLID, 1, &lb,  0, NULL);  
    HGDIOBJ oldPen = (HGDIOBJ)SelectObject(memDc, pen);  
    HGDIOBJ oldFont = (HGDIOBJ)SelectObject(memDc, font);  
      
  
    ::GetTextExtentPoint(memDc, strText, wcslen(strText), &sz); // 获取文本显示所需的高度和宽度  
    ::SetTextColor(hdc, RGB(255, 0, 0));  
  
  
    ::DrawText(memDc, strText, wcslen(strText), &fillRc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);   // 居中显示   
  
    BitBlt(hdc, rc.left, rc.top, nWidth, nHeight, memDc, 0, 0, SRCCOPY);  
  
    SelectObject(memDc, oldBmp);  
    SelectObject(memDc, oldPen);  
    SelectObject(memDc, oldFont);  
    DeleteObject(brush);  
    DeleteObject(memDc);  
    DeleteDC(hdc);  
}  
效果图示:

2. GDI+双缓冲绘图
创建Bitmap, Graphics::FromImg获取Graphics进行绘图, 再使用父Graphics进行DrawImg(Bitmap).

首先需要包含头文件:

#include <objidl.h>  
#include <GdiPlus.h>  
using namespace Gdiplus;  
#pragma comment (lib,"Gdiplus.lib")  
[cpp] view plain copy
void GdiPlusDraw(HWND hwnd)  
{  
    RECT rc;  
    GetClientRect(hwnd, &rc);  
  
    rc.left += 25;  
    rc.right -= 25;  
    rc.top += 25;  
    rc.bottom -= 25;  
  
    int nWidth = rc.right - rc.left;  
    int nHeight = rc.bottom - rc.top;  
    Gdiplus::RectF gdiRc(rc.left, rc.top, nWidth, nHeight);  
  
    HDC hdc = GetDC(hwnd);  
    wchar_t strText[] = TEXT("这是一个GdiPlus要显示的字符串");  
  
    Gdiplus::Graphics baseGraph(hdc);  
  
    Gdiplus::Bitmap bmp(nWidth, nHeight);  
    Gdiplus::Color white(0xFF, 0xFF, 0xFF);  
    for (int x = 0; x < nWidth; x++)  
    {  
        for (int y = 0; y < nHeight; y++)  
        {  
            bmp.SetPixel(x, y, white);  
        }  
    }  
  
    Gdiplus::Graphics *pGraphics = Graphics::FromImage(&bmp);  
  
    Gdiplus::SolidBrush  solidbrush(Color(0xFF, 0x00, 0x00, 0xFF));     // 蓝色画刷  
    Gdiplus::FontFamily  fontFamily(L"微软雅黑");  
    Gdiplus::Font        gdifont(&fontFamily, 16, FontStyleRegular, UnitPixel); // 字体  
    Gdiplus::StringFormat sf;  
    sf.SetAlignment(StringAlignmentCenter);  
    sf.SetLineAlignment(StringAlignmentCenter);         // 文本居中显示  
  
    pGraphics->SetTextRenderingHint(TextRenderingHintClearTypeGridFit);  
    pGraphics->SetTextRenderingHint(TextRenderingHintAntiAlias); // 设置平滑方式  
  
    pGraphics->DrawString(strText, wcslen(strText), &gdifont, gdiRc, &sf, &solidbrush);  
  
    baseGraph.DrawImage(&bmp, gdiRc, 0, 0, nWidth, nHeight, UnitPixel);  
  
    DeleteDC(hdc);  

========

<Win32> GDI+跑马灯滚动字幕

http://blog.csdn.net/meta_cpp/article/details/44803969

目录(?)[+]
1. 跑马灯
银行、商店的一些广告牌采用的跑马灯式的滚动字幕,如下图:

2. GDI+实现
使用GDI+双缓冲实现跑马灯效果。

初始化计时器:

int g_nCount;       // 控制第一个字符串的前进  
int g_nCount2;      // 控制第二个字符串的前进  
BOOL g_bRestart;    // 切换字符串  
int g_nControlWidth;    // 显示宽度  
void CALLBACK MyTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )  
{  
    switch(idEvent)  
    {  
    case 1:  
        DrawMarquee(hwnd);  
        break;  
    default:  
        break;  
    }  
}  
  
void InitTimer(HWND hwnd)  
{  
  
    RECT rc;  
    GetClientRect(hwnd, &rc);  
  
    rc.left += 25;  
    rc.right -= 25;  
    rc.top += 25;  
    rc.bottom -= 25;  
  
    g_nControlWidth = rc.right - rc.left;  
    g_nCount = g_nControlWidth;  
    g_nCount2 = g_nControlWidth;  
    g_bRestart = TRUE;  
  
    SetTimer(hwnd, 1, 200, MyTimerProc);  
}  
绘制:

void DrawMarquee(HWND hwnd)  
{  
    RECT rc;  
    GetClientRect(hwnd, &rc);  
  
    rc.left += 25;  
    rc.right -= 25;  
    rc.top += 25;  
    rc.bottom -= 25;  
  
    int nWidth = rc.right - rc.left;  
    int nHeight = rc.bottom - rc.top;  
    Gdiplus::RectF gdiRc(rc.left, rc.top, nWidth, nHeight);  
  
    HDC hdc = GetDC(hwnd);  
    wchar_t strText[] = TEXT("这是一串GdiPlus要显示的字符,正在以走马灯的形式显示...");  
  
    Gdiplus::Graphics baseGraph(hdc);  
  
    Gdiplus::Bitmap bmp(nWidth, nHeight);  
    Gdiplus::Color white(0xFF, 0xFF, 0xFF);  
    for (int x = 0; x < nWidth; x++)  
    {  
        for (int y = 0; y < nHeight; y++)  
        {  
            bmp.SetPixel(x, y, white);  
        }  
    }  
  
    Gdiplus::Graphics *pGraphics = Graphics::FromImage(&bmp);  
  
    Gdiplus::SolidBrush  solidbrush(Color(0xFF, 0x00, 0x00, 0xFF));     // 蓝色画刷  
    Gdiplus::FontFamily  fontFamily(L"微软雅黑");  
    Gdiplus::Font        gdifont(&fontFamily, 16, FontStyleRegular, UnitPixel); // 字体  
    Gdiplus::StringFormat sf;  
    sf.SetAlignment(StringAlignmentNear);   // 单行靠左    
    sf.SetLineAlignment(StringAlignmentCenter);     // 文本垂直居中显示  
  
    pGraphics->SetTextRenderingHint(TextRenderingHintClearTypeGridFit);  
    pGraphics->SetTextRenderingHint(TextRenderingHintAntiAlias);           
  
    Gdiplus::RectF fstRc;  
    fstRc.X = g_nCount;  
    fstRc.Y = 0;  
    fstRc.Width = nWidth;  
    fstRc.Height = nHeight;  
  
    Gdiplus::PointF gdiPt(rc.left + g_nCount, rc.top);  
  
    Gdiplus::RectF clipRc;  
    clipRc.X = 0;  
    clipRc.Y = 0;  
    clipRc.Width = nWidth;  
    clipRc.Height = nHeight;  
  
    pGraphics->SetClip(clipRc);  
    //pGraphics->DrawString(strText, wcslen(strText), &gdifont, gdiPt, &solidbrush);  
    pGraphics->DrawString(strText, wcslen(strText), &gdifont, fstRc, &sf, &solidbrush);  
  
    g_nCount -= 10;     // 从右到左递进  
    if (!g_bRestart)  
    {  
        Gdiplus::RectF secRc;  
        secRc.X = g_nCount2 + 20;  
        secRc.Y = 0;  
        secRc.Width = nWidth;  
        secRc.Height = nHeight;  
          
        //Gdiplus::PointF secPt(g_nCount2+20, rc.top);  
        //pGraphics->DrawString(strText, wcslen(strText), &gdifont, secPt, &solidbrush);  
        pGraphics->DrawString(strText, wcslen(strText), &gdifont, secRc, &sf, &solidbrush);  
  
        g_nCount2 -= 10;    // 从右到左递进  
  
        if (secRc.X <= -100 && (g_nCount < 0))    // 准备显示第一个字符串  
        {  
            g_nCount = g_nControlWidth;  
        }  
  
        if (secRc.X <= (-g_nControlWidth))   // 第二个字符串已经滚动完毕  
        {  
            g_bRestart = TRUE;  
        }  
    }   
  
    baseGraph.DrawImage(&bmp, gdiRc, 0, 0, nWidth, nHeight, UnitPixel);  
  
    DeleteDC(hdc);  
  
    if (g_nCount <= (-100))      // 第一个字符串已经前进一部分,准备显示第二个字符串  
    {  
        if (g_bRestart)  
        {  
            g_bRestart = FALSE;  
            g_nCount2 = g_nControlWidth;  
        }  
    }  
  
}

工程下载:http://download.csdn.net/detail/meta_cpp/8553175
========

Win32 GDI常见用法

//确定头文件的包含逻辑 即此为win32程序,不要包含MFC内容。 
#define WIN32_LEAN_AND_MEAN

#include <windows.h> //批量包含头文件
#include <windowsx.h> //内部包含宏和常量 
#include <mmsystem.h> // very important and include WINMM.LIB too!
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "resources.h"

//定义窗口类类名 
#define WINDOW_CLASS_NAME "WINCLASS1" 
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

HINSTANCE hinstance_app = NULL; // 只是为了全局使用 应用程序实例
HDC hdc; // 要重画的窗口设备句柄 
HWND hwnd; //窗口句柄 
int xv=5; //物体移动速度
int yv=5; //物体移动速度
int x=WINDOW_WIDTH/2;
int y=WINDOW_HEIGHT/2;

// 窗口事件回调函数 
LRESULT CALLBACK WindowProc(HWND hwnd, //该回调函数对应的窗口句柄 
UINT msg, //事件消息 
WPARAM wparam, 
LPARAM lparam)
{
PAINTSTRUCT ps; // 指向图形环境的句柄 WM_PAINT事件中使用

switch(msg)

case WM_CREATE: //窗口第一次创建时调用 
{
// 初始化代码 
PlaySound(MAKEINTRESOURCE(ID_WAVE1),hinstance_app,SND_RESOURCE|SND_ASYNC|SND_LOOP);
// 返回
return(0);
} break;

case WM_COMMAND: //菜单事件
{
switch(LOWORD(wparam)){
case MENU_FILE_ID_OPEN:
{
break;
}
case MENU_FILE_ID_CLOSE:
{
break;
}
case MENU_FILE_ID_SAVE:
{
break;
}
case MENU_FILE_ID_EXIT:
{
PostQuitMessage(0);
break;
}
case MENU_HELP_ABOUT:
{
// 弹出框方法解析 
//MessageBox(hwnd,lptext,lpcaption,utype)
//hwnd 表示父窗口的句柄 如果为NULL 那么桌面为父窗口 
//lptext 内容
//lpcaption 标题
//utype 控制按钮和文本的显示 
MessageBox(hwnd,"win32程序开发模板V1.0\n---------------------\n by LL","关于 CFrame",MB_OK|MB_ICONEXCLAMATION);
break;
}
default:break;
}
}break;
case WM_ACTIVATE:
{
if(LOWORD(wparam) != WA_INACTIVE)
{
//窗口得到焦点事件
}
else
{
//窗口失去焦点事件
}
}break;
case WM_CLOSE:
{
//用户点击关闭窗口按钮时触发事件
int result = MessageBox(hwnd,"你确定离开游戏么?","离开游戏",MB_YESNO|MB_ICONQUESTION);
if(result == IDYES)
{
return (DefWindowProc(hwnd,msg,wparam,lparam));
}
else return(0);
}break;
case WM_PAINT: //窗口内容需要重画时调用 
{
//BeginPaint测试 这个只获得被破坏的范围 不能获得整个窗口
hdc = BeginPaint(hwnd,&ps); //BeginPaint hdc仅代表了窗口被破坏的区域 并不能操作整个屏幕
// 在此做你自己的绘制 
EndPaint(hwnd,&ps);

// 返回
return(0);
} break;

case WM_DESTROY: //窗口销毁时调用 
{
PlaySound(NULL,hinstance_app,SND_PURGE);
// 这句代码发送 WM_QUIT 信息 
PostQuitMessage(0);

// 返回
return(0);
} break;

default:break;

}

// 默认处理 
return (DefWindowProc(hwnd, msg, wparam, lparam));

}

//画文本
void Draw_Text()
{
SetTextColor(hdc, RGB(rand()%256,rand()%256,rand()%256));
SetBkColor(hdc, RGB(0,0,0));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc,rand()%WINDOW_WIDTH,rand()%WINDOW_HEIGHT, "CFrame文本测试", strlen("CFrame文本测试"));
Sleep(20);
}

void Draw_Pixel()
{
for (int index=0; index < 1000; index++)
{
// get random position
int x = rand()%WINDOW_WIDTH;
int y = rand()%WINDOW_HEIGHT;

COLORREF color = RGB(rand()%255,rand()%255,rand()%255);
SetPixel(hdc, x,y, color);

} // end for index
}

void Draw_Line()
{
// create a random colored pen
HPEN hpen = CreatePen(PS_SOLID,1,RGB(rand()%256,rand()%256,rand()%256));

// select the pen into context
HPEN old_hpen = (HPEN)SelectObject(hdc,hpen);

// move to a random postion
MoveToEx(hdc, rand()%WINDOW_WIDTH, rand()%WINDOW_HEIGHT, NULL);

// draw a line
LineTo(hdc,rand()%WINDOW_WIDTH, rand()%WINDOW_HEIGHT);

// now delete the pen
SelectObject(hdc,old_hpen);
DeleteObject(hpen);
}

void Draw_Rect()
{
RECT rect; // used to hold rect info

// create a random rectangle
rect.left = rand()%WINDOW_WIDTH;
rect.top = rand()%WINDOW_HEIGHT;
rect.right = rand()%WINDOW_WIDTH;
rect.bottom = rand()%WINDOW_HEIGHT;

// create a random brush
HBRUSH hbrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));

// draw either a filled rect or a wireframe rect
if ((rand()%2)==1)
//不使用边界画笔绘制填充矩形 如果画(10,10)到(20,20) 那么填充为(10,10)(21,21)
//包括左上角 不包含右下角
FillRect(hdc,&rect,hbrush); 
else
//绘制仅有边界的中空矩形
//只是用画刷 不适用画笔
FrameRect(hdc,&rect,hbrush);

// now delete the brush
DeleteObject(hbrush);
}

void Draw_Ellipse()
{
// create the pens and brushes
HPEN white_pen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
HPEN black_pen = CreatePen(PS_SOLID, 1, RGB(0,0,0));
HBRUSH green_brush = CreateSolidBrush(RGB(0,255,0));
HBRUSH black_brush = CreateSolidBrush(RGB(0,0,0));

// first select the black pen and brush into context
//下面绘制的目的是消除圆形的上一次的图像,即用黑色覆盖住 这样玩家就看不到了
SelectObject(hdc, black_pen);
SelectObject(hdc, black_brush);
// draw the ball
Ellipse(hdc, x, y, x + 32, y + 32);

// MOVE the ball
x+=xv;
y+=yv;

// test for collisions, first x-axis
if (x < 0 || x > WINDOW_WIDTH - 32)

// invert x-velocity of ball
xv=-xv;

// push ball back
x+=xv;

} // end if
else
// test for y-axis collisions
if (y < 0 || y > WINDOW_HEIGHT - 32)

// invert y-velocity of ball
yv=-yv;

// push ball back
y+=yv;

} // end if

// now select the green and white colors for brush and pen
SelectObject(hdc, white_pen);
SelectObject(hdc, green_brush);

// DRAW the ball
Ellipse(hdc, x, y, x + 32, y + 32);

// delete all the objects
DeleteObject(white_pen);
DeleteObject(black_pen);
DeleteObject(green_brush);
DeleteObject(black_brush);
}

void Draw_Polygon()
{
// select random colors for polygon 
HPEN pen_color = CreatePen(PS_SOLID, 1, RGB(rand()%256,rand()%256,rand()%256));
HBRUSH brush_color = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256));

// select them into dc
HPEN old_hpen =(HPEN)SelectObject(hdc,pen_color);
HBRUSH old_brush =(HBRUSH)SelectObject(hdc,brush_color);

// now create list of random points for polygon
int num_points = 3+rand()%8;

// this will hold the point list
POINT point_list[10];

// create array of points
for (int index = 0; index < num_points; index++)
{
// set next random point in list
point_list[index].x = rand()%WINDOW_WIDTH;
point_list[index].y = rand()%WINDOW_HEIGHT;
} // end for index

// draw the polygon
Polygon(hdc, point_list, num_points);

// now delete the pen
SelectObject(hdc,old_hpen);
SelectObject(hdc,old_brush);

DeleteObject(pen_color);
DeleteObject(brush_color);
}

//游戏主循环
void Game_Main()
{

hdc = GetDC(hwnd);
Draw_Text(); //绘制文字测试
Draw_Pixel(); //绘制像素测试
Draw_Line(); //绘制线段
Draw_Rect(); //绘制矩形
Draw_Ellipse(); //绘制并且移动圆形 有闪烁
Draw_Polygon(); //绘制多边形
ReleaseDC(hwnd, hdc);
}

// WINAPI 强制参数从左到右传递
// hInstance 系统为此应用程序生成的实例句柄 可以用来跟踪这个应用程序
// hPrevInstance 过时了 在旧版本中 负责跟踪这个应用程序以前的实例 
//lpCmdLine 用来命令行运行时传递参数
// nCmdShow 控制该窗口打开时的状态 
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

WNDCLASSEX winclass; //窗口类对象

MSG msg;

hinstance_app = hInstance;

//创建窗口类 
//所有同一个窗口类创建的对象有相同的事件回调函数
//如果需要每个窗口都有自己的回调函数 那么需要创建多余一个的窗口类
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC | //窗口类 类型 
CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hInstance;
winclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ID_ICON2)); // 控制ALT+TAB时的图标
winclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(ID_CURSOR1)); //鼠标样式
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //背景画刷 
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
//注意 资源文件中的第一个图标就是程序图标
//控制任务管理器 窗口左上角 平面下方任务栏的图标
winclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(ID_ICON2));

//注册窗口类
if (!RegisterClassEx(&winclass))
return(0);

//创建窗口
if (!(hwnd = CreateWindowEx(NULL, // 高级特性 一般不用 
WINDOW_CLASS_NAME, // 窗口类类名 
"Your Basic Window", // 标题
WS_OVERLAPPEDWINDOW | WS_VISIBLE,//窗口外观和行为标志 
200,200, // x坐标 y坐标 
WINDOW_WIDTH,WINDOW_HEIGHT, // 宽高
NULL, // 父窗口句柄 如果为NULL 则父窗口是桌面 
NULL, // 父窗口菜单 
hInstance,// 该应用程序实例 
NULL))) // 高级特性 一般不用 
return(0);

HMENU hmenu = LoadMenu(hInstance,"MainMenu");
SetMenu(hwnd,hmenu);

//hdc = GetDC(hwnd);
/*主事件循环 这个不实时
while(GetMessage(&msg,NULL,0,0))

// 转换
TranslateMessage(&msg);

// 发送事件到窗口事件回调函数 
DispatchMessage(&msg);

*/
//主事件循环 实时循环 
while(TRUE)
{
DWORD start_time = GetTickCount(); //获得windows启动后的毫秒数

if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message == WM_QUIT) break; //跳出无限循环条件
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Game_Main(); //此处为游戏主循环

while((GetTickCount() - start_time) <33); //33毫秒后执行下一帧显示 此代码锁定帧频为1秒30帧

//检测键盘事件 主动发送消息结束进程
//用户可以自定义消息 也可以使用PoseMessage来发送消息
if (KEYDOWN(VK_ESCAPE)) SendMessage(hwnd, WM_CLOSE, 0,0);

// slow system down a little
//Sleep(50); //仅仅是延迟程序
}
//ReleaseDC(hwnd,hdc);

return(msg.wParam); 
}
========

电脑显示无法启动程序,丢失gdiplus.dll,一直黑屏

电脑显示无法启动程序,丢失gdiplus.dll,怎么回事,一直黑屏怎么解决啊

gdiplus.dll是GDI图形设备接口图形界面相关模块,对于系统正常运行来说至关重要。当你的电脑弹出提示“缺少gdiplus.dll”或“无法找到gdiplus.dll”等错误问题,说明这个文件丢失了。
dll文件修复方法:
1、下载gdiplus.dll文件。
2、复制文件“gdiplus.dll”到系统目录下。
3、系统目录一般为:C:\WINNT\System32 64位系统为C:\Windows\SysWOW64
4、最后点击开始菜单-->运行-->输入regsvr32 gdiplus.dll 后,回车即可解决错误提示!

去下载或者去别人电脑复制一个gdiplus.dll文件,找个PE盘放到电脑光驱里,进PE环境,把gdiplus.dll复制进去,再按操作走一遍就行了
========

Win32 GDI 学习总结相关推荐

  1. Win32汇编学习——windows汇编语法(小甲鱼教程)

    Win32汇编学习--windows汇编语法(小甲鱼教程) 1)指令集 .386 语句是汇编语句的伪指令,类似指令有:.8086 . .186  ..286  ..386/.386p  . .486/ ...

  2. Win32 GDI TextOut与DrawText低效率深坑问题

    Win32 GDI绘图需要优化的几个地方: 1.内存双缓冲:这个自行GG 2.仅可视区域:视图中不可见的区域是不要绘制 3.不要用DrawText:能用TextOut的地方就别用DrawText,这个 ...

  3. Win32 多线程学习总结

    Win32多线程编程学习心得 http://blog.csdn.net/jonathan321/article/details/50782832 博客原文地址:http://jerkwisdom.gi ...

  4. C#调用Win32 api学习总结

    转载:https://blog.csdn.net/bcbobo21cn/article/details/50930221 从.NET平台调用Win32 API Win32 API可以直接控制Micro ...

  5. java 调用win32 api 学习总结

    java使用JInvoke调用windows API 使用jinvoke调用windowsAPI.jna使用比较麻烦,需要写c代码和参数转换,jinvoke的使用就像jdk中的包一样. 官网使用参考: ...

  6. win32汇编学习的一些摘要

    sizeof 变量名,数据类型或数据结构 lengthof 变量名.数据类型或数据结构名 sizeof 伪指令可以取得变量.数据类型或数据结构以字节为单位的长度,lengthof可以取得变量中数据的项 ...

  7. GDI+学习成果-金鱼宠物

    花了3个晚上学习了下GDI+,顺便练习了下很久没有使用的C++,真累了,基本忘记的差不多了.赶紧把成果弄上来,小小的成就感一下... 下载源代码

  8. win32汇编语言学习笔记(三)

    汇编语言学习笔记(三) CH3.Windows汇编基础 .386 .model flat,stdcall option casemap:none 定义程序使用的指令集.工作模式 相应的还有:.8086 ...

  9. win32 IOCTL学习总结

    Kernel-win32的系统调用机制 毛德操 正如许多网友所言,要在Linux内核中实现Windows系统调用(或别的系统调用),最简单的办法莫过于把 这些系统调用"搭载"在Li ...

最新文章

  1. [转]用C#编写ActiveX控件(一)
  2. BZOJ4503 两个串 【fft】
  3. php-curl-class,一个简单PHP CURL类
  4. 阿里巴巴首席技术官程立:我们相信并正在践行的“好科技”
  5. Ubuntu14.04下安装Chrome出现“未安装软件包 libappindicator1”问题的解决办法
  6. vuetify中文文档_我们为什么选择Vuetify作为前端框架
  7. 考研数学笔记:曲率数学公式推导
  8. 【白话设计模式】23种设计模式一句话通俗讲解
  9. 自动化测试(AT)与探索性测试(ET)
  10. angular路由守卫
  11. 常见互联网公司职级和薪资一览,有条件的一定要进大厂,薪水是真高
  12. 软件测试缺陷密度的计算方法,如何计算缺陷密度?
  13. CE-RTI开源软件代码学习笔记(一)
  14. 《Python数据分析基础教程:NumPy学习指南(第2版)》笔记8:第三章 常用函数4——线性模型、数组修剪与压缩、阶乘
  15. 斐波那契生兔子问题(一月大兔子生a对,二月大兔子生b对,三月大兔子生c对。。。)
  16. 【硬盘】2021-西部数据叠瓦盘(SMR)、垂直盘(CMR/PMR)型号、容量大全
  17. eSIM终于来临,它将如何助力IoT的发展?
  18. 微信公众号开发 自定义分享 从前台到Java后台 调用微信JS接口分享朋友圈
  19. 收藏的技术资料下载网址
  20. 拼图工具箱微信小程序源码下载支持多种拼图模式制作

热门文章

  1. 打怪升级之小白的大数据之旅<Java基础知识点巩固习题>
  2. cmake:ESP32交叉编译工具链定义
  3. python画圆柱体_python绘制圆柱体的方法
  4. Java丨基础:十二、枚举与注解
  5. 科研论文工具大全,收好啦!
  6. java比较时间的小时和分钟的等于和大于小于
  7. 纯CSS实现3D立方体相册效果
  8. requests模块代理IP池搭建视频爬取
  9. Java基本数据类型取值范围(float、double范围勘误)
  10. 小米air2 pro ota 升级固件