MFC GDI 图形设备接口函数

图形设备接口

  • 图形设备接口(Graphics Device Interface)
  • MFC的四种DC
  • 常用函数
  • 库存的GDI对象:
  • RGB和YUV颜色空间
    • CColorDialog 颜色选择通用对话框
    • 创建画笔 CreatePen()、 CreatePenIndirect()
    • 画刷的属性通常包括:填充色、填充图案、填充样式三种。
    • 创建画刷:
  • 获取指定点的颜色
  • 画图形:
  • 直方图
  • 扇形图
  • CRgn 类及区域操作
  • 路径操作
  • 视口和裁剪操作:
  • 图形几何变换:
  • 图形动态定位:
    • 对象的拾取和拖动示例,步骤:
  • 字体
  • 使用字体对话框CFontDialog
  • 常用文字输出函数:
    • 输出多列文字
    • 输出多行文字
    • 文本的格式属性
  • 计算字符的几何尺寸:
    • 计算字符串宽度和高度:
    • 文字特效:
  • 调色板类 CPalette
    • 创建调色板
      • 调色板的一般操作:
      • 调色板 检取:
  • 位图
    • CBitmap 类与GDI位图的显示:
  • Window 图元文件
    • 图元文件的调用和显示:
  • 图像块:
    • GIF图像显示:
  • 图像压缩和JPEG标准
    • 压缩原理简述
  • 图像常用控件
  • 常用图像技巧
  • DDB向DIB转换:
  • DIB向DDB转换:
  • 将位图写入DIB文件:
    • MFC 保存DIB文件,方法1
    • MFC 保存DIB文件,方法2

图形设备接口(Graphics Device Interface)

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()

MFC的四种DC

  1. CPaintDC
    针对OnPaint()函数进行,
    OnPaint()首先构造一个CPaintDC()对象,再调用OnPreoareDC(),再来调用OnDraw()进行绘图。
void CMyView::OnPaint()
{
 CPaintDC dc(this);   // paint 图层、油漆层
 OnPrepareDC(&dc);	  // prepare 使做好准备,把……预备好
 dc.TextOut(0,0,"Hi!");		// 文字输出
 OnDraw(&dc);
}

CPaintDC类自动调用BeginPaint(),析构自动调用EndPaint().

  1. CClientDC
    CClientDC() 只能在窗口的客户区(不包括边框、标题栏、菜单栏、状态栏)中进行绘图。
    (0,0)通常指的是客户区的左上角
    为了便于绘图,往往先调用CWnd::GetClientRect()来获取窗口客户区大小。
    CClientDC()构造函数调用GetDC(),析构函数调用ReleaseDC().

  2. CWindowDC
    CWindowDC()允许在屏幕的任意位置 进行绘图,点(0,0)指整个屏幕的左上角
    在使用CWindowDC绘图时,通常先调用CWnd::GetWindowRect()来获取窗口在屏幕坐标系的
    外框坐标,然后再调用CWnd::ScreenToClient()进行坐标转换。

CWindowDC dc(this);
CRect rectWindow;
GetWindowRect(rectWindow);	// 获得window窗口矩形
ScreenToClient(rectWindow); // window窗口转换至client窗口矩形 
  1. CMetaFileDC
    CMetaFileDC封装了在一个Windows图元文件中绘图的方法。使用步骤如下:
    1. 构造一个CMetaFileDC()对象,并调用Create()函数;
    2. 向此对象发送所需要的CDC绘图命令,如:MoveTo()、LineTo()
    3. 调用成员函数Close()关闭设备环境,并返回HMETAFILE句柄。
    4. 使用CDC::PlayEnhMetaFile()来播放图元文件或者调用CopyMetaFile等函数对图元文件
      进行其他处理。

构造CDC对象,因为Window限制了环境设备的数目,最好是在堆栈中构造对象:

void CMyView::MyFunction()
{
 ...
 CRect rect;
 CClientDC dc(this);
 dc.GetClipBox(rect);
 ...
}

常用函数

  1. GetDeviceCaps()
    获取显示参数,物理宽高(毫米)、逻辑宽高(像素)、分辨率等。
  • 3种坐标的解释:
    • 设备坐标:
      指显示设备或者打印设备坐标系下的坐标,单位像素。对于窗口视图而言,设备坐标的原点在客户区的左上角。
    • 逻辑坐标:
      在各种映射模式下的坐标。缺省时,逻辑坐标和设备坐标是一致的。
    • 物理坐标:
      由程序员自己定义的坐标。精度更准确。

调用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));		//
}
  • 映射解释
    • MM_TEXT映射模式:
      X向右递增,Y向下递增;
    • "固定比例"映射模式:
      X向右递增,Y向下递减;实际的比例因子不同。
    • "可变比例"映射模式:
      MM_ISOTROPIC和MM_ANISOTROPIC,允许改变比例因子和坐标原点。当用户改变了窗口尺寸时,绘制的图形大小也会发生相应的变化。
      MM_ISOTROPIC映射模式下,纵横比总是1:1,无论比例因子如何变化圆总是圆;(ISOTROPIC 各向同性的;等方性的)
      MM_ANISOTROPIC映射模式下,比例因子可以独立的变化,圆可以被拉贬成椭圆。

通常调用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
}
  • 坐标系使用原则

    1. 大多数CDC成员函数逻辑坐标为参数,但和区域有关的函数除外。
    2. CWnd的成员函数都是以设备坐标为参数。
    3. 所有选中测试HitTest操作都应使用设备坐标
    4. 从鼠标消息(如:WM_LBUTTONDOWN)获得的鼠标位置都是设备坐标
    5. 某些像CRect::PtInRect()之类的函数只有采用设备坐标参数时才会保证有正确的结果。
    6. 将一些需要长期使用的值,用逻辑坐标或者物理坐标来保存。如果用设备坐标来保存某
      点的坐标的话,那么只要用户对窗口进行一下滚动,该点的坐标就不再有效了。
  • 使用GDI对象绘图,遵循的步骤:

    1. 在堆栈中定义一个GDI对象(如CPen、CFont对象),然后用相应的构造函数(如CreateFontIndirect、CreataRectRgn) 创建此GDI对象。有些GDI派生类的构造函数允许我们提供足够的信息,从而一步即可完成对象的创建任务;这些派生类有CPen、CBrush。
    2. 将刚才构造的GDI对象选入当前设备环境中,不要忘记将原来的GDI对象保存起来。
    3. 绘图结束后,恢复当前设备环境中原来的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
 {
  	...//出错警告
 }
}

库存的GDI对象:

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));
 }
}

RGB和YUV颜色空间

COLORREF、GetBValue、GetGValue、GetRValue、RGB、PALETTERGB
GetSysColor();//获取当前系统定义的一些颜色

CColorDialog 颜色选择通用对话框

构造函数: CColorDialog(COLORREF clrInit=0, DWORD dwFlags = 0, CWnd* pParentWnd = NULL);
当该对话框OK退出,即DoModal返回 IDOK 时,可以调用下列成员获取相应的颜色:

  1. COLORREF GetColor() const;//返回用户选择的颜色
  2. static COLORREF* GetSavedCustomColors();
  3. void SetCurrentColor(COLORREF clr);//强制使用clr作为当前选择的颜色

创建画笔 CreatePen()、 CreatePenIndirect()

当修饰画笔的宽度大于1个像素时,画笔的风格只能取PS_NULL 、 PS_SOLID 、 PS_INSIDEFRAME ,定义其他风格不会起作用。

画刷的属性通常包括:填充色、填充图案、填充样式三种。

  • 填充色使用 COLORREF 颜色填充;
  • 填充图案使用8*8位图;
  • 填充样式是CDC内部定义的一些特性,都是以HS_为前缀的标识。

创建画刷:

  1. 创建填充色画刷: BOOL CreateSolidBrush(COLORREF color);//用指定的颜色color创建
  2. 创建填充样式画刷: BOOL CreateHatchBrush(int iHatch, COLORREF color);//通指定的样式iHatch和颜色color创建
    BOOL CreateSysColorBrush(int nIndex);//创建一个系统颜色的填充样式nIndex画刷
  3. 创建填充图案画刷:
    • BOOL CreateDIBPatternBrush(HGLOBAL h, UINT iUsage);//
    • BOOL CreatePatternBrush(HBITMAP hbm);//
    • BOOL CreateBrushIndirect(CONST LOGBRUSH *plbrush);//

获取指定点的颜色

  • COLORREF GetPixel(int x, int y) const;//获取指定点的颜色
  • COLORREF GetPixel(POINT point) const;

画图形:

CDC 的 LineTo 和 MoveTo 函数,配合使用完成直线和折线的绘制操作。

  • 绘制直线

    • BOOL LineTo(int x, int y); // 绘制线段 当前位置 至 x,y 注:绘制结束改变当前位置
    • BOOL LineTo(POINT point);
    • CPoint GetCurrentPosition() const;//获取当前位置
  • 绘制折线的函数

    • BOOL Polyline(LPPOINT lpPoints, int nCount);//不使用当前位置,也不更新当前位置
    • BOOL PolylineTo(const POINT* lpPoints, int nCount);//把当前位置作为起点,在折线划完之后,把终点设为新的当前位置
    • BOOL PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount);//不使用当前位置,也不更新当前位置
  • 矩形和圆角矩形:

    • BOOL Rectangle(int xl, int yl,int x2, int y2);//绘制矩形
    • BOOL RoundRect(LPCRECT lpRect);//绘制圆角矩形
    • int GetPolyFillMode() const;//返回当前填充模式
  • 多边形和正多边形

    • BOOL Polygon(LPPOINT lpPoints, int nCount);//绘制多边形
    • BOOL PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount);//绘制多个多边形
  • 圆弧和椭圆

    • BOOL Arc(int x1, int y1, int x2, int y2,int x3, int y3,int x4, int y4);
    • BOOL Arc(LPCRECT lpRect, POINT ptStart, POINT ptEnd);//绘制椭圆弧线或者整个椭圆
    • int SetArcDirection(int nArcDirrction);//修改绘制的方向,顺时针或者逆时针
    • BOOL ArcTo(int x1, int y1, int x2, int y2,int x3, int y3,int x4, int y4);
    • BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd);//会将圆弧的终点作为新的当前位置,Arc不会
    • BOOL Ellipse(int x1, int y1, int x2, int y2);
    • BOOL Ellipse(LPCRECT lpRect);//用当前画刷绘制一个椭圆区域
  • 弦形

    • BOOL Chord(int x1, int y1, int x2, int y2,int x3, int y3,int x4, int y4);
    • BOOL Chord(LPCRECT lpRect, POINT ptStart, POINT ptEnd);//绘制弦形
  • 扇形

    • BOOL Pie(int x1, int y1, int x2, int y2,int x3, int y3,int x4, int y4);
    • BOOL Pie(LPCRECT lpRect, POINT ptStart, POINT ptEnd);//绘制扇形
  • Bezier曲线

    • PolyBezier()//绘制一条或者多条Bezier曲线
    • SetDataLimits()//设置显示的数据分为
    • SetGrid()//设置整个线图的位置大小和网格大小
    • SetCommerGraphDC()//将某设备环境类指针传给类CCommerGraph
    • ShowDataRuler()//用来显示数据标尺
 #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的成员函数中的 AddBarGraphDrawBarGraph 是 用来分别绘制单个数据和全部数据的直方图。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的成员函数中的 AddPieGraphDrawPieGraph 是用来分别绘制单个数据和全部数据的扇形图。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 类及区域操作

区域CRgn是一个重要的GDI对象,是窗口中的圆弧或者多边形区域的组合,可被填充、反色显示、 移动、判定,并可与剪裁路径相互运算等。

  • BOOL CreateRectRgn(__in int x1, __in int y1, __in int x2, __in int y2);
  • BOOL CreateRectRgnIndirect(__in CONST RECT *lprect);//创建一个矩形区域
  • BOOL CreateEllipticRgn(__in int x1, __in int y1, __in int x2, __in int y2);
  • BOOL CreateEllipticRgnIndirect(__in CONST RECT *lprect);//创建一个椭圆区域
  • BOOL CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode);//创建一个封闭的多边形区域
  • BOOL CreatePolyPolygonRgn(__in CONST POINT *pptl, __in CONST INT *pc, __in int cPoly, __in int iMode);//重新创建一个矩形区域

CDC 为区域对象提供了有关绘制 Paint 的一些操作:

  • BOOL FillRgn(CRgn* pRgn, CBrush* pBrush);//用指定的画刷填充区域
  • BOOL PaintRgn(CRgn* pRgn);//用CDC当前的画刷填充区域
  • BOOL InvertRgn(CRgn* pRgn);//将区域的颜色反转显示
  • BOOL FrameRgn(CRgn* pRgn, CBrush* pBrush, int nWidth, int nHeight);//用指定的画刷勾画区域的轮廓

路径操作

操作一个路径通常按下列步骤进行:

  1. 调用 BeginPath 函数开始一个路径操作。
  2. 调用绘图函数(如 LineTo 、 TextOut )生成轨迹。
  3. 调用EndPath函数结束路径操作,此时该路径已成为CDC的当前路径。
  4. 调用路径的其他函数对路径进行处理。
  • StrokePath();//用当前画笔来绘制当前路径的轮廓
  • BOOL FillPath();//闭合当前路径中所有断开的图形
  • BOOL StrokeAndFillPath();//相当于StrokePath和FillPath的操作
  • BOOL WidenPath();//将拓宽之后的路径作为当前路径,画笔宽度大于1调用成功
  • BOOL AbortPath();//关闭并清除设备环境中所有的路径
  • BOOL CloseFigure();//在当前路径中闭合一个断开的图形
  • BOOL FlattenPath();//将当前路径中所有的曲线转换成连续的线段
  • int GetPath(LPPOINT lpPoints, LPBYTE lpTypes, int nCount) count;//返回当前路径中直线的端点

注意:不是所有的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 CSize SetViewportExt(int cx, int cy);//设置视口的大小
  • virtual CSize SetViewportExt(SIZE size);
  • virtual CPoint SetViewportOrg(int cx, int cy);//设置视口原点位置
  • virtual CPoint SetViewportOrg(POINT point);

设置裁剪区域

  • virtual int SelectClipRgn(CRgn* pRgn);

  • int SelectClipRgn(CRgn* pRgn, int nMode);

  • int SetROP2(int nDrawMode);//设置指定的光栅操作模式

  • int GetROP2() const;//获得当前的光栅操作模式

图形几何变换:

是对图形的几何信息经过几何变换后产生新的图形。

图形动态定位:

  1. CRectTracker 类 - 图形动态定位的框架
    CRectTracker 类允许用户在视窗里移动一个矩形对象或改变矩形对象的大小;
    成员函数有:
  • void Draw(CDC* pDC) const;//用当前的风格来绘制矩形边框和调整大小
  • BOOL Track(CWnd* pWnd, CPoint point, BOOL bAllowinvert = FALSE, CWnd* pWndClipTo = NULL);//一般在WM_LBUTTONDOWN消息处理函数中调用
  • BOOL TrackRubberBand(CWnd* pWnd, CPoint point, BOOL bAllowInvert = TRUE);//
  • int HitTest(CPoint point) const;//判断某个点是在哪个调整大小的句柄上
  • BOOL SetCursor(CWnd* pWnd, UINT nHitTest) const;//在视图的WM_SETCURSOR消息处理函数中调用此函数可以改变光标的形状

对象的拾取和拖动示例,步骤:

  1. 用MFC APPWizard创建单文档应用程序TrackDemo.
  2. 为CTrackDemoView类增加共有成员变量:
    BOOL m_bSelect;//是否可以选择对象
    CRect m_elliRect;//椭圆的外接矩形
    CRectTracker m_tracker;//
  3. 为CTrackDemoView类增加 RectInRect函数,用来判断一个矩形是否包含另一个矩形;
  4. 用ClassWizard为CTrackDemoView类添加 WM_LBUTTONDOWN 消息;
  5. 用用ClassWizard为CTrackDemoView类添加 WM_SETCURSOR 消息;

常用的定位技术有:

  • 参考定位、
  • 约束定位、
  • 动态定位、
  • 素描式定位;

动态定位的方法有:

  • 橡皮条技术、
  • 牵引技术。

CRectTracker 类已经为我们构筑了图形动态定位的框架,只要以此为基类派生出一个 CMetaTracker 类,并在该 类中添加 CRectTracker 的虚方法 DrawTrackerRect 就可实现直线、矩形、圆等的橡皮条方法类。

字体

字体分为四种基本类型:

  • 光栅字体(点阵字体)、
  • 矢量字体、
  • TrueType字体
  • OpenType字体。

字体属性:字样(宋体、黑体…)、风格(粗体、斜体…)、尺寸(毫米、英寸、号数…)

逻辑字体的具体属性可由 LOGFONT 结构来描述。

  • BOOL CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC=NULL);//

当使用完CFont 对象后,必须将其和设备环境分离,并删除字体对象。

使用字体对话框CFontDialog

待补全

常用文字输出函数:

  • 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 将当前位置移动至指定位置后,再调用文本输出函数。

文本的格式属性

文本的格式属性通常包括文本颜色、对齐方式、字符间隔以及文本调整等。

  • virtual COLORREF SetTextColor(COLORREF crColor);//设置文本颜色
  • virtual COLORREF SetBkColor(COLORREF color);//设置文本背景色
  • int SetBkMode(int mode);//设置背景模式
  • COLORREF GetTextColor() const;//返回当前文本颜色
  • COLORREF GetBkColor() const;//返回当前文本背景色
  • int GetBkMode() const;//返回背景模式

计算字符的几何尺寸:

在CDC 类中,GetTextMetrics(LPTEXTMETRIC lpMetrics)是用来获得指定字符尺寸及其他属性。

  • GetCharWidth()、
  • GetOutputCharWidth()、
  • GetCharABCWidths()

计算字符串宽度和高度:

  • 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;//计算一行含有制表符的字符串的整个宽度和高度

文字特效:

  • 旋转文字:使用TrueType 或者GDI笔划字体的文本就可以显示旋转效果。
  • 文字阴影:
  • 文字变形:获得要输出的矢量数据,再根据适当的矩阵变换,实现。
  • 弧形文字:将上下基准点分别设成半径不等的圆弧上的连续点。
  • 双弧文字:将上下文基准点分别设置成半径不等且圆心不同的圆弧上的连续点。
  • 直弧文字:将上面的基准点设置成连续小线段的水平线,而下面的基准点设成圆弧上的连续点
  • 波形文字:将上下基准点设成连续Bezier线段上的点。

调色板类 CPalette

CPalette 类封装了一个Window 调色板具有的大部分功能;这些功能包括:创建、操作、捡取等。

创建调色板

两个步骤:先构造一个CPalette 对象,再调用下列成员函数进行初始化操作:

  • BOOL CraetePalette(LPLOGPALETTE lpLogPalette);//创建一个指定的逻辑调色板
  • BOOL CreateHalftonePalette(CDC* pDC);//为pDC指定的设备环境创建调色板

调色板的一般操作:

  • void AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors);//
  • UINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors);
  • BOOL ResizePalette(UINT nNumEntries);

调色板 检取:

  • int GetEntryCount();//返回逻辑调色板中的项目数
  • UINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors);//返回逻辑调色板中某范围的表项内容

位图

Window 的位图实际上是一些和显示像素相对应的位阵列,有两种类型:GDI位图(也称DDB)、DIB位图。

  • GDI位图是由MFC中的CBitmap 类来表示的;也称为DDB位图,设备相关位图。显示方式视显示卡而定,有设备依赖性。在另一台PC机上显示会出问题。
  • DIB位图比GDI位图有很多编程优势;自带颜色信息,使调色板管理更加容易。后缀为.BMP

CBitmap 类与GDI位图的显示:

  • 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位图的显示则必须遵循下列步骤

  1. 调用CBitmap 类中的CreateBitmap()、 CreateBitmapIndirect()、 CreateCompatibleBitmap()函数创建一个适当的位图对象
  2. 调用CDC::CreateCompatibleDC()函数创建一个内存设备环境,以便位图在内存中保存下来,并与指定设备(窗口设备)环境相兼容。
  3. 调用CDC::SelectObject()函数将位图对象选入内存设备环境中。
  4. 调用CDC::BitBlt()或者CDC::StretchBlt()函数将位图复制到实际的设备环境中。
  5. 使用之后,别忘记将内存设备环境清除

函数解释

  • CDC::BitBlt()
    该函数从源矩形中复制一个位图到目标矩形。不能缩放,显示更新速度较快。
  • CDC::StretchBlt()
    该函数从源矩形中复制一个位图到目标矩形。可以对位图进行缩小或放大。

位图信息数据
由一个位图信息头和一个颜色表组成。
位图信息头是一个 BITMAPINFOHEADER 数据结构组成,内含有DIB的尺寸和颜色格式信息。
颜色表数据是可变化的,其长度由位图信息头的biBitCount 值决定。颜色表中的每一项都是一个RGBQUAD 数据结构。

图像数据
位图数据的长度由图像尺寸、像素的位数和压缩方式等共同决定的。实际尺寸可由文件头中的第二项‘文件大小’减去第五项‘数据偏移’值得到。

DIB位图数据可以有逆序顺序。当BITMAPINFOHEADER 结构中的biHeight 为正时,位图是顺序,否则为逆序。

注:只有逆序位图数据才能被压缩,顺序位图则不行。

Window 图元文件

图元文件 MetaFile 是一个设备无关的图像文件,是将图像以图形对象(线、圆弧、多边形)而不是像素的形式来存储的
分标准型和增强型。

  • 标准型扩展名.WMF
  • 增强型扩展名.EMF

BOOL CreateEnhanced(CDC* pDCRef, LPCTSTR lpszFileName, LPCRECT lpBounds, LPCTSTR lpszDescription);//为增强图元
//–文件创建一个设备环境,该设备环境可用来储存图片,lpBounds必须正值,并以0.01mm为单位

图元文件的调用和显示:

  • BOOL PlayMetaFile(HENHMETAFILE hmf, LPCRECT lpBounds);//将图元文件内容显示在指定的矩形区域内
  • GetEnhMetaFileDescription()//判断并获得图元文件的说明文字
    图元文件记录:整个图元文件数据都是用记录表示的,具有 ENHMETARECORD 结构;
  • GetEnhMetaFilePaletteEntries()//判断图元文件是否存在自己的调色板
  • EnumEnhMetaFile()//遍历图元文件记录,使用此函数需要提供类似EnhMetaFileProc()的回调函数

图像块:

GIF文件中的每一幅图像就是一个图像块。图像块由四部分组成:图像块标识码、图像描述块、一个可选的局部颜色表、图像数据。

GIF图像显示:

  1. 打开一个GIF文件并读取其文件头信息。
  2. 判断GIF文件是否存在全局调色板,如有则读取并为设备环境创建相应逻辑调色板。
  3. 分配一个缓冲区用来放入GIF文件图像数据。
  4. 根据GIF文件结构,调用LZW解压程序将图像数据转换成位图。
  5. 用DrawDib将此位图拷贝至相应的内存设备环境。
  6. 释放缓冲区。
  7. 在相应的设备环境中显示位图。

图像压缩和JPEG标准

图像存储压缩方法:

  • 行程编码压缩、
  • Huffman编码压缩、
  • LZW压缩、
  • 算术压缩、
  • JPEG压缩标准。

压缩原理简述

  1. DIB中的行程编码RLE压缩原理:
    将某扫描行中颜色值相同的相邻像素,用一个计数值和那些像素的颜色值来代替。
    对于DIB位图,有BI_RLE8 和 BI_RLE4 压缩方法

  2. LZW压缩原理:
    把每一个第一次出现的字符串用一个数值来编码,将其存储在压缩文件中,在还原程序中,再将这个数值还原成原来的字符串。

  3. 基本JPEG算法操作可分成三个步骤:
    1)颜色空间转换
    2)使用离散余弦变换DCT去除诗数据冗余
    3)进行量化处理

图像常用控件

  1. VC的Picture控件
  2. Web 浏览器控件
    void CWebBrowser2::Navigate2(VARIANT* URL, VARIANT* Flags, VARIANT* TargerFrameName, VARIANT* PostData, VARIANT* Headers);
    步骤:创建一个基于对话框的项目ImageDemo.
    选择"Project|Add To Project|Commponents and Controls…"菜单,弹出"Commponents and Controls…“对话框。
    在此对话框中选择"Registered ActiveX Controls”,将"Web浏览器"组建插入;
    打开IDD_IMAGEDEMO对话框资源模板,并添加"Web浏览器"控件,保留其缺省的ID号;
    在添加"打开"按钮(ID_IMAGE_OPEN);
    用 ClassWizard为"Web浏览器"控件添加成员变量m_WebBrowser,为"打开"按钮添加BN_CLICKED 消息处理,并实现函数。
    3)Kodak图像压缩图控件:包括Kodak图像编辑、管理、批注、扫描、缩略图控件;
    这些控件可以处理AWD、BMP、DCX、JPG、PCX、TIFF、XIF、GIF、WIFF等;并提供批注、裁剪、缩放、滚动、剪贴操作以及许多常用图像文件管理功能。
    m_ImageThumb.SetImage(dlg.GetPathName());//m_ImageThumb是CImgThumbnail类变量

常用图像技巧

  1. 将位图复制到剪贴板:
    对于有调色板的彩色位图,必须将位图中的调色板复制到剪贴板上才能保证位图的正确显示。
    复制DDB位图到剪贴板时,在最后要调用Detach 函数将GDI 对象的句柄释放,以避免系统将GDI对象重复复制。

  2. 图像的几何变换:

    1. 镜像分水平镜像垂直镜像两种。使用StretchBlt()进行操作;
    2. 旋转:在WindowNT中使用PlgBlt() 可以将位图进行旋转;对Window9X用旋转矩阵对像素进行变换。直接用GetPixel()和 SetPixel()来操作像素的方法
      对于较大的位图来说,转换速度慢;若是DIB位图则可使用**GetDIBits()**和 **SetDIBits()**函数来对指定扫描行的像素进行变换,可较大程度提高速度。
    3. 转置:是将X、Y坐标对换;实际上是逆时针旋转90度之后再做平移。

函数解析: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兼容的兼容DC
    • 创建与原DC兼容的兼容位图(可以是CBitMap),
    • 把兼容位图用SelectObject函数选进兼容DC,
    • 最后通过GetDIBits函数从兼容DC的位图中输出到数组。

对于兼容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;

//也可以自己直接赋值指定格式
  1. 色彩处理:
    实质是变换图形中相应的调色板或直接变换像素的颜色值,从而改变了其显示效果。
    包括:色彩的灰度转换、反色、阀值化、对比度扩展。

    1. 灰度转换:将调色板中的彩色变成灰度,形成新的调色板;没有调色板的,对像素进行操作。
    2. 反色:形成底片效果。对图片进行处理之后,图像亮的部分变暗,暗的部分变亮;其处理方法是将图像中调色板的RGB
      各个分量减去255即可实现。要考虑图像是否存在调色板。
    3. 阀值化:是一种将连续色调图像变成黑白图的方法。基本思想是使像素值小于指定的阀值的像素转换成黑像素,而等于或者大于指定阀值的
      像素转换成白像素。
    4. 对比度扩展:将对比度低的图像变成高的对比度。
  2. 图像的柔化和锐化:

    1. 柔化:除去图像中点状噪声的有效方法。使图像上任何一个像素与其相邻像素的颜色值的大小不会出现陡突的一种处理方法。高斯模板。
    2. 中值滤波:非线性处理技术。
    3. 锐化:和柔化相反;通过增强高频分量减少图像中的模糊,也称高通滤波。增加了图像的噪音。常用模板是拉普拉斯模板。

DDB向DIB转换:

未补齐

DIB向DDB转换:

未补齐

将位图写入DIB文件:

MFC 保存DIB文件,方法1

往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;
}

MFC 保存DIB文件,方法2

直接往视类添加位图保存函数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文件的格式保存成一张图片。

你可能感兴趣的:(MFC,GDI,mfc,windows,c++)