GDI是图形设备接口的英文缩写,主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形和图像输出
属性 | 缺省值 | GDI 函数 |
---|---|---|
背景色 | 白色 | SetBkColor() |
背景模式 | OPAQUE | SetBkMode() |
位图 | 无 | CreateBitmap()、CreateBitmapIndirect()、CreateCompatibleBitmap()、SelectObject() |
画刷 | WHITE_BRUSH | CreateBrushIndirect()、CreateDIBPatternBrush()、CreateHatchBrush()、CreatePatternBrush()、CreateSolidBrush()、SelectObject() |
画刷原点 | (0,0) | SetBrushOrg()、UnrealizeObject() |
裁剪区域 | 窗口客户区 | ExcludeClipRect()、IntersectClipRect()、OffsetClipRgn()、SelectClipRgn() |
调色板 | DEFAULT_PALETTE | CreatePalette()、RealizePalette()、SelectPalette() |
画笔 | BLACK_PEN | CreatePen()、CreatePenIndirect()、SelectObject() |
画笔当前位置 | (0,0) | MoveTo() |
绘图模式 | R2_COPYPEN | SetROP2() |
字体 | SYSTEM_FONT | CreateFont()、CreateFontIndirect()、SelectObject() |
字符间距 | 0 | SetTextCharacterExtra() |
映射模式 | MM_TEXT | SetMapMode() |
多边形填充模式 | ALTERNATE | SetPolyFillMode() |
位图扩展模式 | BLACKONWHITE | SetStretchBltMode() |
文本色彩 | 黑 | SetTextColor() |
视口大小 | (1,1) | SetViewportExt() |
视口原点 | (0,0) | SetViewportOrg() |
窗口大小 | (1,1) | SetWindowExt() |
窗口原点 | (0,0) | SetWindowOrg() |
void CMyView::OnPaint()
{
CPaintDC dc(this); // paint 图层、油漆层
OnPrepareDC(&dc); // prepare 使做好准备,把……预备好
dc.TextOut(0,0,"Hi!"); // 文字输出
OnDraw(&dc);
}
CPaintDC类自动调用BeginPaint(),析构自动调用EndPaint().
CClientDC
CClientDC() 只能在窗口的客户区(不包括边框、标题栏、菜单栏、状态栏)中进行绘图。
(0,0)通常指的是客户区的左上角。
为了便于绘图,往往先调用CWnd::GetClientRect()来获取窗口客户区大小。
CClientDC()构造函数调用GetDC(),析构函数调用ReleaseDC().
CWindowDC
CWindowDC()允许在屏幕的任意位置 进行绘图,点(0,0)指整个屏幕的左上角。
在使用CWindowDC绘图时,通常先调用CWnd::GetWindowRect()来获取窗口在屏幕坐标系的
外框坐标,然后再调用CWnd::ScreenToClient()进行坐标转换。
CWindowDC dc(this);
CRect rectWindow;
GetWindowRect(rectWindow); // 获得window窗口矩形
ScreenToClient(rectWindow); // window窗口转换至client窗口矩形
构造CDC对象,因为Window限制了环境设备的数目,最好是在堆栈中构造对象:
void CMyView::MyFunction()
{
...
CRect rect;
CClientDC dc(this);
dc.GetClipBox(rect);
...
}
调用CDC的SetViewportOrg()和SetWindowOrg()函数来改变该模式下的坐标原点位置。
void CMyView::OnDraw(CDC* pDC)
{
pDC->SetMapMode(MM_TEXT); // 设置映射模式
pDC->SetWindowOrg(CPoint(100, 100)); // 设置window窗口的原点坐标
pDC->Rectangle(CRect(100, 100, 300, 300)); //
}
通常调用SetWindowExt设置窗口范围和SetViewportExt设置视口范围来设置所需要的比例因子;
void CMyView::OnDraw(CDC* pDC)
{
CRect rectClient;
GetClientRect(rectClient);
pDC->SetMapMode(MM_ANISOTROPIC); // 映射模式
pDC->SetWindowExt(1000, 1000); // 设置窗口范围
pDC->SetViewportExt(rectClient.right, -rectClient.bottom); // 设置视口范围
pDC->SetViewportOrg(rectClient.right/2, rectClient.bottom/2); // 设置视口原点
pDC->Ellipse(CRect(-500, -500, 500, 500)); // 画椭圆形 Ellipse
}
坐标系使用原则:
使用GDI对象绘图,遵循的步骤:
void CMyView::OnDraw(CDC* pDC)
{
CPen penBlack;//构造并初始化
if (penBlack.CreatePen(PS_SOLID, 2, RGB(0,0,0)))
{
CPen* pOldPen = pDC->SelectObject(&penBlack);//将此画笔选入当前设备环境并保存原来的画笔
pDC->MoveTo(...);//使用此画笔绘图
pDC->LineTo(...);
pDC->SelectObject(pOldPen);//恢复设备环境中原来的画笔
}
else
{
...//出错警告
}
}
Window包含了一些库存的可以利用的GDI对象。用不着删除他们。
CDC的成员函数SelectStockObject()可以把一个库存对象选入当前设备环境中,并
返回原先被选中的对象指针,同时是原先被选中的对象从设备环境中分离出来。
void CMyView::OnDraw(CDC* pDC)
{
CPen newPen(PS_SOLID, 2, RGB(0,0,0));
pDC->SelectObject(&newPen);
pDC->MoveTo(...);
pDC->LineTo(...);
pDC->SelectStockObject(BLACK_PEN); //newPen被分离出来
CFont* pOldFont = pDC->SelectObject(m_pPrintFont);
m_hOldFont = (HFONT)pOldFont->GetSafeHandle();
}
void cMyView::SwitchToOldFont(CDC* pDC)
{
if (m_hOldFont)
{
pDC->SelectObject(CFont::FromHandle(m_hOldFont));
}
}
COLORREF、GetBValue、GetGValue、GetRValue、RGB、PALETTERGB
GetSysColor();//获取当前系统定义的一些颜色
构造函数: CColorDialog(COLORREF clrInit=0, DWORD dwFlags = 0, CWnd* pParentWnd = NULL);
当该对话框OK退出,即DoModal返回 IDOK 时,可以调用下列成员获取相应的颜色:
当修饰画笔的宽度大于1个像素时,画笔的风格只能取PS_NULL 、 PS_SOLID 、 PS_INSIDEFRAME ,定义其他风格不会起作用。
CDC 的 LineTo 和 MoveTo 函数,配合使用完成直线和折线的绘制操作。
绘制直线
绘制折线的函数
矩形和圆角矩形:
多边形和正多边形
圆弧和椭圆
弦形
扇形
Bezier曲线
#include "CommerGraph.h"
CCommerGraph m_CommerGraph;
m_CommerGraph.SetCommerGraphDC(pDC);
m_CommerGraph.DrawGrid(RGB(0,200,255));
m_CommerGraph.SetDataLimits(0,175);
m_CommerGraph.AddLineGraph(data[0],20,0,RGB(255,0,0));
m_CommerGraph.AddLineGraph(data[1],20,1,RGB(0,192,0));
m_CommerGraph.AddLineGraph(data[2],20,2,RGB(0,0,255));
m_CommerGraph.ShowDataRuler(RGB(0,0,0),TRUE);
通常直方图画成竖直的。CCommerGraph的成员函数中的 AddBarGraph 和 DrawBarGraph 是 用来分别绘制单个数据和全部数据的直方图。DrawBarGraph是用缺省的画刷来绘制的,而 AddBarGraph可以为每一个直方图指定画刷。
#include "CommerGraph.h"
CCommerGraph m_CommerGraph;
... ...
m_CommerGraph.SetCommerGraphDC(pDC);
m_CommerGraph.DrawGrid(RGB(0,200,255));
m_CommerGraph.SetDataLimits(0,175);
m_CommerGraph.DrawBarGraph(data,20,RGB(0,192,0),RGB(255,0,255));
m_CommerGraph.ShowDataRaler(RGB(0,0,0),TRUE);
CCommerGraph的成员函数中的 AddPieGraph 和 DrawPieGraph 是用来分别绘制单个数据和全部数据的扇形图。DrawPieGraph是用缺省的画刷来绘制的,而 AddPieGraph可以为每一个扇形图指定画刷。
#include "CommerGraph.h"
CCommerGraph m_CommerGraph;
... ...
m_CommerGraph.SetCommerGraphDC(pDC);
m_CommerGraph.DrawPicGraph(pieData,7,100,RGB(0,200,255),RGB(255,0,255));
区域CRgn是一个重要的GDI对象,是窗口中的圆弧或者多边形区域的组合,可被填充、反色显示、 移动、判定,并可与剪裁路径相互运算等。
CDC 为区域对象提供了有关绘制 Paint 的一些操作:
操作一个路径通常按下列步骤进行:
注意:不是所有的CDC绘图函数都可以用来构造路径的,对于Window95或98而言,则只有:
CloseFigure()、 ExtTextOut 、 LineTo() 、 MoveTo 、 PolyBezier() 、 PolyBezierTo()、
Polygon()、 Polyline()、 PolylineTo()、 PolyPolygon()、 PolyPolyline()、TextOut()
而在WindowNT中除了上述函数外,还可以有:
AngleArc()、Arc、ArcTo、Pie、Chord、Rectangle、
Ellipse、PolyDraw、RoundRect
往往用作视图的滚动和缩放,且视口是一个矩形形状的窗口;而不像裁剪区域可以是圆形或其他复杂的形状。
设置裁剪区域
virtual int SelectClipRgn(CRgn* pRgn);
int SelectClipRgn(CRgn* pRgn, int nMode);
int SetROP2(int nDrawMode);//设置指定的光栅操作模式
int GetROP2() const;//获得当前的光栅操作模式
是对图形的几何信息经过几何变换后产生新的图形。
常用的定位技术有:
动态定位的方法有:
CRectTracker 类已经为我们构筑了图形动态定位的框架,只要以此为基类派生出一个 CMetaTracker 类,并在该 类中添加 CRectTracker 的虚方法 DrawTrackerRect 就可实现直线、矩形、圆等的橡皮条方法类。
字体分为四种基本类型:
字体属性:字样(宋体、黑体…)、风格(粗体、斜体…)、尺寸(毫米、英寸、号数…)
逻辑字体的具体属性可由 LOGFONT 结构来描述。
当使用完CFont 对象后,必须将其和设备环境分离,并删除字体对象。
待补全
virtual BOOL TextOut(int x, int y, LPCTSTR lpscString, int nCount);//
BOOL TextOut(int x, int y, const CString& str);//用当前字体在指定位置显示一个文本
virtual BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, LPCTSTR lpscString, UINT nCount, LPINT lpDxWidths);
BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect, const CString& str, LPINT lpDxWidths);//用当前字体在指定矩形框内显示一个文本
virtual CSize TabbedTextOut(int x, int y, LPCTSTR lpscString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);
CSize TabbedTextOut(int x, int y, const CString& str, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin);//用当前字体在指定位置显示一个文本
virtual int DrawText(LPCTSTR lpscString, int nCount, LPRECT lpRect, UINT nFormat);
int DrawText(const CString& str, LPRECT lpRect, UINT bFormat);//在指定矩形是对文本进行格式化绘制
如果想要绘制的文本是一个多列的列表形式,那么采用TabbedTextOut 函数,启用制表停止位,可以是绘制出来的文字效果更佳。
如果要在一个矩形区域内绘制多行文本,采用DrawText 函数会更加高效。
如果文本和图形结合紧密,字符间隔不等,并要求有背景颜色或矩形裁剪特性,那么ExtTextOut 函数是最好的选择。
如果没有什么特殊要求,使用TextOut 函数就显得简练。
注意:缺省时,上述文本输出函数既不使用也不更新当前位置。若要使用和更新当前位置,则必须调用SetTextAlign 并将 参数设置为TA_UPDATECP 使用时,最好在文本输出前用MoveTo 将当前位置移动至指定位置后,再调用文本输出函数。
文本的格式属性通常包括文本颜色、对齐方式、字符间隔以及文本调整等。
在CDC 类中,GetTextMetrics(LPTEXTMETRIC lpMetrics)是用来获得指定字符尺寸及其他属性。
CSize GetTextExtent(LPCTSTR lpscString, int nCount) const;
CSize GetTextExtent(const CString& str) const;//计算当前设备环境下,一行字符串的宽度和高度
CSize GetTabbedTextExtent(LPCTSTR lpscString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const;
CSize GetTabbedTextExtent(const CString& str, int nTabPositions, LPINT lpnTabStopPositions) const;//计算一行含有制表符的字符串的整个宽度和高度
CPalette 类封装了一个Window 调色板具有的大部分功能;这些功能包括:创建、操作、捡取等。
两个步骤:先构造一个CPalette 对象,再调用下列成员函数进行初始化操作:
Window 的位图实际上是一些和显示像素相对应的位阵列,有两种类型:GDI位图(也称DDB)、DIB位图。
BOOL LoadBitmap(LPCTSTR lpscResourceName);//
BOOL LoadBitmap(UINT nIDResource);//从程序中调入一个位图资源并赋予CBitmap对象
BOOL LoadOEMBitmap(UINT nIDBitmap);//调入指定的Window预定义的位图资源
BOOL CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CONST VOID *lpBits);//用指定的宽高和位图模式创建一个位图对象
BOOL CreateBitmapIndirect(LPBITMAP *pbm);//直接用BITMAP结构来创建一个位图对象
BOOL CreateCompatibleBitmap(CDC* pDC, int cx, int cy);//为某设备环境创建一个指定的宽高位图对象
由于位图不能直接显示在实际设备中,因此对于GDI位图的显示则必须遵循下列步骤:
函数解释:
位图信息数据:
由一个位图信息头和一个颜色表组成。
位图信息头是一个 BITMAPINFOHEADER 数据结构组成,内含有DIB的尺寸和颜色格式信息。
颜色表数据是可变化的,其长度由位图信息头的biBitCount 值决定。颜色表中的每一项都是一个RGBQUAD 数据结构。
图像数据:
位图数据的长度由图像尺寸、像素的位数和压缩方式等共同决定的。实际尺寸可由文件头中的第二项‘文件大小’减去第五项‘数据偏移’值得到。
DIB位图数据可以有逆序和顺序。当BITMAPINFOHEADER 结构中的biHeight 为正时,位图是顺序,否则为逆序。
注:只有逆序位图数据才能被压缩,顺序位图则不行。
图元文件 MetaFile 是一个设备无关的图像文件,是将图像以图形对象(线、圆弧、多边形)而不是像素的形式来存储的。
分标准型和增强型。
BOOL CreateEnhanced(CDC* pDCRef, LPCTSTR lpszFileName, LPCRECT lpBounds, LPCTSTR lpszDescription);//为增强图元
//–文件创建一个设备环境,该设备环境可用来储存图片,lpBounds必须正值,并以0.01mm为单位
GIF文件中的每一幅图像就是一个图像块。图像块由四部分组成:图像块标识码、图像描述块、一个可选的局部颜色表、图像数据。
图像存储压缩方法:
DIB中的行程编码RLE压缩原理:
将某扫描行中颜色值相同的相邻像素,用一个计数值和那些像素的颜色值来代替。
对于DIB位图,有BI_RLE8 和 BI_RLE4 压缩方法
LZW压缩原理:
把每一个第一次出现的字符串用一个数值来编码,将其存储在压缩文件中,在还原程序中,再将这个数值还原成原来的字符串。
基本JPEG算法操作可分成三个步骤:
1)颜色空间转换
2)使用离散余弦变换DCT去除诗数据冗余
3)进行量化处理
将位图复制到剪贴板:
对于有调色板的彩色位图,必须将位图中的调色板复制到剪贴板上才能保证位图的正确显示。
复制DDB位图到剪贴板时,在最后要调用Detach 函数将GDI 对象的句柄释放,以避免系统将GDI对象重复复制。
图像的几何变换:
函数解析:GetDIBits
百度:GetDIBits函数获取指定兼容位图的位,然后将其作一个DIB—设备无关位图(Device-Independent Bitmap)使用的指定格式复制到一个缓冲区中。
原型:
int GetDIBits(HDC hdc, HBITMAP hbmp, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage);
参数:
1、IN:当前的DC句柄
2、IN:hbmp:位图句柄,图像源
3、IN:uStartScan:开始检索的行ID
4、IN:cScanLines:指定检索的扫描线数。 以“行”为单位进行拷贝。
5、OUT:lpvBits:指向用来检索位图数据的缓冲区的指针。
如果此参数为NULL,那么函数将把位图的维数与格式传递给lpbi参数指向的BITMAPINFO结构。
6、IN:lpbi:指向一个BITMAPINFO结构的指针,此结构确定了设备所在位图的数据格式。
7、IN:uUsage:指定BITMAPINFO结构的bmiColors成员的格式。它必须为下列取值:
DIB_PAL_COLORS:颜色表由指向当前逻辑调色板的16位索引值数组构成。
DIB_RGB_COLORS:颜色表由红、绿、蓝(RGB)三个直接值构成。
返回值:OUT:线数。 如果lpvBits参数非空,并且函数调用成功,那么返回值为从位图复制的扫描线数。
注:
Windows 95和Windows 98:如果lpvBits参数为NULL并且GetDIBits成功地填充了BITMAPINFO结构,那么返回值为位图中总共的扫描线数。
Windows NT:如果lpvBits参数为NULL并且GetDIBits成功地填充了BITMAPINFO结构,那么返回值为非0。如果函数执行失败,那么将返回0值。Windows NT: 若想获得更多错误信息,请调用GetLastError函数。
注释:如果所需要的DIB格式与其内部格式相匹配,那么位图的RGB值将被复制。如果不匹配,那么将合成一个颜色表。下表描述了针对每 一种颜色格式所合成的颜色表。
1_BPP:颜色表中仅包含黑白表项。
4_BPP:颜色表由标准VGA定义的颜色组合而成。
8_BPP:颜色表由GDI定义的256色组合而成。
24_BPP:不返回颜色表。
如果lpvBits参数为一个有效指针,那么 *位图信息头结构* 的 前6个成员 必须初始化为DIB的大小和格式。
注意:位图信息头结构可为以下几种格式:
操作系统 位图信息头结构 (Operating System Bitmap Information Header)
Windows NT 3.51及早期的 BITMAPINFOHEADER
Windows NT 4.0及Windows 95中的 BITMAPV4HEADER
Windows NT 5.0及Windows 98中的 BITMAPV5HEADER
通过将**高度设为正数**来指定一个自下而上的DIB,而自上而下的DIB则通过设置一个**负的高度值**来指定。位图的颜色表将附加在BITMAPINFO结构的后面。
如果lpvBits为NULL,那么GetDIBits将检查lpbi所指向的第一个结构的第一个成员。这一成员必须指定BITMAPCOREHEADER结构或位图信息头结构的字节数。函数将通过指定的大小来确定剩余成员如何被初始化。
如果lpvBits为NULL,并且BITMAPINFO结构的bit count成员初始化为0,那么GetDIBits将不填充BITMAPCOREHEADER结构或位图信息头结构的颜色表部分。这一技术可用来查询位图属性。
应用程序调用这个函数时必须将hbmp参数所标识的位图选择到一个设备环境中。
自下而上DIB的原点为位图的左下角,自上而下DIB的原点为其左上角。
速查:Windows NT:3.1及以上版本;Windows:95及以上版本;Windows CE:不支持;头文件:wingdi.h;库文件:gdi32.lib,DLL: gdi32.dll
要输出位图,就要用到GetDIBits函数把位图信息读到一个数组中,然后保存数组。
对于兼容DC上位图1:对兼容DC的所有操作都会体现在位图上,也就是说如果在兼容DC上作图,相当于在兼容DC上的位图上作图。
对于兼容DC上位图2:位图数据的来源可以是通过在兼容DC上作图得到的,也可以是从原DC上BitBlt来的
举例说明:
//兼容DC
CDC compatibleDC;
compatibleDC.CreateCompatibleDC(pDC);
//兼容位图
CBitmap bitmapData;
bitmapData.CreateCompatibleBitmap(pDC, colNum, rowNum);
compatibleDC.SelectObject(&bitmapData);
{
//此处可以用兼容DC绘制,体现在兼容位图
compatibleDC.FillSolidRect(0, 0, colNum, rowNum, RGB(255, 255, 255));
CPen pen(PS_SOLID, 10, RGB(255, 0, 0));
CPen *oldpen = compatibleDC.SelectObject(&pen); // 固定写法
compatibleDC.Ellipse(10, 10, 20, 20);
compatibleDC.SelectObject(oldpen); // 固定写法,必须还原
//或者可以把原DC上的数据复制到兼容DC的兼容位图
compatibleDC.BitBlt(0, 0, colNum, rowNum, pDC, 0, 0, SRCCOPY);
}
//最后把兼容DC兼容位图中复制到数组中
GetDIBits(compatibleDC.m_hDC, (HBITMAP)bitmapData.m_hObject, 0, rowNum, pByte, (LPBITMAPINFO)&bitmapInfoHeader, DIB_RGB_COLORS)
/*
下边就可以组织BITMAPFILEHEADER、BITMAPFILEHEADER对象的设置,准备输出了
在对BMP的文件头、信息头格式设置格式时,可以采用下边方法
*/
BITMAP mbitmap;
bitmapData.GetBitmap(&mbitmap);
bitmapInfoHeader.biWidth = mbitmap.bmWidth;
bitmapInfoHeader.biHeight = mbitmap.bmHeight;
bitmapInfoHeader.biPlanes = mbitmap.bmPlanes;
bitmapInfoHeader.biBitCount = mbitmap.bmBitsPixel;
//也可以自己直接赋值指定格式
色彩处理:
实质是变换图形中相应的调色板或直接变换像素的颜色值,从而改变了其显示效果。
包括:色彩的灰度转换、反色、阀值化、对比度扩展。
图像的柔化和锐化:
未补齐
未补齐
往DIB类(用户自己建立的位图操作类)添加SaveBmp函数
通常来说,采用往DIB类中添加位图保存函数,更符合C++面向对象的思想,即将对对象的处理函数封装在一个类中,编程的条理性也更好。不足之处是,如果在位图上绘制图形时,如矩形、椭圆等图形,调用位图保存函数时,绘制的图形并未保存。这是因为MFC中采用的文档/视图类的结构,文档仅处理数据的读写操作,数据保存在文档类的成员变量中。视图从文档类中将文档的数据取出来,然后再屏幕上显示文档的数据。直接调用DIB类中的SaveBmp,只能保存位图的数据,而无法把绘制的图形连同位图一起保存。
// MFC 中位图操作
BOOL CDib::SaveBmp(LPCTSTR filename)
{
//写模式打开文件
CFile file;
if (!file.Open (filename ,CFile::modeCreate |CFile ::modeReadWrite |CFile ::shareExclusive ))
return FALSE ;
//求取颜色表和位图数据大小
int m_nColorTableLength =GetNumberOfColors ();
int BitCount=m_pBitmapInfoHeader->biBitCount;
//保证待存储图像数据每行字节数为4的倍数
int lineByte=(GetWidth ()*BitCount+31)/32*4;
DWORD bmpDataSize=lineByte*GetHeight ();
m_pData = pDib + m_pBitmapInfoHeader->biSize+ sizeof (RGBQUAD )*m_nColorTableLength;
try {
//文件头结构写进文件
file.Write (&bitmapFileHeader ,sizeof(BITMAPFILEHEADER ));
//文件信息头写入文件
file.Write(m_pBitmapInfoHeader ,sizeof(BITMAPINFOHEADER ));
//如果有颜色表,颜色表写进文件
if (m_nColorTableLength !=0)
file.Write (m_pRGB ,sizeof (RGBQUAD )*m_nColorTableLength );
//位图数据写进文件
file.Write (m_pData ,bmpDataSize );
}
catch (CException * pe){
pe->Delete ();
AfxMessageBox ("write error!");
return FALSE;
}
file .Close ();
return TRUE;
}
直接往视类添加位图保存函数OnSave
往****View视类中添加OnSave函数可以完美的解决这一问题,因为其保存图片的思想是根据MFC工作区位图的位置、大小,对工作区制定位置的图形进行完完整整的复制,由于复制的是屏幕上工作区的图案,故人为绘制的图形可以保存。
//位图的保存
void CDrawView::OnFileSave()
{
// TODO: 在此添加命令处理程序代码
//--------------------------------------------------------
//保存位图:可以保存工作区上的图片,不仅可以保存原有的位图文件,还可以保存在位图上绘制的图形
//--------------------------------------------------------
CClientDC dc(this);
CDC memDC;
memDC.CreateCompatibleDC(&dc); // 兼容DC 即:与dc参数相同,但没有数据的新dc(memDC)
CBitmap bm;
CRect rect;
GetClientRect (&rect ); // 获得客户区矩形
int Width = bmp_width;
int Height =bmp_height;
bm.CreateCompatibleBitmap(&dc, Width, Height); // 创建兼容位图
CBitmap* pOld = memDC.SelectObject(&bm); // 更换位图
memDC.BitBlt(0 , 0 , Width, Height, &dc, xorigin, yorigin, SRCCOPY); // 当前客户区dc内容拷贝至memDC的bm中
memDC.SelectObject(pOld);
BITMAP btm;
bm.GetBitmap(&btm); // 将位图拷贝至 btm (BITMAP结构对象)
DWORD size = btm.bmWidthBytes * btm.bmHeight;
LPSTR lpData = (LPSTR)GlobalAlloc(GPTR, size); // 分配内存 用于存储位图数据
BITMAPFILEHEADER bfh; // 位图文件头
BITMAPINFOHEADER bih; // 位图信息头
// 为信息头赋值
bih.biBitCount = btm.bmBitsPixel; // 位大小 1、4、8、16、24
bih.biClrImportant = 0;
bih.biClrUsed = 0;
bih.biCompression = 0;
bih.biHeight = btm.bmHeight; // 高度 注:如果高度是负值,则图像将以X轴镜像形式展示
bih.biPlanes = 1; // 平面
bih.biSize = sizeof(BITMAPINFOHEADER); // 信息头size
bih.biSizeImage = size; // 位图size
bih.biWidth = btm.bmWidth; // 宽度
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
// 拷贝图像数据
GetDIBits(dc, bm, 0,bih.biHeight, lpData, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
// 设置文件头
bfh.bfReserved1 = bfh.bfReserved2 = 0; // 预留位 必须为0
bfh.bfType = ((WORD)('M'<< 8)|'B'); //等价 bfh.bfType=0x4D42; //bmp类型
bfh.bfSize = 54 + size;
bfh.bfOffBits = 54;
CFileDialog dlg(false,_T("BMP"),_T("*.bmp"),OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,_T("*.bmp|*.bmp|*.*|*.*|"));
if (dlg.DoModal()!=IDOK)
return;
CFile bf;
CString ss=dlg.GetPathName();
if(bf.Open(ss, CFile::modeCreate | CFile::modeWrite))
{
bf.Write(&bfh,sizeof(BITMAPFILEHEADER)); // 文件头
bf.Write(&bih,sizeof(BITMAPINFOHEADER)); // 信息头
// 调试板 因为为真彩色,所以没有调试板
bf.Write(lpData, size); // 数据
bf.Close();
}
GlobalFree(lpData);
AfxMessageBox ("图片保存成功!");
SaveFlag =TRUE ;
}
MFC 例3:
笔者最近在做一个关于远程控制的插件,结果刚开始就在获取屏幕图像并取出每个像素点的RGB色的时候就碰到了问题,以下就简单的写一个代码
HDC screenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
HDC hMemDC = CreateCompatibleDC(screenDC);
//因为是获取整个桌面的图像,所以这里的x,y是事先获取到的屏幕分辨率,GetSystemMetrics可完成此功能
//两个内存对象的HDC参数要使用同一个,刚开始我使用错了,造成一直无法取得数据
HBITMAP hBitmap = CreateCompatibleBitmap(screenDC, x, y);
BITMAPINFO bitmapInfo = {0};
//BITMAPINFO结构有两个成员变量,分别是BITMAPINFOHEADER结构和RGBQUAD结构,
//BITMPINFOHEADER结构中包含了图像的一些基本信息,包括宽与高
//我们要先将biSize属性初始为BITMAPINFOHEADER结构的大小,以便GetDIBits函数在调用的时候填充其他的属性变量
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
//将hBitmap选入到hMemDC中,通俗点说就是关联起来,对hMemDC的操作也就是对hBitmap操作,
//比如在hMemDC中写入文字在hBitmap中也能体现,常见给图片打水印可以用这个方法实现
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
//StretchBlt函数,将拷贝整个屏幕的图像到hMemDC中,函数说明见MSDN,此处不详细解释
StretchBlt(hMemDC, 0, 0, x, y, screenDC, 0, 0, x, y, SRCCOPY);
//第一次调用 GetDIBits函数,并将参数五置为NULL,且bitmapInfo的BITMAPINFOHEADER结构的biSize已经初始化过,
//函数将会把图像的宽,高还有整个图像所点的字节数填充到bitmapInfo结构中,以便接下来的使用
//函数的详细解释网上很多。
GetDIBits(hMemDC, hBitmap, 0, y, NULL, &bitmapInfo, DIB_RGB_COLORS);
//根据第一次调用 GetDIBits函数完成后,填充到bitmapInfo中的图像占字节数大小来动态分配内存
//你也可以用图像的宽*高来得到图像所占字节数,但此方法有一点要说明 ,第一行的字节数必须是4的倍数,不够的用0补足。
//例:宽为923像素的图像,923/4 = 230(取整数部分),多出三个字节,这个时候我们就要补足四位,变成(230+1)*4 = 924 实际一行占的字节数
unsigned char *bitmapBits = new unsigned char[bitmapInfo.bmiHeader.biSizeImage];
memset(bitmapBits, 0, bitmapInfo.bmiHeader.biSizeImage);//初始为0
//第二次调用函数,并且第五个参数为有效的指针且可写入,
//函数调用后,将会把图像的每个像素点的RGB值 以16进制值写入到bitmapBits中,
//常见的一个像素是占4个字节,第一个字节是蓝色--B,第二个字节是绿色--G,第三个字节是红色--R,第四个不知道
GetDIBits(hMemDC, hBitmap, 0, y, bitmapBits, &bitmapInfo, DIB_RGB_COLORS);
SelectObject(hMemDC, hOldBitmap);
//然后是释放资源
delete []bitmapBits;
DeleteObject(hBitmap);
DeleteDC(hMemDC);
DeleteDC(screenDC);
大家可以再结合网上讲的一些知识点,完成自己要完成的功能,
bitmapBits保存的就是屏幕的矩阵图像数据,接下来就是压缩传输了或者根据BMP文件的格式保存成一张图片。