none
图片的合并与分解 RRS feed

  • 常规讨论

  • 作者:Oshj

    下载源代码

    摘要:本文及其附带源码利用GID+ Bitmap实现了将一张图片分解为多张指定尺寸的图片,以及将多张图片合并为一张。

    关键字:GDI+、图片合并、分解

    环境:VS2005/WinXP/SP2/1280*800分辨率

      事情的起因是这样的:我的一个VC6工程 res 路径下存在一些图片,它们是在绘制 ToolBar的时候生成的,基本是多个16*16或32*32的小图片拼凑而成的一张大图片,我需要用到其中的某几个小图片,单独存为BMP或者ICO,但是找了几个做图标的软件,都没有这个功能。问了坛子里的一些朋友,好像也没有这种简单的现成的工具,于是一咬牙一跺脚,自己写吧。顺便把多个图片合并也写进去了,目前只是测试了自己需要的部分(仅BMP格式),和源码一起放出来,希望对你有用,不够用自己去改哈。
    程序编译环境:VS2005 VC++ MFC UNICODE

      在程序的实现过程中,没有去分析 BMP 的结构,而是直接调用了GDI+ Bitmap,我只是想使用,不想太多的深入。所以代码阅读起来很简单,核心代码就那么2~3行。

    这里我假设读者是第一次使用GDI+ 来进行逐步说明。如果你在这方面是姚明的手――高手@_@,请忽略。

    1、包含GDI+ 的头文件和库

    我们需要包含 GDI+ 头文件,包含 lib,使用 Gdiplus 的别名。为了省事,我放到了 stdafx.h 里。

    //请在主框架里GdiplusStartup / GdiplusShutdown
    #include 	//GDI+ 声明,可以GDI/GDI+ 混合使用
    #pragma comment(lib, "gdiplus.lib")
    using namespace Gdiplus;
    

      你也可以在 工程属性->链接器->输入->附加依赖项 里包含 gdiplus.lib 文件,也可以不使用别名,那么定义GDI+ 对象的时候,就要带上域名,如 Gdiplus::Bitmap, 这里建议还是 using namespace.

    2、初始化和释放GDI+ 调用

      首先定义一个ULONG_PTR的成员变量 m_gdiplusToken ,这是一个DWORD数据类型,该成员变量用来保存GDI+ 被初始化后在应用程序中的GDI+ 标识,以便能在应用程序退出后,引用该标识来调用Gdiplus:: GdiplusShutdown来关闭GDI+。细节请参考CBmp2MApp里的相关代码。

    ULONG_PTR m_gdiplusToken; //GDI+
    

    然后在 BOOL CBmp2MApp::InitInstance() 里开始初始化 GDI+ 环境。

    //初始化GID+
    GdiplusStartupInput gdiplusStartupInput;	
    GdiplusStartup( &m_gdiplusToken,&gdiplusStartupInput,NULL );
    

      最后,在int CBmp2MApp::ExitInstance()里,我们来释放GDI+ 的调用。注意:在向导生成的缺省项目中,这个函数是没有的,我们需要重载它。在类视图里选择CBmp2MApp,然后在属性里点重写,就可以看到。(是不是太罗嗦了^_^)

    int CBmp2MApp::ExitInstance() 
    {
    	//释放GDI+ 调用
    	GdiplusShutdown( m_gdiplusToken );
    
    	return CWinApp::ExitInstance();
    }
    

    3、图片的分解

      分解一张图为多张,这是程序的缺省模式。如源码中的注释,我只考虑了横向分解,没有考虑纵向的情况,也没有考虑智能的获取图片的尺寸问题,缺省就是16*16,如果你的图片是32*32或其他的模式,请手动更改。但你点击选择文件,成功选择一个合适的图片文件之后,界面应该是下图所示的样子。如果不想改变拆分图片的存储路径,你可以直接点确定了,不出意外的话,会打开一个文件夹界面,然后里边有N张存储成功可供使用的图片了。

    核心函数就是下面的这个:

    //分解,目前仅考虑 BMP 格式
    void CBmp2MDlg::ImageUnPack(LPCTSTR sPath,LPCTSTR sType,int x,int y)
    {
    	if( NULL==m_bitmap ) return; //must!
    	//
    	Bitmap *bmp = m_bitmap;
    	int w = bmp->GetWidth();
    	int h = bmp->GetHeight();	
    	CLSID clsid;
    	GetEncoderClsid( sType,&clsid );
    
    	CString ss;
    	int i,k=1;
    	for( i=0;i<w;i+=x,k++ ) //只考虑横向
    	{
    		//新建指定大小的图片
    		Bitmap bt(x,y);
    		//从它得到绘制设备
    		Graphics *gc = Graphics::FromImage( &bt );
    		//从原图片上截取指定大小的图片贴到这个新建的图片的上面
    		gc->DrawImage( bmp,RectF(0,0,x,y),i,0,x,y,UnitPixel ); 
    		//
    		ss.Format( _T("%s%s_%d.%s"),sPath,m_sFileTitle,k,GetType(sType) );
    		//保存到指定路径
    		bt.Save( ss,&clsid,NULL );
    		//
    		delete gc;
    		gc = NULL;
    	}
    
    	ShellExecute( NULL,_T("open"),sPath,NULL,NULL,SW_SHOW );
    }
    

    就是按照输入的大小先创建一张空图片,然后计算位置,从源图片上的截取这个位置上的图贴过来,然后保存。

    4、图片的合并

    合并多张图为一张,就是上面的逆过程了。核心函数就是 ImagePack 请去参考源码。

    就是这么个小工具,希望在你想用的时候能节省一点时间。你可以任意去扩充它,比如:增加图片预览的功能,保存的命名规则,或者拖曳 ListCtrl 让各图片按你预想的顺序排到一张大图上……等等。

     

    2009年6月1日 2:42