预备知识:掌握一些mfc基本的控件用法以及bmp格式的图片的一些内容,当然不知道也无伤大雅。
然后将PictureControl的ID随便改个名字,必须改(因为默认它为静态的)
双击打开按钮进行添加事件响应程序,当然在这之前你也可以改变按钮的ID,这样有利于程序的读的顺眼。如果你都双击完了,就别再改ID!!!这里我改成了IDC_OPEN和IDC_SAVE
然后将下列代码复制到时间相应程序中:
CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "位图文件(*.bmp)|*.bmp||");
if (dlg.DoModal() != IDOK) return;
CFile file(dlg.GetPathName(), CFile::modeRead);
这时有可能会出现这种错误:
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2664 “CFileDialog::CFileDialog(const CFileDialog &)”: 无法将参数 5 从“const char [24]”转换为“LPCTSTR” Demo f:\csdn_demo\demo\demo\demo\demodlg.cpp 162
可以这么解决:
点击解决方案,然后找到你的项目名称,右键然后点击属性,在常规中将字符集改为使用多字节字符集即可。
这时候你已经可以选择文件了。
①文件头:
file.Read(&m_file_Head, sizeof(BITMAPFILEHEADER)); //WORD是一个字,两个字节。
在.h中添加变量m_file_Head的变量声明:
BITMAPFILEHEADER m_file_Head;
bfSize = bfOffBits + 图片像素数据的大小(图片的行(还有个四字节对齐问题)*列)。
bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +1024(灰度图像有的颜色表,彩色图像没有)。
②位图信息,包括位图信息头和颜色表
添加以下代码
//读取整个位图信息,包括信息头和颜色表
if (m_pdataInfo != NULL) delete[] m_pdataInfo; //看看这段内存是否已经用了
m_pdataInfo = (BITMAPINFO*)new BYTE[m_file_Head.bfOffBits - 14];
file.Read(m_pdataInfo, m_file_Head.bfOffBits - 14);
// BITMAPINFOHEADER *pBmih = m_pdataInfo->bmiHeader;//位图信息头指针其实就是位图信息的指针
//int nWidth, nHeight, nBitCount;
m_nWidth = m_pdataInfo->bmiHeader.biWidth;
m_nHeight = m_pdataInfo->bmiHeader.biHeight;
m_nBitCount = m_pdataInfo->bmiHeader.biBitCount;
m_nDibWidth = (m_nWidth*m_nBitCount + 31) / 32 *4;//
int nDL = m_nDibWidth * m_nHeight;//字节
if (m_pImage != NULL) delete[] m_pImage;
m_pImage = new BYTE[nDL];
file.Read(m_pImage, nDL);
file.Close();
RedrawWindow();//回调OnPaint,在OnPaint中实现显示
然后我们添加变量,添加变量肯定要在类的声明中添加,故打开类视图,双击C×××Dlg,将下列变量定义加入
public:
BYTE *m_pdataInfo;//存位图信息的变量指针变量
int m_nWidth, m_nHeight, m_nBitCount;//从左到右依次是图像宽度,高度,每个像素的位数
BYTE *m_pImage;//存像素数据
考虑到两个都是指针类型的变量,我们就要有销毁和初始化,初始化肯定是在OnInitDialog中,销毁需要自己加。
初始化:双击OnInitDialog,添加如下代码:
m_pdataInfo = NULL;
m_pImage = NULL;
如图所示:
销毁:添加OnDestory函数
再将以下代码贴到OnDestory函数中
if (m_pImage != NULL) delete[] m_pImage;
if (m_pdataInfo != NULL) delete[] m_pdataInfo;
然后我们解释解释这句代码:
m_nDibWidth = (m_nWidth*m_nBitCount + 31) / 32 *4;
这句话就牵扯到四字节对齐问题,它的目的就是保证每行的像素个数都是4的倍数,这句话囊可以将每行不是4的倍数转成每行都是4倍数的像素个数。
假设m_nWidth = 3,8位灰度图像,由于这些全部都是整型,所以(3*8+31)/32*4 = 55/32*4 = 1*4 = 4。
为啥弄成四字节对齐,这牵涉计算机底层了,我们也没必要知道。
接下来我们要用到那个框了,先给框加变量。如图所示
在OnPaint中添加:
//显示第一个框
if (m_pdataInfo != NULL && m_pImage != NULL)
{
//以上只是读完了图片,下面开始显示,m_ctrlImage就是那个框
HDC hdc = m_ctrlImage.GetDC()->GetSafeHdc(); //获得显示的句柄
//把图形的哪一部分显示
CRect rc;
m_ctrlImage.GetWindowRect(&rc);//得到框的信息给到rc中,左上角是原点,
m_ctrlImage.ScreenToClient(&rc);//坐标转换,将框的坐标转换到rc中
SetStretchBltMode(hdc, STRETCH_HALFTONE); //设置缩放显示模式
StretchDIBits(hdc, rc.left, rc.top, rc.Width(), rc.Height(),
0, 0, m_nWidth, m_nHeight, m_pImage, (BITMAPINFO *)m_pdataInfo, DIB_RGB_COLORS, SRCCOPY);
}
关键是要区分彩色图片和灰度图片的差别。
双击控件按钮保存添加处理代码,
然后加入以下程序:
// TODO: 在此添加控件通知处理程序代码
int nDL = m_nDibWidth * m_nHeight;
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoHeader;
BYTE datacolor[sizeof(RGBQUAD) * 256];
BYTE *m_pImage2 = NULL;
/*
BYTE *m_pImage_t = NULL;
if (m_pImage_t != NULL) delete[] m_pImage_t;
m_pImage_t = new BYTE[nDL];
memcpy(m_pImage_t, m_pImage, nDL);
*/
CFile bmpFile;
CString strFileName;
CFileDialog dlg(FALSE, "*.BMP", NULL, NULL, "位图文件(*.BMP)|*.bmp;*.BMP");
if (!dlg.DoModal() == IDC_SAVE) return;
strFileName = dlg.GetPathName();
if (bmpFile.Open(strFileName, CFile::modeCreate | CFile::modeWrite) == 0)return;
fileheader.bfType = 0x4d42;
fileheader.bfOffBits = m_file_Head.bfOffBits;
fileheader.bfSize = m_file_Head.bfOffBits + nDL;
fileheader.bfReserved1 = 0;
fileheader.bfReserved2 = 0;
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
infoHeader.biWidth = m_pdataInfo->bmiHeader.biWidth;
infoHeader.biHeight = m_pdataInfo->bmiHeader.biHeight;
infoHeader.biBitCount = m_pdataInfo->bmiHeader.biBitCount;
infoHeader.biClrImportant = m_pdataInfo->bmiHeader.biClrImportant;
infoHeader.biClrUsed = m_pdataInfo->bmiHeader.biClrUsed;
infoHeader.biCompression = m_pdataInfo->bmiHeader.biCompression;
infoHeader.biPlanes = m_pdataInfo->bmiHeader.biPlanes;
infoHeader.biSizeImage = nDL;
infoHeader.biXPelsPerMeter = m_pdataInfo->bmiHeader.biXPelsPerMeter;
infoHeader.biYPelsPerMeter = m_pdataInfo->bmiHeader.biXPelsPerMeter;
if (m_pImage2 != NULL) {
delete[]m_pImage2;
}
m_pImage2 = new BYTE[nDL];
memcpy(m_pImage2, m_pImage, nDL);
bmpFile.Write(&fileheader, 14);
bmpFile.Write(&infoHeader,40);
if (m_pdataInfo->bmiHeader.biBitCount == 8) {
memcpy(datacolor, m_pdataInfo->bmiColors, 1024);
bmpFile.Write(datacolor,1024);
}
bmpFile.Write(m_pImage2, nDL);
bmpFile.Close();
再看下效果: