自学内容网 自学内容网

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

      本案例程序目的是将一般图像文件转换成图标格式(ico)。实现起来不是很复杂,这里为了介绍MFC的具体使用方法,在程序界面上分成几个功能块,包括:打开图像文件、选择ICON大小、转换、预览、保存等。相关具体步骤如下:
一、建立一个基于对话框的MFC应用
       项目名称:PicFileToIcoFile
       其余使用缺省;在Dialog界面,将自动生成的控件删去。
二、程序界面:标题
       在对话框上部居中显示“普通图像文件转Ico文件”几个字。做法:拖入一个静态文本框放在对话框靠上居中位置;设置这个静态文本框:右键静态文本框->属性->ID:改为IDC_TITLE->对齐文本:选择Center。接下来,在PicFileToIcoFileDlg.cpp(以下简称*.cpp)中找到BOOL CPicFileToIcoFileDlg::OnInitDialog()函数,在//TODO:行下面加入以下代码:

   //显示程序界面标题“普通图像文件转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.变量 

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

 2.自定义函数

    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函数,在这个函数中我们实现打开文件、显示文件名功能。这个函数的代码如下:

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实现运行部分或全部代码。具体如下:

void CPicFileToIcoFileDlg::transform(int isTrans, int isPreview, int isSave)
{
HICON hIcon=NULL; //转换后的图标句柄(不是最终的图标文件)
CDC cmptblDC; //声明设备上下文兼容对象
HBITMAPhOldBmp=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()中。
函数代码如下:

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个单选框添加事件处理程序,添加的程序名称及添加的代码如下:

 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:之下先添加下列代码:

// 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:行下面,添加代码:

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

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

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

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


原文地址:https://blog.csdn.net/m0_72128260/article/details/144700920

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!