MFC学习笔记之一(绘图+控制)

简介:

学习来源是:http://blog.csdn.net/column/details/megogame.html

本章内容是这个博客的(一)~(六)的内容,(六)中只包含是其图像双重缓冲技术的内容

本文是对其详细解释、加深、以及扩展(如果我有能力的话)


1)首先要定义图片。

①如果是图像双重缓冲技术的,定义一个用来存放背景的CDC(例如CDC m_cache),这个CDC可以理解为画图的环境

 

②然后这个CDC得准备一个东西来画画(比如说画布/区域,画笔,颜料盒/调色板等,也可以是别人的图如CBitmap),在这里,这个东西是位图(就是把别人的图复制到自己的画图环境中),这张位图需要和你要绘制的那个区域(比如整个窗口)是一样大的(CBitmap m_cacheCBitmap),但这个图究竟是什么,在具体画的时候看加载哪个了(通过SelectObject来切换,不仅可以切换图,也可以切换成别的笔等)。

 

③我们肯定是需要背景的,背景图呢,则需要有一个类对象来加载他。如果有不同的背景图,则需要有不同的类对象来加载。因此,有几个背景图就需要几个加载背景图的类对象(例如CImage m_bg;  又假如如果有多个,可以m_bg1; m_bg2;之类的,我觉得,不确定);

 

④然后得有这个背景图得有一个描述它的坐标吧(通过四个坐标,可以描述它的尺寸),CRect m_client

CRect是一个类,里面有四个坐标(leftrighttopbottom

 

⑤然后背景图上得有其他东西吧,比如人物(人物的图就不用是CBitmap类了,改用CImage类,因为这个是画到背景上的),于是CImage m_hero;每个图都需要声明一个。

 

⑥人物也得有描述它的坐标(尺寸)吧,于是CRect m_heroPos,如果有更多图,每个都得有对应尺寸。

 

2)图片确定了,下来跳过创建窗口的菜单、状态栏设置界面(具体的用的时候再查)。这里开始说准备事项了,也就是,创建窗口前需要做的工作(CChildView::PreCreateWindow() )函数。

①没必要的东西删掉,具体怎么删(找教程);

 

②既然定义了加载图的对象,那么这些对象该加载图了吧。

m_bg.Load(_T("..\\练习\\res\\bg.png"));//读取背景图

m_hero.Load(_T("..\\练习\\res\\1.png"));//读取英雄图

分别加载背景图和英雄图。这里是相对路径,注意要写两个\\

 

③如果非背景图是透明的,总得有处理透明图的函数把(貌似不处理透明的图的透明部分就是白色,而不是透明的),于是有函数TransparentPNG(&m_hero)(注意,这个函数不是自带的,需要自己定义其函数);

 

④之前有两个坐标CRect类对象(背景、和其他绘制在背景上的),那么,要么其通过某个途径获取CRect值来赋值(例如在OnPaint()函数里,GetClientRect(&m_bg)函数调用后,给CRect类对象m_bg赋值),要么需要给其一个初始的坐标,而这个初始的坐标,也决定了这个图形的大小(当然,后期也可以改变大小)。

给坐标赋值的方式是:m_heroPos.left=1;(其左边的坐标是1,可以赋值为负数),然后依次是righttopbottom。其中rightbottom可以根据lefttop分别加一个值(这个值其实变相描述了这个CImage类对象加载的图的尺寸)。

 

3)加载完图了,也给其初始的坐标了,也就是说,做完窗口的准备工作了。于是,要创建窗口了,在创建窗口之前,有一个窗口创建中的函数(OnCreate(),可以通过类向导创建),这个函数不创建窗口,只是在窗口显示前设置窗口的属性,如风格,位置等。也可以在这个函数内,创建定时器。

①如果需要画面定时刷新,那么需要创建一个定时器(SetTimer(TIMER_PAINT, 10, NULL);

这个函数第一个参数是定时器的ID(可以传递给OnTimer()函数,然后在函数内部根据传递的不同ID决定不同的动作),

 

②第二个参数指多久给OnTimer()函数(如果在第三个函数内使用NULL来使用默认OnTimer()函数的话)传递给一个消息,执行第三个参数的函数。

 

③第三个参数指执行的函数,如果不使用NULL,则在这里放执行的函数。也就是多久间隔(第二个参数)执行一个第三个参数的函数。

 

 

3.5)创建完定时器,肯定要有响应定时器的函数了(这个就是OnTimer())

①可以通过switch(参数) 来根据传递给该函数的参数(参数是定时器的ID,也就是SetTimer的第一个参数),决定执行哪一条指令。

 

OnTimer()可以通过类向导添加。其说明是:指示计时器的超时间隔已过。

 

③定时器的第一个参数是一个整型,可以用#define来为该整型取别名。

#define 别名 本名

 

define是直接的文本替换,而typedef不是,并且顺序相反

#typedef 本名 别名

 

 

4)理论上,这里还应该有个Create()函数,不过不知道,先跳过。

 

5)下来窗口已经被创建好了,需要绘图了。即OnPaint()函数。

①首先,声明一个CDC类对象指针(CDC* cdc=this->GetDC()),这个指针(cdc)用来指向当前窗口的。

 

②将当前窗口的尺寸赋给一个CRect对象(例如GetClientRect(&m_client))之所以给他,是因为背景图一般和当前窗口的尺寸是一样的,如果不一样,那么可以在(1)中声明一个专门用于储存当前窗口尺寸的CRect类对象(例如CRect m_a)

 

③因为使用了双重图像缓冲技术(CDC m_cache)(用于防止贴图闪烁)(其原理是先将图像画到内存中的一个DC上,就是m_cache,然后再将内存的DC放到当前窗口的DC之上,因此可以防止闪烁的出现),这个CDC类对象是用于充当缓冲DC,其他图像画到这个缓冲的DC上(因此,所有图像都应该贴到这个DC上,再把这个DC放到窗口的DC)。因为这个缓冲DC要放到当前窗口的DC上,那么肯定要和当前窗口的DC兼容嘛,所以需要调用函数创建一个兼容的DC(即m_cache.CreateCompatilbeDC(NULL)NULL表示兼容)。

 

③这个DC要绘画的话,肯定要有画图的工具,在这里,我们用位图(CBitmap m_cacheCBitmap)作为画图工具(其他图画到这个位图之上)。,那么这个位图也要被创建,并且要兼容当前窗口,因此调用函数

m_cacheCBitmap.CreateCompatibleBitmap(cdc,m_client.Width(),m_client.Height()),就是说,创建一个兼容(跟cdc参数兼容)的位图对象,他的尺寸跟客户区宽和高是一样的(于是从0,0开始绘制,就刚好把图绘制到里面了);

 

④然后用DC把这个画图的工具联系起来(选择它),即cache.SelectObject(&m_cacheCBitmap);,注意,这里有个&运算符。原因在于其绘图时,是绘制在DC上的,而不是绘制到某个图上,这里是内存DC,因此必须让这个DC选择这个图,才能画到这个DC上。于是,其他图就可以画到这个DC上了(假如不关联,会发现无法将其他图画到这个DC上,猜测是因为不关联则不知道内存中的这个DC的区域大小)。

 

⑤因为我们准备好位图了(他是有一定宽度和高度的的),于是可以把背景图和人物图画到这个上面了(但超出部分无效)。因此调用函数:

m_bg.Draw(m_cache, m_client); //画背景图

m_hero.Draw(m_cache,m_client); //画人物图

需要先画下面的,再画上面的,因为后画的会遮挡先画的。

 

⑥我们把该画的已经画到缓冲DC上了,但他还在内存中,并没有被显示出来,因此,我们需要把其显示到当前窗口上,这个时候,需要调用

cdc->Bitblt(0, 0, m_client.Width(), m_client.Height(), &m_cache, 0, 0, SRCCOPY);

来将内存DC中的放到当前窗口的某部分区域(或者全部区域)。

 

⑦这个时候,图已经画出来了,但需要加上一行命令ValidateRect(&m_client);这行命令的意思是,让当前窗口的某个区域(m_client来说明)变得有效。否则,会产生一个问题就是,窗口会无效,而无效就会导致程序不断发送WM_PAINT命令要求重画,而重画呢,就会导致程序大量占用系统资源,主要是CPU的资源会被占用。假如加上了这条命令,那么系统会发现,哦,原来是有效的,就不会发送这条命令了,于是降低了资源占用,也避免产生一些其他的问题。

 

⑧绘图完了,窗口也有效了,为了防止在函数结束后丢失已申请的句柄(产生内存泄露问题),因此,申请的东西都应该还给系统。GetDC()得到的,需要Release()来还给(如Release(cdc)),而其他的,使用另外的方式,如m_cache.DelectDC()m_cacheCBitmap.DeleteObject(); 来释放(前者是DC,后者是画图的工具,因此命令不同)。

 

 

6)图画完了,但我们总是需要操纵的(除非完全只有动画),那么需要响应我们操纵的类,如响应键盘和响应鼠标的。而响应鼠标和键盘的函数,可以用类向导创建。

①响应键盘的按键的(如按下去是OnKeyDown函数,抬起来是OnKeyUp函数),传递的参数第一个是按键码(其和键盘字母的大写的ASCII码是一样的),第二个是保存了键被重击的次数,第三个是定扫描代码。后两个暂时不懂不关心。第一个可以通过switch命令,在按不同键的时候,执行不同的代码。例如修改人物坐标(然后再绘图发生位置变化)以达成移动的效果。

 

②响应鼠标的按键的,有多个函数,例如鼠标左键按下去且按一下的OnLButtonDown()函数,当按下去之后,将执行这个函数内部的代码。

 

 

于是,基本的 绘图 控制 则这么完成了。


目录
相关文章
|
存储 编解码 缓存
Qt开发技术:Qt绘图系统(一)绘图系统介绍
Qt开发技术:Qt绘图系统(一)绘图系统介绍
Qt开发技术:Qt绘图系统(一)绘图系统介绍
|
7月前
|
API C语言 图形学
EasyX图形库学习(一、窗口创建函数initgraph、背景颜色设置setbkcolor、图形绘制函数)
EasyX图形库学习(一、窗口创建函数initgraph、背景颜色设置setbkcolor、图形绘制函数)
|
7月前
MFC绘图操作
MFC绘图操作
42 0
|
7月前
win32编程 -- GDI绘图操作
win32编程 -- GDI绘图操作
64 0
|
7月前
Qt6学习笔记十三(绘图)
Qt6学习笔记十三(绘图)
146 0
|
C++
QT图形视图系统 - 使用一个项目来学习QT的图形视图框架 - 始篇
详细的介绍可以看QT的官方助手,那里面介绍的详细且明白,需要一定的英语基础,我这里直接使用一个开源项目来介绍QGraphicsView、QGraphicsScene的使用。
313 1
|
定位技术 图形学 容器
Qt5入门学习——图形视图框架
Qt5入门学习——图形视图框架
395 0
Qt5入门学习——图形视图框架
|
编解码 API 图形学
Qt 5——绘图和绘图设备
Qt 5——绘图和绘图设备
215 0
Qt 5——绘图和绘图设备
|
编解码 API 图形学
10、QT基础——绘图和绘图设备
10、QT基础——绘图和绘图设备
260 0
10、QT基础——绘图和绘图设备
|
缓存 索引 Windows