zoukankan      html  css  js  c++  java
  • VS2008+GDI实现多幅图像的GIF动画制作

      

      相信很多朋友和我一样,经常由于这或那的原因,需制作一些特定格式的图像。如开发过程中需要给菜单、工具条及按钮等添加对应的图形标识,通过代码或资源导入方式加载这些图像时往往会有较高的格式要求。

      比如,为按钮添加"bmp"类型图标,而手头只有"jpg"格式的图像,此时若是简单地在图像编辑器里改变图像大小或保存为后缀"bmp"格式,很多情况是会读取失败并终止程序的。

      当然,在如今这个移动互联网如此发达的时代,早就有很多在线图像制作及转换的网站。普遍遇到的图像转换问题在那里几乎都能解决,方便快捷,只要有网络。

      言归正传,近日我又遇到了类似的问题:将多幅图像(格式可不同)生成GIF动画!

      马上在网上转悠一圈,现成的工具很多,下载挺方便,用的感觉也还过得去。

      问题是慢慢地发现,分享的代码很少,基于VC的就更少了。

      兴致到此,没办法,自己动手丰衣足食吧。但也不可操之过急,毕竟关于GIF的格式及数据添加方法不是很熟悉,于是就把网上能找到的关于这方面的代码先理解,主要有多幅"bmp"图像生成GIF,"jpg"、"tif"等转"bmp"的。

      不过靠谱的很少,大家多少也懂一点,下载前信心满满,后面就......

      好了,接下来说说我的GIF制作过程,用到的语言工具为VS2008(MFC+GDI),方法有些是借鉴前辈分享的资料。

      因为GDI一般都会在安装VS时自动载入,所以使用前只需进行简单的配置就可(其实更准确地说只是进行初始化)。

      建立MFC工程时就取名称"CreateGIF",对应的对话框类名"CreateGIFDlg"。

      1、在头文件"StdAfx.h"中添加以下代码,也可以在其他头文件中添加:

    1 #include <gdiplus.h>
    2 #pragma comment(lib, "gdiplus.lib") 
    3 using namespace Gdiplus;           

      2、在"CreateGIF.h"中添加成员变量,GDI初始化时用:

    1 private:
    2     GdiplusStartupInput m_GdiplusStartupInput;  
    3     ULONG_PTR m_pGdiToken;

      3、重载父类虚函数,用以结束GDI:

    virtual int ExitInstance();

      4、在"CreateGIF.cpp"源文件的初始化函数InitInstance()中添加GDI初始化语句,注意该语句必须放在对话框生成语句之前,否则在对话框中操作时会因为GDI未初始化而出错。

    1 GdiplusStartup(&m_pGdiToken,&m_GdiplusStartupInput,NULL);

      5、在"CreateGIF.cpp"中添加虚函数ExitInstance()的定义:

    1 int CCreateGIFApp::ExitInstance(){  
    2     GdiplusShutdown(m_pGdiToken);  
    3     return CWinApp::ExitInstance();  
    4 }

      到此,GDI的初始化工作算是完成,可以直接使用其库中的资源了——图像类及处理功能,如Image。

      由于位图(BMP)是比较标准的图像格式,将其数据写入GIF文件中,不是难事,以下是实现过程:

      (变量m_sSavePath为事先指定的完整保存路径,类型CString)

     1 CFileDialog dlg(TRUE,"BMP",NULL,0,"图像文件(*.bmp)|*.bmp||",this);
     2 
     3 if(dlg.DoModal() != IDOK)
     4 {
     5     return;
     6 }
     7 
     8 HBITMAP hBmp = (HBITMAP)LoadImage(NULL,dlg.GetPathName(),IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
     9 
    10 if(hBmp == NULL)
    11 {
    12     return;
    13 }
    14         
    15             
    16 BYTE *palette = NULL;
    17 BYTE *pData = NULL;
    18 int nWidth,nHeight;
    19 BYTE bitsPixel = 8;
    20 
    21 if(GetData(hBmp,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
    22 {
    23     DeleteObject(hBmp);
    24     return ;
    25 }
    26 
    27 DeleteObject(hBmp);
    28 
    29 CFile file;
    30 if(file.Open(m_sSavePath,CFile::typeBinary|CFile::modeCreate|CFile::modeWrite) == FALSE)
    31 {
    32     return;
    33 }
    34 
    35 CreateGIFHeard(file,nWidth,nHeight,bitsPixel);
    36 
    37 short int nTransparentColorIndex = -1;
    38 
    39 AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
    40 delete []pData; 
    41 delete []palette;

      上述代码实现的功能是:从文件打开对话框中选择一幅"bmp"格式的图像,读取其数据信息,打开"gif"文件创建头信息,将"bmp"图像数据写入到"gif"文件中。

      可以看到结尾处并没有关闭"gif"打开的文件,所以要想再添加一幅或若干幅"bmp"图像只需重复上述过程。完成之后,添加以下代码结束文件的写操作。GIF文件就生成并保存在了指定路径中。

    1 CloseGIF(file);
    2 file.Close();

      那接下的问题就是:如何将其他格式的图像数据读取并写入到"gif"文件,参与动画制作的大家庭。

      最初在网上找了一篇文章,说可以直接通过文件名将"jpg"等格式图像读取成Bitmap对象,进一步提取出HBITMAP信息,然后就可以同上述过程进行添加了。代码大致如下:

    1 Bitmap   tempBmp(FileName.AllocSysString());   
    2 Color    backColor;         
    3 HBITMAP  HBitmap;     
    4 tempBmp.GetHBITMAP(backColor,&HBitmap);   
    5 return   HBitmap; 

      但是这样做总是不行,个人估计是"Bitmap tempBmp(FileName.AllocSysString());"这个地方并不能真正地将其他类型的图像转为"bmp"。

      后来又找到了一种方法,通过上面提到过的Image类,先将图像读取进来,然后保存为"bmp"格式。当然,读取的时候也可以读取"bmp"类型。指定源图像文件及目标图像文件的路径之后,便可以实现方便、快捷的隐式转换。

     1 Graphics graphics(GetDC()->m_hDC);
     2   
     3 Image image(ToWChar(m_sOpenPath.GetBuffer(m_sOpenPath.GetLength())));       
     4 
     5 CLSID clsid;  
     6 
     7 if(GetImageCLSID(L"image/bmp", &clsid))  
     8 {  
     9     image.Save(ToWChar(m_sBMPSavePath.GetBuffer(m_sBMPSavePath.GetLength())), &clsid, NULL);   
    10 }   

      注意,上面用到的两个函数:ToWCchar()与GetImageCLSID()并不是自带的,而是要自己实现。

     1 WCHAR* CCreateGIFDlg::ToWChar(char *str)  
     2 {  
     3     //在 GDI+中,有关字符的参数类型全部都是 WCHAR 类型的  
     4     //该函数是将传统字符串进行转换  
     5     const int nnn = 1024;
     6     static WCHAR buffer[nnn];  
     7     wcsset(buffer,0);  
     8     MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,nnn);  
     9     return buffer;  
    10 }
    11 
    12 int CCreateGIFDlg::GetImageCLSID(const WCHAR *format, CLSID *pCLSID)  
    13 {  
    14     UINT num=0;  
    15     UINT size=0;  
    16 
    17     ImageCodecInfo* pImageCodecInfo=NULL;  
    18 
    19     GetImageEncodersSize(&num,&size);    
    20 
    21     if(size==0)  
    22         return FALSE; // 编码信息不可用  
    23     // 分配内存  
    24 
    25     pImageCodecInfo=(ImageCodecInfo*)(malloc(size));  
    26 
    27     if(pImageCodecInfo==NULL)  
    28         return FALSE; // 分配失败  
    29     // 获得系统中可用的编码方式的所有信息  
    30     GetImageEncoders(num,size,pImageCodecInfo);  
    31     // 在可用编码信息中查找 format 格式是否被支持  
    32 
    33     for(UINT i=0;i<num;++i)  
    34     {  
    35         //MimeType: 编码方式的具体描述  
    36         if (wcscmp(pImageCodecInfo[ i] .MimeType,format)==0)  
    37         {  
    38             *pCLSID=pImageCodecInfo[i].Clsid;  
    39             free(pImageCodecInfo);  
    40             return TRUE;  
    41         }  
    42     }  
    43     free(pImageCodecInfo);  
    44     return FALSE;  
    45 }

      执行之后,就可以看见转换好的"bmp"图像了。

      离真相不远了,接下来需要做的就是将上述的生成"gif"与"bmp"这两个过程合并。具体如下:

      1 void CCreateGIFDlg::OnCreateGIF() 
      2 {
      3     // TODO: Add extra validation here
      4     if(!UpdateData())
      5     {
      6         return;
      7     }
      8 
      9     CFileDialog dlg(TRUE,"(*.*)|*.*",NULL,0,"图像文件 (*.*)|*.*||",this); 
     10 
     11     if(dlg.DoModal() != IDOK)
     12     {
     13         return;
     14     }
     15 
     16     m_sOpenPath = dlg.GetPathName();
     17     m_sBMPSavePath = dlg.GetFileTitle()+"1.bmp";
     18 
     19     OnFileSave();
     20 
     21     HBITMAP hBmp = (HBITMAP)LoadImage(NULL,m_sBMPSavePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
     22 
     23     if(hBmp == NULL)
     24     {
     25         return;
     26     }
     27             
     28     BYTE *palette = NULL;
     29     BYTE *pData = NULL;
     30     int nWidth,nHeight;
     31     BYTE bitsPixel = 8;
     32 
     33     //为位图生成调色板,得到索引数据、宽、高
     34     if(GetData(hBmp,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
     35     {
     36         DeleteObject(hBmp);
     37         return ;
     38     }
     39 
     40     DeleteObject(hBmp);
     41 
     42     //创建GIF文件
     43     CFile file;
     44     if(file.Open(m_sSavePath,CFile::typeBinary|CFile::modeCreate|CFile::modeWrite) == FALSE)
     45     {
     46         return;
     47     }
     48 
     49     //写GIF头
     50     CreateGIFHeard(file,nWidth,nHeight,bitsPixel);
     51 
     52     short int nTransparentColorIndex = -1;
     53 
     54     //加入第一幅图片
     55     AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
     56     delete []pData;    //这两个变量在此相当于二维数组
     57     delete []palette;
     58 
     59     //////////////////////////////////////////////////////////////////////////////////
     60     while(1)
     61     {
     62         CFileDialog dlg(TRUE,"(*.*)|*.*",NULL,0,"图像文件 (*.*)|*.*||",this); 
     63         if(dlg.DoModal() != IDOK)
     64         {
     65             CDialog::OnOK();
     66             return;
     67         }
     68 
     69         m_sOpenPath = dlg.GetPathName();
     70         m_sBMPSavePath = dlg.GetFileTitle()+"1.bmp";
     71 
     72         OnFileSave();
     73 
     74         HBITMAP hBmp2 = (HBITMAP)LoadImage(NULL,m_sBMPSavePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
     75 
     76         if(hBmp2 == NULL)
     77         {
     78             CloseGIF(file);
     79             file.Close();
     80             return;
     81         }
     82 
     83         if(GetData(hBmp2,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
     84         {
     85             DeleteObject(hBmp2);
     86             CloseGIF(file);
     87             file.Close();
     88             return ;
     89         }
     90         DeleteObject(hBmp2);
     91 
     92         nTransparentColorIndex = -1;
     93 
     94         //加入其它图片
     95         AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
     96         delete []pData;
     97         delete []palette;
     98     }
     99 
    100     //结束GIF
    101     CloseGIF(file);
    102 
    103     file.Close();
    104 
    105     CDialog::OnOK();
    106 }  
    107 
    108 WCHAR* CCreateGIFDlg::ToWChar(char *str)  
    109 {  
    110     //在 GDI+中,有关字符的参数类型全部都是 WCHAR 类型的  
    111     //该函数是将传统字符串进行转换  
    112     const int nnn = 1024;
    113     static WCHAR buffer[nnn];  
    114     wcsset(buffer,0);  
    115     MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,nnn);  
    116     return buffer;  
    117 }
    118 
    119 int CCreateGIFDlg::GetImageCLSID(const WCHAR *format, CLSID *pCLSID)  
    120 {  
    121     UINT num=0;  
    122     UINT size=0;  
    123 
    124     ImageCodecInfo* pImageCodecInfo=NULL;  
    125 
    126     GetImageEncodersSize(&num,&size);    
    127 
    128     if(size==0)  
    129         return FALSE; // 编码信息不可用  
    130     // 分配内存  
    131 
    132     pImageCodecInfo=(ImageCodecInfo*)(malloc(size));  
    133 
    134     if(pImageCodecInfo==NULL)  
    135         return FALSE; // 分配失败  
    136     // 获得系统中可用的编码方式的所有信息  
    137     GetImageEncoders(num,size,pImageCodecInfo);  
    138     // 在可用编码信息中查找 format 格式是否被支持  
    139 
    140     for(UINT i=0;i<num;++i)  
    141     {  
    142         //MimeType: 编码方式的具体描述  
    143         if (wcscmp(pImageCodecInfo[ i] .MimeType,format)==0)  
    144         {  
    145             *pCLSID=pImageCodecInfo[i].Clsid;  
    146             free(pImageCodecInfo);  
    147             return TRUE;  
    148         }  
    149     }  
    150     free(pImageCodecInfo);  
    151     return FALSE;  
    152 }  
    153 
    154 void CCreateGIFDlg::OnFileSave()  
    155 {   
    156     Graphics graphics(GetDC()->m_hDC);
    157   
    158     Image image(ToWChar(m_sOpenPath.GetBuffer(m_sOpenPath.GetLength())));       
    159 
    160     CLSID clsid;  
    161 
    162     if(GetImageCLSID(L"image/bmp", &clsid))  
    163     {  
    164         image.Save(ToWChar(m_sBMPSavePath.GetBuffer(m_sBMPSavePath.GetLength())), &clsid, NULL);   
    165     }   
    166 }

      在GIF生成函数OnCreateGIF()中使用了while循环机制,图像选择文件对话框会一直跳出,即用户可以不断地添加图像。当点击取消时终止图像添加过程,对话框自动关闭,动画——GIF文件自动生成并保存。

      整个流程大致就是如此,对图像处理方面的要求比较高一点。

  • 相关阅读:
    css 基础
    css 基础-1
    html 入门2-表
    CMDB (后台管理) CURD 插件
    序列化
    AES(高级加密)
    API验证
    数据库取时间(分组)
    用户权限 (知识点)
    xss 过滤
  • 原文地址:https://www.cnblogs.com/tgyf/p/3849340.html
Copyright © 2011-2022 走看看