我的一个MFC小项目

本文记录了一个使用MFC完成的小项目,主要涉及自定义控件以处理图像,包括位图的移动和旋转。作者探讨了在MFC中自定义控件的过程,并学习了SDK中关于图像处理的知识。通过这个项目,作者认识到重要的是在解决问题过程中获得的收获,而不只是单纯地掌握某个工具或库。
摘要由CSDN通过智能技术生成

虽然是小项目,但是还是有点挑战性的,因为从来没有做过一个比较正式的项目;之前在编程过程当中,都是比较粗糙,想到什么就写什么,在没有一个系统的架构前提之下,虽可谓倚马可待,但bug很多,多到自己想法都没有了,最后不得不丢下个“四不像”的一堆代码。

找个借口原谅自己就是自觉阅读之前写下的代码,然后认真总结和分析,谈谈自己的收获。

其实也没什么,就一个处理图像的东西,很多的东西已经被sdk封装了,其实你能使用sdk到游刃有余的地步,那也是一种强的表现了,别被别人的闲言冷语冷落到“认为sdk没出息”。

重要的不是你学会了sdk什么的,重要的是你在完成一个任务的过程当中的收获,more or less。

功能列表

文件

获取图片信息

图像操作

打开

像素宽度

移动

*重新加载

像素高度

旋转

另存为

两点距离

放大

保存

每行象素所占字节数

缩小

退出

当然还有一些具体的要求。

 

因为需要用到显示位图,所以我也突发奇想要自定义一个控件,专门用来对付位图的处理,包括移动旋转之类的,感受到了吧,OO。开始的时候不去借助网络资源,自己操手干起来,但是遇到的问题还是蛮多的。

离开了win32一段时间了,来到MFC就忘本了。现在背背,

image

 

 

大概的过程就是这个(其实还是翻了书)。

自定义控件的思路也是这样的,只是createwindow之后的东西(消息处理过程还是要我们操手)IDE帮我们做好了,注意就算在win32下我们还是要对控件createwindow的。上面说消息处理过程还是要自己动手,就是标准控件的消息处理过程已经被包装好了,但是我们自定义的控件会有我们自己想要处理的消息。

在对话框资源窗口添加了Custom Control之后,在属性对话框中要增加Class,在这里要注意填写的是你的注册窗口类而不是你的窗口类,明白人懂的。

添加了一个窗口类(继承自CWnd)之后,里边什么都没有,当然除了动态创建,消息映射,以及CWnd的一些函数还是有的,因为这是CWnd自有的,“爸爸有,儿子也要有”(C++里边的“遗传”好蛋疼)。任何一个窗口都要注册窗口类,所以一定要先在构造函数里面注册好。代码如下:

BOOL  CBMPViewer::RegisterWndClass()
{
     WNDCLASS windowclass; 
     HINSTANCE  hInst = AfxGetInstanceHandle();
 
     windowclass.style =  CS_HREDRAW | CS_VREDRAW |CS_OWNDC; 
     windowclass.lpfnWndProc = ::DefWindowProc; 
     windowclass.cbClsExtra = windowclass.cbWndExtra = 0; 
     windowclass.hInstance = hInst; 
     windowclass.hIcon = NULL; 
     windowclass.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW); 
     windowclass.hbrBackground =( HBRUSH )::CreateSolidBrush(#808a87);
     //::GetSysColorBrush(COLOR_WINDOW); 
     windowclass.lpszMenuName = NULL; 
     windowclass.lpszClassName = L "MYCLASS" ;
 
     if  (!AfxRegisterClass(&windowclass)) 
    
         AfxThrowResourceException(); 
         return  FALSE; 
    
     return  TRUE;
}

之后的话就不废话了,想干嘛就干嘛。

在自定义控件当中只有在onpaint()中的设备环境才是有效的,在其他的方法当中你也无法get到,这让我很疑惑,调试了一下,发现连hwnd都是unused的(好奇怪)。如果你发现了,就告诉我。

BitmapSize=bmp.bmWidthBytes*bmp.bmHeight; 在这一次实验当中,涉及到了比较多的BMP图片的学习,包括基本的组成和操作(放大缩小等)。下面是DIB的文件格式:

 

 

image

这就是BMP图片的组成,可以当成是他的数据结构,这里面就存放了显示图片需要的数据。结构体内的数据比较复杂,但是在这里讲几点。

offsetbit 为文件头到像素为的位移;

imagesize为像素位的大小;

size为整个DIB文件的大小。

文件头当中,offsetbit 的计算其实很简单,就是文件头和信息头的大小加起来(当然默认是24位的位图),但是因为他们是固定大小的的所以比较简单。

imagesize计算的方式看起来很复杂:

((((m_bdBmpdata.width*32)+31)/32)*4)*m_bdBmpdata.height,因为所占字节数不足4的倍数的话要用0来补全,慢慢理解一下。

如果你准备的信息比较完善的话还可以用下面的公式来计算:

BitmapSize=bmp.bmWidthBytes*bmp.bmHeight;

第一个是BITMAP结构中的字段,表示每行所占的字节数。

8位位图除了可以索引彩色图像外,也可以是灰阶图像,相信更多的是用于灰度的图像,既然有8位的灰阶图像,也就是说从白到黑分成256种渐变,那16位灰阶图像也是存在的,只不过从白到黑分成2^16种渐变;但这是一种很大的浪费我觉得,因为灰阶图像应用不是非常广,在一些专业领域或许会用到。

 

而如今PC下的更多的是24位的,32位的,16位的也有。一开始的时候还不知道什么是RGB,其实简单来说就是Red,Green,Blue分别用一定的位数来存储他们的值。16位以上多见RGB格式的图,16位位图图片还可分为R5G6B5,R5G5B5X1(有1位不携带信息,其实就是最高位),R5G5B5A1,R4G4B4A4 等等。

其中RGB555,BITMAPINFOHEADER信息头字段biCompression成员的值是BI_RGB,它的存储格式是:

XRRRRRGG GGGBBBBB,注意它跟24位位图没有颜色表。

24位位图更简单,它的存储格式是:

RRRRRRRR GGGGGGGG BBBBBBBB

而32位的位图,新增了一个透明度,这在XP下很常见了:

AAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB

 

图形处理中,通常把RGB三种颜色信息称为红通道、绿通道和蓝通道,相应的把透明度称为Alpha通道。由RGB形成的图像均称做真彩色


OK,差不多就啰嗦到这里吧。真正在写程序中遇到的问题是位图的转换:将24位真彩位图转换为8位灰阶位图。而没有上面的东西,下面的东西看起来会很吃力

 

问题描述的清楚一点吧:原图片是24位位图,而如今项目当中的绝多数操作都是基于8位位图,不可能让客户多一手准备8位的位图,自然而然转换的任务就落在了程序的头上了。

 

我的解决方法是这样:就用一个方法来实现这个转换的操作,而函数的参数就一个路径(让客户只提供路径,这方便很多了),在转换之后再写为另一个8位位图文件。

 

或许一开始拿到这样的任务,还是摸不着头脑,不知道从何下手;我画了下面的一张图,

image

 

Y是什么,Y就是明度,在灰阶位图当中,只有明度(灰阶值),而没有色度和浓度,它和RGB是有区别的,是两种不同的颜色编码方法,RGB注重的是色彩,而YUV注重的亮度。幸运的是,两者之间可以进行转换,

 

\begin{array}{rll}Y &= 0.299 * R + 0.587 * G + 0.114 * B \\U &= 0.436 * (B - Y) / (1 - 0.114) \\V &= 0.615 * (R - Y) / (1 - 0.299)\end{array}

 

对,上面的第一个公式正是我们想要的,熟悉DIB格式的童鞋都应该有思路了吧,像素位里面就存着RGB,它与上面的公式能够帮助我们将24位位图转换为8位灰阶位图。

 

其中有个问题,就是涉及到位图的每行所占字节数的问题,重申一次,Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,每行所占字节数的计算公式:((((width*32)+31)/32)*4)。

 

举一个简单的例子:假如一个8位灰阶位图,它的宽是31,则每一行需要31个字节存储,因为字节数必须是4的整倍数,所以应该是32字节,此时BITMAPINFOHEADER字段当中,biWidth=31,biBitCount=8,但要清楚,每行所占字节是32字节。因此在做每行转换为8位位图的时候,我们需要每个扫描行+32,而不是每个扫描行+31。

所以下面所展示的程序,看到宽度转换可以回到这里找答案。上代码吧,

void  Convertto8Bit()
{
     HANDLE  hFile;                   //文件句柄
     DWORD  dwWritten;            //记录以写入的字节数
     hFile   =   CreateFile(L "F:\\1.bmp" ,GENERIC_READ,
     FILE_SHARE_READ,
     NULL,
     OPEN_EXISTING,
     FILE_FLAG_SEQUENTIAL_SCAN,
     NULL);
 
     BITMAPFILEHEADER  bmfh;                 //文件头<
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值