MFC案例:图片文件转图标(ico)格式

本案例程序目的是将一般图像文件转换成图标格式(ico)。实现起来不是很复杂,这里为了介绍MFC的具体使用方法,在程序界面上分成几个功能块,包括:打开图像文件、选择ICON大小、转换、预览、保存等。相关具体步骤如下:

一、建立一个基于对话框的MFC应用

项目名称:PicFileToIcoFile

其余使用缺省;在Dialog界面,将自动生成的控件删去。

二、程序界面:标题

在对话框上部居中显示"普通图像文件转Ico文件"几个字。做法:拖入一个静态文本框放在对话框靠上居中位置;设置这个静态文本框:右键静态文本框->属性->ID:改为IDC_TITLE->对齐文本:选择Center。接下来,在PicFileToIcoFileDlg.cpp(以下简称*.cpp)中找到BOOL CPicFileToIcoFileDlg::OnInitDialog()函数,在//TODO:行下面加入以下代码:

cpp 复制代码
   //显示程序界面标题"普通图像文件转Ico文件	"
       CFont titleFont;
       titleFont.CreateFont(36, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
	  FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
	  CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
	  FIXED_PITCH | FF_MODERN, _T("宋体"));
       GetDlgItem(IDC_TITLE)->SetFont(&titleFont);
       CString str;
       str = L"普通图像文件转Ico文件";
       GetDlgItem(IDC_TITLE)->SetWindowText(str);

三、为保证一些变量或自定义函数在不同函数中都能使用,在PicFileToIcoFileDlg.h中,类内以public:方式声明以下变量和自定义函数:

1.变量

cpp 复制代码
    CString m_filePath; //记录打开源图像文件路径及文件名
    CImage sourceImage=L""; //CImage类对象,用于载入源图像 
    int hadTrans = 0; //是否完成转换的标志
    int cxIcon=32, cyIcon=32; //icon图标的大小,默认32*32

2.自定义函数

cpp 复制代码
    void transform(int isTrans,int isPreview,int isSave); //转换等功能实现
    void recove(); //恢复部分界面

(以上两个函数具体意义见下面实现部分)

四、程序界面:打开源图像文件并显示文件名

1.从工具箱中拖入一个Button放在对话框左偏上位置,然后:右键Button->属性->描述文字改为:"打开源图像文件"、ID改为:IDC_OPENFILE。

2.再从工具箱中拖入一个文本编辑框(Edit Control),放到Button右边,用途是作为打开文件的显示界面;右键文本编辑框->属性->ID:改为IDC_EDIT_OPEN。

3.再右键"打开源图像文件"控件->选择:添加事件处理程序->类列表选择:CPicFileToIcoFileDlg,其余缺省。此时,在*Dlg.cpp文件中,可以看到增加了一个OnBnClickedOpenfile函数,在这个函数中我们实现打开文件、显示文件名功能。这个函数的代码如下:

cpp 复制代码
void CPicFileToIcoFileDlg::OnBnClickedOpenfile()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog fileDlg(  //声明文件对话框类对象
		TRUE, //TRUE --- 打开文件、FALSE ---保存文件
		NULL, 
		NULL, 
		OFN_HIDEREADONLY, 
		L"All Files(*.*)||", 
		AfxGetMainWnd(), 
		0UL, 
		1);
	fileDlg.DoModal(); //显示打开文件对话框(可在此选择图像文件)
	m_filePath = fileDlg.GetPathName();//获得路径及文件名
	GetDlgItem(IDC_EDIT_OPEN)->SetWindowText(m_filePath);
           //在文本编辑框中显示
        recove(); //后添加
}

五、在完善其它程序界面前,先将前面声明的两个自定义函数实现一下。

1.实现transform函数,我们在这个函数中实现转换、预览、保存等功能,也就是程序界面上"开始转换"、"预览"、"保存"分在三个Button上执行,但调用的都是这一个函数。通过控制这个函数的参数isTrans、isPreview、isSave实现运行部分或全部代码。具体如下:

cpp 复制代码
void CPicFileToIcoFileDlg::transform(int isTrans, int isPreview, int isSave)
{
	HICON hIcon=NULL; //转换后的图标句柄(不是最终的图标文件)
	CDC cmptblDC; //声明设备上下文兼容对象
	HBITMAP	hOldBmp=NULL;//声明默认bitmap
   	if (isTrans == 1) //"选择转换"Button按下式执行
	{
	//1.声明CImage对象,将欲转换的图片载入对象中
		CImage sourceImage;
		sourceImage.Load(m_filePath); //载入图像
		int w = sourceImage.GetWidth(); //获取图像宽度(像素)
		int h = sourceImage.GetHeight(); //获取图像高度(像素)
	//2.定义与当前设备上下文兼容对象,再定义与当前设备上下文指针兼容的BITMAP对象(尺寸是我们最后需要的Icon尺寸),并把这个BITMAP对象选入兼容设备上下文
		CDC* CurrentDC = GetDC(); //声明设备上下文指针并获得当前设备(指显示器)指针
		cmptblDC.CreateCompatibleDC(CurrentDC); //让生成的设备上下文对象与当前设备上下文兼容
		CBitmap bmp; //声明CBitmap对象bmp
		bmp.CreateCompatibleBitmap(CurrentDC, cxIcon, cyIcon); //生成与当前设备上下文指针兼容的CBitmap
		hOldBmp = (HBITMAP)cmptblDC.SelectObject(bmp); //将bmp选入兼容设备上下文对象并保留缺省指针
		//3.调用CImage成员函数,将原图像拷贝到兼容设备上下文中,再获得已选入兼容设备上下文中的bmp指针
		sourceImage.StretchBlt(cmptblDC.m_hDC, 0, 0, cxIcon, cyIcon, 0, 0, w, h, SRCCOPY);//拉伸(缩放)拷贝图像函数
		HBITMAP hBmp = (HBITMAP)bmp.m_hObject;//通过bmp的属性获得其指针(此时bmp中已有图像)
		//4.声明一个Icon信息结构体对象并将bmp作为参数写入其属性中
		ICONINFO iconinfo;
		memset(&iconinfo, 0, sizeof(iconinfo)); //将iconinfo赋初值为0
		iconinfo.fIcon = 1; //1代表图标 0代表光标(此处我们需要的是图标)
		iconinfo.hbmColor = hBmp; //图标颜色位图的句柄(HBITMAP类型)
		iconinfo.hbmMask = hBmp; //指定图标位掩码位图(HBITMAP类型)
		iconinfo.xHotspot = 0; //对于图标而言此坐标无用
		iconinfo.yHotspot = 0;
	//5.创建图标
		hIcon = CreateIconIndirect(&iconinfo);
		if (!hIcon)
		{
			MessageBox(L"转换失败......");
			return;
		}
	   sourceImage.Detach(); //将img对象与bmp分离
	}
	//6.预览图标(需要同时运行转换部分)
	if (isTrans == 1 && isPreview == 1)
	{
		if (hadTrans==0)
		{
			MessageBox(L"请先进行转换操作...");
			return;
		}
	    //显示图标
	    DrawIconEx(GetDC()->m_hDC, 280, 350, hIcon, 64, 64, 0, NULL, DI_NORMAL);
		hadTrans = 0;
	}
	if (isTrans == 1 && isSave == 1)
	{
	//7.声明创建图片对象专用结构体并进行赋值
		PICTDESC pictdesc; //这个结构体的属性中包含通过OleCreatePictureIndirect函数创建图片对象的参数
		memset(&pictdesc, 0, sizeof(pictdesc)); //将该结构体赋初值为0
		pictdesc.cbSizeofstruct = sizeof(pictdesc); //结构体字节长度属性
		pictdesc.picType = PICTYPE_ICON; //图片类型
		pictdesc.icon.hicon = hIcon; //(将图片选入)路线:hIcon<-iconinfo<-hBmp<-img
	//8.创建图片对象
		IPicture* ipPic = NULL; //声明一个图片接口指针ipPic(IPicture接口可接收图片)
		HRESULT resOle = OleCreatePictureIndirect(&pictdesc, IID_IPicture, TRUE, (LPVOID*)&ipPic);
		//创建由pictdesc对象初始化的新图片对象,结果存入IPicture*指向的地址
		//参数:1、pictdesc取址,2、接口指针类型,3、销毁对象时销毁图片,4、结果接收指针
		if (FAILED(resOle))
		{
			MessageBox(L"接口指针定义失败...");
			exit(1);
		}
	//9.将图片对象内容、字节数传入IStream流接口对象中
		LPSTREAM pStream = 0; //声明一个空的流接口对象指针
		HRESULT hResult = CreateStreamOnHGlobal(0, TRUE, &pStream);
		//创建流指针对象(申请空间)
		if (FAILED(resOle))
		{
			MessageBox(L"申请流对象内存空间失败...");
			exit(1);
		}
		LONG size = 0;
		ipPic->SaveAsFile(pStream, TRUE, &size);
		//图片对象ipPic调用成员函数SaveAsFile将自身图像写入pSream,字节数存到size
	//10.将流对象中的内容保存进ico类型文件
		LARGE_INTEGER offsetLI; //Union组合起来的用来表示大的整数的结构体
		offsetLI.HighPart = 0;
		offsetLI.LowPart = 0;
		ULARGE_INTEGER ulnewpos; //同上,但无符号,用于记录流内部指针的位置
		pStream->Seek(offsetLI, STREAM_SEEK_SET, &ulnewpos); //对pStream内部指针进行定位
		//参1:LARGE_INTEGER类型的偏移量,此处为0
		//参2:STREAM_SEEK_SET(值=0),文件首
		//参3:将定位指针信息存入ulnewpos
		HANDLE hFile = CreateFile(L"ico.ico", GENERIC_ALL, 0, 0, CREATE_ALWAYS, 0, 0);
		//生成ico文件句柄
		//参1:文件名  参2:访问模式  参3:共享模式  参4:安全属性
		//参5:打开文件模式 参6:文件属性 参7:模板文件句柄
		if (hFile)
		{
			char* buf = new char[size]; //定义字符数组,长度为流接口对象中的字节长度
			memset(buf, 0, size); //赋值为0
			ULONG nRead = 0; //定义无符号长整型
			pStream->Read(buf, size, &nRead);
			//参1:指向缓冲区的指针,用于接收流数据
			//参2:试图读取的数据字节数
			//参3:从流对象读取的实际字节数
			ULONG nWrited = 0;
			WriteFile(hFile, buf, nRead, &nWrited, 0);
			//参1:待写入文件的句柄
			//参2:待写入内容
			//参3:要写入数据的字节数量
			//参4:实际写入文件的字节数量
			//参5:是否异步写,一般置为NULL
			CloseHandle(hFile); //关闭文件句柄
		}
	ipPic->Release(); //释放图片接口指针
	}
	cmptblDC.SelectObject(hOldBmp); //恢复兼容对象默认bmp
}

2.实现recove()函数,在transform函数中,有模拟的进度条和预览图标的显示,当重新打开文件或者重新选择ICON大小时,模拟的进度条和预览的图标应隐去,这个函数就是实现这个功能。为保证打开文件时也能实现这个功能,这个函数也要添加到OnBnClickedOpenfile()中。

函数代码如下:

cpp 复制代码
void CPicFileToIcoFileDlg::recove()
{
	CDC* pDC;
	pDC = GetDC();
	int startX = 290;
	int posY = 285;
	int step = 18;
	pDC->SetBkColor(0xF0F0F0);//文字背景灰
	pDC->SetTextColor(0xF0F0F0); //文字前景色
	for (int i = 0; i < 26; i++)
	{
		pDC->TextOut(startX + step * i, posY, L">"); //恢复进度条
	}
	pDC->FillSolidRect(280, 350, 64, 64, 0xF0F0F0); //恢复预览区
	hadTrans = 0;
}

六、程序界面:选择ICON大小

  1. 常用的ico文件像素大小一般有32*32、48*48、64*64、128*128、256*256等类别。这里用单选框进行选择,为排列整齐并让这几个单选框成为一体,故先从工具箱中先拖入一个Group Box控件,位置放在打开文件及显示编辑框的正下方;然后右键Group Box->属性->描述文字改为:选择ICON大小。

2.向Group Box中拖入5个单选框(Radio Button)横向一字排开,分别将其属性中的ID、描述文字进行修改,并为每个控件添加变量,结果如下:

ID 文字描述 变量名

CHOICE32 32*32像素 m_Choice32

CHOICE48 48*48像素 m_Choice48

CHOICE48 64*64像素 m_Choice64

CHOICE128 128*128像素 m_Choice128

CHOICE256 256*256像素 m_Choice256

3.为这5个单选框添加事件处理程序,添加的程序名称及添加的代码如下:

cpp 复制代码
 void CPicFileToIcoFileDlg::OnBnClickedChoice32()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Choice32.SetCheck(TRUE); //置为已选
	m_Choice48.SetCheck(FALSE);
	m_Choice64.SetCheck(FALSE);
	m_Choice128.SetCheck(FALSE);
	m_Choice256.SetCheck(FALSE);
	cxIcon = 32;
	cyIcon = 32;
	recove(); 
}
void CPicFileToIcoFileDlg::OnBnClickedChoice48()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Choice32.SetCheck(FALSE);
	m_Choice48.SetCheck(TRUE);//置为已选
	m_Choice64.SetCheck(FALSE);
	m_Choice128.SetCheck(FALSE);
	m_Choice256.SetCheck(FALSE);
	cxIcon = 48;
	cyIcon = 48;
	recove();
}
void CPicFileToIcoFileDlg::OnBnClickedChoice64()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Choice32.SetCheck(FALSE);
	m_Choice48.SetCheck(FALSE);
	m_Choice64.SetCheck(TRUE);//置为已选
	m_Choice128.SetCheck(FALSE);
	m_Choice256.SetCheck(FALSE);
	cxIcon = 64;
	cyIcon = 64;
	recove();
}
void CPicFileToIcoFileDlg::OnBnClickedChoice128()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Choice32.SetCheck(FALSE);
	m_Choice48.SetCheck(FALSE);
	m_Choice64.SetCheck(FALSE);
	m_Choice128.SetCheck(TRUE);//置为已选
	m_Choice256.SetCheck(FALSE);
	cxIcon = 128;
	cyIcon = 128;
	recove();
}
void CPicFileToIcoFileDlg::OnBnClickedChoice256()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Choice32.SetCheck(FALSE);
	m_Choice48.SetCheck(FALSE);
	m_Choice64.SetCheck(FALSE);
	m_Choice128.SetCheck(FALSE);
	m_Choice256.SetCheck(TRUE);//置为已选
	cxIcon = 256;
	cyIcon = 256;
	recove();
}

七、程序界面:开始转换

1、从工具箱中拖入一个Button放在"选择ICON大小"下方靠左,右键这个Button->属性->ID:改为IDC_TRANS、描述文字:改为开始转换。

2、右键"开始转换",选择添加事件处理程序,在*.cpp文件中可以看到生成了一个函数 void CPicFileToIcoFileDlg::OnBnClickedTrans(),在这个函数中TODO:之下先添加下列代码:

cpp 复制代码
	// TODO: 在此添加控件通知处理程序代码
	if (m_filePath == L"")
	{
		MessageBox(L"尚未打开文件,无转换对象......");
		return;
	}
	transform(1,0,0); //控制参数仅运行转换部分
        hadTrans = 1;
	CDC* pDC;
	pDC = GetDC();
	int startX = 290;
	int posY = 285;
	int step = 18;
	pDC->SetBkColor(0xF0F0F0);//文字背景灰
	pDC->SetTextColor(0x000000); //文字颜色黑
	for (int i = 0; i < 26; i++)
	{
		pDC->TextOut(startX + step * i, posY, L">");
		Sleep(50);
	}

八、预览

1、从工具箱中拖入一个Button放在对话框下方左端,右键这个Button->属性->ID:改为IDC_PREVIEW、描述文字:预 览

2、右键"预 览",选择添加事件处理程序,在*.cpp文件中可以看到生成了一个函数void CPicFileToIcoFileDlg::OnBnClickedPreview(),在这个函数的TODO:行下面,添加代码:

cpp 复制代码
	// TODO: 在此添加控件通知处理程序代码
	if (m_filePath == L"")
	{
		MessageBox(L"尚未打开文件,无转换对象......");
		return;
	}
	transform(1, 1, 0);

六、程序界面:保存

再拖入一个Button放到对话框下方右端,文字描述改为:保 存。右键保存->选择添加事件处理程序,在*.cpp中可以看到函数void CPicFileToIcoFileDlg::OnBnClickedSave()。在该函数TODO:下方,添加下列代码:

cpp 复制代码
	// TODO: 在此添加控件通知处理程序代码
	if (m_filePath == L"")
	{
		MessageBox(L"尚未打开文件,无转换对象......");
		return;
	}
	transform(1, 0, 1);//运行转换、保存文件部分
	MessageBox(L"已将图标在当前目录下保存成ico.ico");

七、至此,程序的界面及功能已经实现完毕,退出时没有需要处理的内容,故退出操作直接使用对话框右上角的"X"完成。

运行后界面截图:

相关推荐
2402_857583493 小时前
“协同过滤技术实战”:网上书城系统的设计与实现
java·开发语言·vue.js·科技·mfc
LaoWaiHang5 小时前
MFC小知识:CEdit::GetSel函数的使用
mfc
arriettyandray5 小时前
MFC/C++学习系列之简单记录1——错误解决与Dialog移植
mfc
KevinRay_20 小时前
Linux系统编程深度解析:C语言实战指南
linux·c语言·mfc·gdb
arriettyandray2 天前
MFC/C++学习系列之简单记录2——thread和Release
mfc
arriettyandray3 天前
MFC/C++学习系列之简单记录4——错误解决与错误提示
mfc
sam-zy3 天前
MFC用List Control 和Picture控件实现界面切换效果
c++·mfc
arriettyandray3 天前
MFC/C++学习系列之简单记录6
mfc
arriettyandray3 天前
MFC/C++学习系列之简单记录——序列化机制
mfc