GDI阐述
GDI :Graph device interface  图形设备接口,应用程序调用图形编程的接口

为什么会使用GDI呢?

首先要理解显示器如何进行图像的显示,一般显示器把将要显示的内容存放在显存中(集成显卡一般使用内存,而独立显卡现在基本都使用独显),framebuffer就是用来存储显示的数据,由DMA自动读取数据将其显示在窗口中。而这里显示的只是一个个的像素点,通常是先显示一行的像素点,发出水平同步信号后,再显示下一行,当整个一屏幕的数据显示完全后,才会发出垂直同步信号,发出下一帧数据。

注意:这里显示任何图像,包括点,线,多边形,窗口等等,其实都是由像素一点点画出来的。

关键对于编程人员而言必须要懂得图形硬件接口的操作,就是必须懂得如何驱动底层,每当更换硬件或者升级硬件时,就需要重新写驱动接口,这对于上层应用的人而言,更无异于天灾人祸,因为很多上层开发人员并不懂得底层操作,而且底层图形设备千奇百怪,所以提供一个设备无关的接口是必须的。GDI就是这种有微软主推的图形接口,当然OpenGL等也可以的,现在主要探讨GDI,GDI其实也是一种二次封装,在底层驱动或者操作系统层次上的一个类库,SDK本身就提供了接口,而MFC又做了一次封装,使其操作更方便而起,而且更健壮。

注意:这里函数显示的时候并不是直接进行显示,而是将要显示的数据存储在设备描述表中处理好一个即将要显示的逻辑数据,然后再将此数据显示到窗口中,这样一来,可以处理字体,字型,颜色,不同窗口重叠显示等等

MFC如何使用GDI?

首先对于GDI而言,他并不是将图像直接显示在设备上,而是将显示的数据首先绘制在一个设备描述表DC中,这里面存储了显示图像的描述字段,包括物理设备和各种状态信息。画图前,先取得设备描述表句柄,然后再调用GDI输出函数进行输出并将句柄返回给GDI。

注意:其实GDI已经完成了数据流过程,就是将要显示的数据已经组织好,并放入指定位置,告诉DMA到这里取数据,然后显示。

MFC中只需先获取设备描述表对象指针,然后调用绘图函数即可。这又是什么意思呢?其实就是我要将数据流中间截断,把我想显示的数据放入,这样就可以输出我的数据。如何做呢?就是要获取数据流中对象,也就是设备描述表句柄,把他勾出来,使用他提供的函数进行绘制图像,然后把句柄再返回给GDI,有借有还再借不难,况且他人也要用这个句柄。看起来有点麻烦,其实MFC给我做好了的,CDC类直接将设备描述表和获取设备描述表句柄的GDI函数封装在一起了,我们只管使用。

那有那些设备描述句柄对象呢?

MFC提供了CDC类,如下一个例子

  CDC* pDC = GetDC();CRect rect;GetClientRect(&rect);pDC->DrawText(_T("hello MFC!"), &rect, DT_SINGLELINE| DT_CENTER | DT_VCENTER);ReleaseDC(pDC);

这里例子主要是显示一行数据”hello MFC“,这里声明了对象还需要释放,而GetDC只能在窗口客户区画图,GetWindowDC可以在窗口的任一地方画图,包括非客户区菜单栏,工具栏。另外在OnPaint函数中不能使用上面的获取和释放函数,使用如下的方式:

PAINTSTRUCT ps;
CDC *pDC = BeginPaint(&ps);
pDC->Ellipse(100,100,400,400);
EndPaint(&ps);

看到这里,发现获取方式设备描述表句柄好像多了,的确,幸亏MFC提供了如下分类的类:

CPaintDC :窗口客户区画图(OnPaint)

CClientDC:窗口客户区画图(非OnPaint)

CWindowDC:窗口任意地方画图(包括非客户区)

CMetaFileDC:GDI元文件画图(暂略)

注意:1)使用CPaintDC比使用::BeginPaint和::EndPaint好,因为在绘图工作下,不能将该消息从消息队列中删除,当应用程序处理WM_PAINT消息失败时会陷入死循环,而CPaintDC不会。

2)在使用CClientDC和CWindowDC可以在全屏幕窗口下画图,如下方式

CClientDC dc(NULL);//CWindowDC  dc(NULL);
dc.Ellipse(0, 0, 1440, 900); //在默认映射模式下,1个单位对应一个像素点,而我的显示器分辨率正好是1440*900

设备描述表属性
在之前的字体显示中,我们采用的都是默认的 画笔(1个像素点宽的黑实线),默认的画刷(单一的白色),默认的字体(12点高的普通比例字体),定义设备描述表属性的函数是SelectObject,可以进行画笔,画刷,字体,位图,调色板,区域的选取:如下,其他请参看MSDN中CDC类的有关文档

CDC::SetBkColor设置当前背景色。

CDC::SetBkMode设置背景模式。

CDC::SelectObject选择绘制对象如钢笔的GDI。

CDC::SelectStockObject选择Windows提供的某个预定义的库存钢笔、画笔或字体

绘图模式
在默认绘图模式下,使用的是直接将颜色复制到指定位置,不采用其他复合的运算,而CDC::SetROP2可以 设置其他的绘图模式,如下一些模式:

R2_BLACK 像素始终黑色。

R2_WHITE 像素始终为空。

R2_NOP 像素不变。

R2_NOT 像素是屏幕颜色的反向操作。

R2_COPYPEN 像素是钢笔颜色。 //默认使用模式

R2_NOTCOPYPEN 像素是钢笔颜色的反向操作。

R2_MERGEPENNOT 像素是钢笔颜色和屏幕颜色(最终像素= (不是屏幕像素)或笔)的反的组合。

R2_MASKPENNOT 像素是颜色组合共有的笔和屏幕(最终像素= (不是屏幕像素)和笔)的反向操作。

R2_MERGENOTPEN 像素是屏幕颜色和钢笔颜色(最终像素= (不是笔)或屏幕像素)的反的组合。

R2_MASKNOTPEN 像素是颜色组合共有的屏幕和笔(最终像素= (不是笔)和屏幕像素)的反向操作。

R2_MERGEPEN 像素是钢笔颜色和屏幕颜色(最终像素的组合=笔或屏幕像素为单位)。

R2_NOTMERGEPEN 像素是R2_MERGEPEN 颜色(最终像素的反=非(笔或屏幕像素)。

R2_MASKPEN 像素是颜色组合共有的笔和屏幕(最终像素=笔和屏幕像素为单位)。

R2_NOTMASKPEN 像素是R2_MASKPEN 颜色(最终像素的反=非(笔和屏幕像素)。

R2_XORPEN 像素是在笔或在屏幕颜色组合,但是,不在两个(最终像素=钢笔"异或"屏幕像素为单位)。

R2_NOTXORPEN 像素是R2_XORPEN 颜色(最终像素的反=非(钢笔"异或"屏幕像素)。

如下在客户区会两条对角线,第一条为默认黑色,第二条为默认画笔颜色的反色,就是白色的线,因为默认背景色成灰色状态,所以这个还是可以看到的,如下

CRect rect;
GetClientRect(&rect);CClientDC dc(this);
dc.SetBkMode(TRANSPARENT);
dc.MoveTo(rect.left, rect.top);
dc.LineTo(rect.right, rect.bottom);
dc.SetROP2(R2_NOTCOPYPEN);
dc.MoveTo(rect.left, rect.bottom);
dc.LineTo(rect.right, rect.top);

映射模式
   默认情况一个1单位的逻辑尺寸正好对应一个像素,就是MM_TEXT映射模式,调用的函数为 CDC::SetMapMode,如下其他一些模式:

MM_ANISOTROPIC 逻辑单位转换为随机缩放的轴的任意单元。设置为MM_ANISOTROPIC 的映射模式不会更改当前窗口或视区设置。若要更改单元,orientation和缩放,调用SetWindowExt 和 SetViewportExt 成员函数。

MM_HIENGLISH 每个逻辑单位转换为0.001英寸。正x是在右侧;正y启用。

MM_HIMETRIC 每个逻辑单位转换为0.01毫米。正x是在右侧;正y启用。

MM_ISOTROPIC 逻辑单位转换为方式调用的轴的任意单元,即沿X轴平移1个单位与沿y轴平移1个单位相等。使用SetWindowExt 和SetViewportExt 成员函数指定所需的单元测试和轴的orientation。GDI根据需要进行调整确保x和y单元保持大小相同。

MM_LOENGLISH 每个逻辑单位转换为0.01英寸。正x是在右侧;正y启用。

MM_LOMETRIC 每个逻辑单位转换为0.1毫米。正x是在右侧;正y启用。

MM_TEXT 每个逻辑单位转换为1设备像素。正x是在右侧;正y放置。 //默认映射模式

MM_TWIPS 每个逻辑单位转换为1/20点。(因为点是1/72英寸,twip是1/1440英寸。)正x是在右侧;正y启用。

 CClientDC dc(this);CRect rect;GetClientRect(&rect);
//dc.SetMapMode(MM_TEXT);
//dc.SetMapMode(MM_LOMETRIC); //此种模式下,y轴方向向上,设备原点不变还是左上角(0,0)dc.SetMapMode(MM_LOENGLISH); dc.MoveTo(0,-100);dc.LineTo(2000,-100);

注意:当设备为非MM_TEXT映射模式下,需要主要他们的y轴方向是向上的,不是向下,坐标原点默认还是视口的(0,0)

看下面自己定义设备尺寸与视口尺寸的比例:

 dc.SetMapMode(MM_ANISOTROPIC);dc.SetWindowExt(500,500);dc.SetViewportExt(rect.Width(),rect.Height());CString str,strnew;str.Format(_T("OP(%d,%d):"),point.x,point.y);dc.DPtoLP(&point);strnew.Format(_T("NP(%d,%d)"),point.x,point.y);str += strnew;dc.TextOut(point.x,point.y, str);CRect rectObj(100,100, 500, 500);dc.Rectangle(&rectObj);dc.Ellipse(&rectObj);

这里可以进行比例的设备,逻辑尺寸定义大小就是长500,宽500的窗口,也就是说下面的绘图尺寸不能超过这个尺寸,否则真正的窗口无法进行显示,而真正的窗口就是默认的客户区的宽高,这样一来可能比例拉伸的情况,图片最能反映,以后当窗口尺寸发生变化,但是逻辑尺寸仍然以(0,0,500,500)这个矩形的尺寸内绘图。这里将坐标进行了显示,由于是在右键按下函数里面写的,默认传过来的是客户区尺寸,将它转换为设备尺寸,更能看见他们的区别。
坐标转换
    请见上面的例子,这里就是逻辑坐标与设备真正的坐标进行转换,注意区分窗口区坐标与客户区坐标,后两个主要是参考点不一样导致的,而前面主要是显示的原理不一样,逻辑坐标是我们处理数据的缓冲区,真正显示的是设备坐标。

获取设备信息
    检索有关显示设备的各种形式设备特定的信息。CDC::GetDeviceCaps,如下一些实例

CClientDC dc(this);
CRect rect;
GetClientRect(&rect);
int cx = dc.GetDeviceCaps(HORZRES); //显示器水平像素点数目
int cy = dc.GetDeviceCaps(VERTRES);  //显示器垂直像素点数目
int cx1 = dc.GetDeviceCaps(LOGPIXELSX); //水平方向每逻辑英寸内像素点的数目
int cy1 = dc.GetDeviceCaps(LOGPIXELSY); //垂直方向每逻辑英寸内像素点的数目
long numclrs = dc.GetDeviceCaps(NUMCOLORS);//静态的颜色数目
long bitpix = dc.GetDeviceCaps(BITSPIXEL); //每个像素点用几位(bit)表示
int planes = dc.GetDeviceCaps(PLANES); //位平面的数目
dc.SetMapMode(TRANSPARENT);
CString strxy;
strxy.Format(_T("显示器分辨率:%d*%d"), cx, cy);
dc.TextOut(0,0, strxy);
strxy.Format(_T("dpix:%d, dpiy: %d"), cx1, cy1);
dc.TextOut(0,20, strxy);
strxy.Format(_T("numclrs:%d, bitclrs: %d, plans:%d"), numclrs, bitpix, planes);
dc.TextOut(0,40, strxy);

当前使用分辨率为1440*900的19英寸显示器,如下得到的一些值:

dpi:是由于操作系统限制为96,修改这个值会发生变化;

numclrs:按理应该为2^24, 显示为-1,暂未搞懂;

bitclrs:因为真彩色使用RGB每个8位 ,就是需要24位,但是为了总线存取方便通常会使用32位(4个字节)来存储一个像素点;

planes:位平面暂未搞懂,回头再看。。。
 ———————————————— 
版权声明:本文为CSDN博主「comwise」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/comwise/article/details/17091957

深入分析MFC之GDI原理透析相关推荐

  1. 华为交换机stp原理透析及实战

    转载来源 :华为交换机stp原理透析及实战 :https://mp.weixin.qq.com/s/hK2UhxssPb-ww6J0oCu1dQ 生成树协议stp,快速生成树协议rstp,多实例生成树 ...

  2. 深入浅入 ~ ConCurrentHashMap底层原理透析

    创作宗旨:化繁为简,绝不冗余,点到为止 ConcurrentHashMap<K,V> 继承了AbstractMap<K,V>,实现了ConcurrentMap<K,V&g ...

  3. java中数据类型byte的底层原理透析

    byte数据类型详解 二进制.位.字节的关系 二进制 位 字节 三者联系 ascll码表 Byte数据类型底层原理说明 代码与结果展示 结果透析 二进制.位.字节的关系 二进制 十进制:0,1,2,3 ...

  4. 网络传输协议原理透析

    网上对OSI参考模型的讲解比较多,但是看起来总还是没法有个完整的记忆. 为了全面透析网络传输,遂总结这篇笔记,留后续查阅. 名词解释: 开放式系统互联通信参考模型(英语:Open System Int ...

  5. 转:大规模网站架构技术原理透析

    来自:http://tech.it168.com/a2009/0904/674/000000674253.shtml 跟朋友聊天的时候,发现很多人对大型网站系统架构非常感兴趣,我也很感兴趣,经常会在家 ...

  6. 深入浅出~HashMap的底层原理透析

    创作宗旨:化繁为简,绝不冗余,点到为止 直接开门见山,就事论事! 什么是 HashMap? HashMap类继承了父类AbstractMap<K,V>,实现了接口Map<K,V> ...

  7. 精华推荐 | 【JVM深层系列】「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)

    前提介绍 很多小伙伴,都跟我反馈,说自己总是对JVM这一块的学习和认识不够扎实也不够成熟,因为JVM的一些特性以及运作机制总是混淆以及不确定,导致面试和工作实战中出现了很多的纰漏和短板,解决广大小伙伴 ...

  8. HTTPS协议原理透析

    1.HTTPS本身并非协议,而是标准的HTTP协议架在SSL/TLS协议之上的一种结构.(一种不太合适的说法可以认为是两种协议的叠加).HTTP是工作在OSI7层模型的最上层,就是第7层:Applic ...

  9. 透析ICMP协议(一): 协议原理

    透析ICMP协议(一): 协议原理 =============================== 这篇文章原创自bugfree/CSDN 平台: VC6 Windows XP ICMP简介: --- ...

  10. java序列化算法透析_Java序列化机制与原理的深入分析

    Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.Java序列化API提供一 ...

最新文章

  1. java文字转pdf格式_java根据富文本生成pdf文件
  2. react-native for android windows开发环境搭建详细记录
  3. 如何查看linux系统是32位还是64位
  4. Prometheus 初探
  5. android按下enter键如何让光标跳到下一个edittext,我们如何知道光标已经移动到edittext的下一行android...
  6. 表必须要有主键吗_玄关隔断什么材质好?玄关隔断必须要做吗?
  7. 贪心---移掉K位数字
  8. idea代码上传到gitee组织流程
  9. 047、JVM实战总结:高级工程师的硬核技能:JVM的Full GC日志应该怎么看?
  10. WorkerMan 入门学习之(三)基础教程-Timer类的使用
  11. sudo: add-apt-repository:找不到命令_C++腾讯面试题库干货!作为程序员,这些都掌握了,还有什么理由拿不到offer?...
  12. 一些Select检索高级用法(适用于mssql)
  13. ipad怎么阅读html文件,iPad浏览器怎么开阅读模式
  14. linux177端口怎么打开,AIX5.3,如何使用xmanager管理?177端口打不开的相关推荐_ChinaUnix论坛...
  15. 普通型母函数和指数型母函数
  16. [毕业设计]威客网站可行性研究报告书
  17. 谈谈对java线程的理解(五)--------ReentrantLock之阻塞队列
  18. 修改Visata下的无线网卡(Intel 5100 agn)Mac地址
  19. 微信官方支付接口配置教程
  20. g2plot 水滴图,包含数据更新时渲染问题

热门文章

  1. 扩展php-bcmath,centos安装PHP扩展(bcmath)
  2. 如何在js中实现html语言,如何使用脚本标签将JavaScript插入HTML
  3. 外部表改为内部表_2、从外部导入数据创建表(ACCESS图解操作系列)
  4. spark生成dataframe的几种方式
  5. c语言试讲课程,《C语言程序设计教程》试讲教案.doc
  6. c++ opencv图像中选择点显示点的坐标_如何使用OpenCV进行Delaunay三角剖分和Voronoi图...
  7. excel做地图热力图_我做了一个傻瓜式热力图生成工具
  8. Java学习笔记——正则表达式
  9. 函数不可以直接调用其他函数内部定义的变量_JavaScript(4) 函数
  10. 主进程和子进程_Python - 进程-线程-协程