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"中添加以下代码,也可以在其他头文件中添加:
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
2、在"CreateGIF.h"中添加成员变量,GDI初始化时用:
private:
GdiplusStartupInput m_GdiplusStartupInput;
ULONG_PTR m_pGdiToken;
3、重载父类虚函数,用以结束GDI:
virtual int ExitInstance();
4、在"CreateGIF.cpp"源文件的初始化函数InitInstance()中添加GDI初始化语句,注意该语句必须放在对话框生成语句之前,否则在对话框中操作时会因为GDI未初始化而出错。
GdiplusStartup(&m_pGdiToken,&m_GdiplusStartupInput,NULL);
5、在"CreateGIF.cpp"中添加虚函数ExitInstance()的定义:
int CCreateGIFApp::ExitInstance(){
GdiplusShutdown(m_pGdiToken);
return CWinApp::ExitInstance();
}
到此,GDI的初始化工作算是完成,可以直接使用其库中的资源了——图像类及处理功能,如Image。
由于位图(BMP)是比较标准的图像格式,将其数据写入GIF文件中,不是难事,以下是实现过程:
(变量m_sSavePath为事先指定的完整保存路径,类型CString)
CFileDialog dlg(TRUE,"BMP",NULL,,"图像文件(*.bmp)|*.bmp||",this); if(dlg.DoModal() != IDOK)
{
return;
} HBITMAP hBmp = (HBITMAP)LoadImage(NULL,dlg.GetPathName(),IMAGE_BITMAP,,,LR_LOADFROMFILE|LR_CREATEDIBSECTION); if(hBmp == NULL)
{
return;
} BYTE *palette = NULL;
BYTE *pData = NULL;
int nWidth,nHeight;
BYTE bitsPixel = ; if(GetData(hBmp,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
{
DeleteObject(hBmp);
return ;
} DeleteObject(hBmp); CFile file;
if(file.Open(m_sSavePath,CFile::typeBinary|CFile::modeCreate|CFile::modeWrite) == FALSE)
{
return;
} CreateGIFHeard(file,nWidth,nHeight,bitsPixel); short int nTransparentColorIndex = -; AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
delete []pData;
delete []palette;
上述代码实现的功能是:从文件打开对话框中选择一幅"bmp"格式的图像,读取其数据信息,打开"gif"文件创建头信息,将"bmp"图像数据写入到"gif"文件中。
可以看到结尾处并没有关闭"gif"打开的文件,所以要想再添加一幅或若干幅"bmp"图像只需重复上述过程。完成之后,添加以下代码结束文件的写操作。GIF文件就生成并保存在了指定路径中。
CloseGIF(file);
file.Close();
那接下的问题就是:如何将其他格式的图像数据读取并写入到"gif"文件,参与动画制作的大家庭。
最初在网上找了一篇文章,说可以直接通过文件名将"jpg"等格式图像读取成Bitmap对象,进一步提取出HBITMAP信息,然后就可以同上述过程进行添加了。代码大致如下:
Bitmap tempBmp(FileName.AllocSysString());
Color backColor;
HBITMAP HBitmap;
tempBmp.GetHBITMAP(backColor,&HBitmap);
return HBitmap;
但是这样做总是不行,个人估计是"Bitmap tempBmp(FileName.AllocSysString());"这个地方并不能真正地将其他类型的图像转为"bmp"。
后来又找到了一种方法,通过上面提到过的Image类,先将图像读取进来,然后保存为"bmp"格式。当然,读取的时候也可以读取"bmp"类型。指定源图像文件及目标图像文件的路径之后,便可以实现方便、快捷的隐式转换。
Graphics graphics(GetDC()->m_hDC); Image image(ToWChar(m_sOpenPath.GetBuffer(m_sOpenPath.GetLength()))); CLSID clsid; if(GetImageCLSID(L"image/bmp", &clsid))
{
image.Save(ToWChar(m_sBMPSavePath.GetBuffer(m_sBMPSavePath.GetLength())), &clsid, NULL);
}
注意,上面用到的两个函数:ToWCchar()与GetImageCLSID()并不是自带的,而是要自己实现。
WCHAR* CCreateGIFDlg::ToWChar(char *str)
{
//在 GDI+中,有关字符的参数类型全部都是 WCHAR 类型的
//该函数是将传统字符串进行转换
const int nnn = ;
static WCHAR buffer[nnn];
wcsset(buffer,);
MultiByteToWideChar(CP_ACP,,str,strlen(str),buffer,nnn);
return buffer;
} int CCreateGIFDlg::GetImageCLSID(const WCHAR *format, CLSID *pCLSID)
{
UINT num=;
UINT size=; ImageCodecInfo* pImageCodecInfo=NULL; GetImageEncodersSize(&num,&size); if(size==)
return FALSE; // 编码信息不可用
// 分配内存 pImageCodecInfo=(ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo==NULL)
return FALSE; // 分配失败
// 获得系统中可用的编码方式的所有信息
GetImageEncoders(num,size,pImageCodecInfo);
// 在可用编码信息中查找 format 格式是否被支持 for(UINT i=;i<num;++i)
{
//MimeType: 编码方式的具体描述
if (wcscmp(pImageCodecInfo[ i] .MimeType,format)==)
{
*pCLSID=pImageCodecInfo[i].Clsid;
free(pImageCodecInfo);
return TRUE;
}
}
free(pImageCodecInfo);
return FALSE;
}
执行之后,就可以看见转换好的"bmp"图像了。
离真相不远了,接下来需要做的就是将上述的生成"gif"与"bmp"这两个过程合并。具体如下:
void CCreateGIFDlg::OnCreateGIF()
{
// TODO: Add extra validation here
if(!UpdateData())
{
return;
} CFileDialog dlg(TRUE,"(*.*)|*.*",NULL,,"图像文件 (*.*)|*.*||",this); if(dlg.DoModal() != IDOK)
{
return;
} m_sOpenPath = dlg.GetPathName();
m_sBMPSavePath = dlg.GetFileTitle()+"1.bmp"; OnFileSave(); HBITMAP hBmp = (HBITMAP)LoadImage(NULL,m_sBMPSavePath,IMAGE_BITMAP,,,LR_LOADFROMFILE|LR_CREATEDIBSECTION); if(hBmp == NULL)
{
return;
} BYTE *palette = NULL;
BYTE *pData = NULL;
int nWidth,nHeight;
BYTE bitsPixel = ; //为位图生成调色板,得到索引数据、宽、高
if(GetData(hBmp,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
{
DeleteObject(hBmp);
return ;
} DeleteObject(hBmp); //创建GIF文件
CFile file;
if(file.Open(m_sSavePath,CFile::typeBinary|CFile::modeCreate|CFile::modeWrite) == FALSE)
{
return;
} //写GIF头
CreateGIFHeard(file,nWidth,nHeight,bitsPixel); short int nTransparentColorIndex = -; //加入第一幅图片
AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
delete []pData; //这两个变量在此相当于二维数组
delete []palette; //////////////////////////////////////////////////////////////////////////////////
while()
{
CFileDialog dlg(TRUE,"(*.*)|*.*",NULL,,"图像文件 (*.*)|*.*||",this);
if(dlg.DoModal() != IDOK)
{
CDialog::OnOK();
return;
} m_sOpenPath = dlg.GetPathName();
m_sBMPSavePath = dlg.GetFileTitle()+"1.bmp"; OnFileSave(); HBITMAP hBmp2 = (HBITMAP)LoadImage(NULL,m_sBMPSavePath,IMAGE_BITMAP,,,LR_LOADFROMFILE|LR_CREATEDIBSECTION); if(hBmp2 == NULL)
{
CloseGIF(file);
file.Close();
return;
} if(GetData(hBmp2,&palette,&pData,&bitsPixel,&nWidth,&nHeight) == FALSE)
{
DeleteObject(hBmp2);
CloseGIF(file);
file.Close();
return ;
}
DeleteObject(hBmp2); nTransparentColorIndex = -; //加入其它图片
AddImageToGIF(file,pData,palette,nWidth,nHeight,bitsPixel,m_nDelay,nTransparentColorIndex);
delete []pData;
delete []palette;
} //结束GIF
CloseGIF(file); file.Close(); CDialog::OnOK();
} WCHAR* CCreateGIFDlg::ToWChar(char *str)
{
//在 GDI+中,有关字符的参数类型全部都是 WCHAR 类型的
//该函数是将传统字符串进行转换
const int nnn = ;
static WCHAR buffer[nnn];
wcsset(buffer,);
MultiByteToWideChar(CP_ACP,,str,strlen(str),buffer,nnn);
return buffer;
} int CCreateGIFDlg::GetImageCLSID(const WCHAR *format, CLSID *pCLSID)
{
UINT num=;
UINT size=; ImageCodecInfo* pImageCodecInfo=NULL; GetImageEncodersSize(&num,&size); if(size==)
return FALSE; // 编码信息不可用
// 分配内存 pImageCodecInfo=(ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo==NULL)
return FALSE; // 分配失败
// 获得系统中可用的编码方式的所有信息
GetImageEncoders(num,size,pImageCodecInfo);
// 在可用编码信息中查找 format 格式是否被支持 for(UINT i=;i<num;++i)
{
//MimeType: 编码方式的具体描述
if (wcscmp(pImageCodecInfo[ i] .MimeType,format)==)
{
*pCLSID=pImageCodecInfo[i].Clsid;
free(pImageCodecInfo);
return TRUE;
}
}
free(pImageCodecInfo);
return FALSE;
} void CCreateGIFDlg::OnFileSave()
{
Graphics graphics(GetDC()->m_hDC); Image image(ToWChar(m_sOpenPath.GetBuffer(m_sOpenPath.GetLength()))); CLSID clsid; if(GetImageCLSID(L"image/bmp", &clsid))
{
image.Save(ToWChar(m_sBMPSavePath.GetBuffer(m_sBMPSavePath.GetLength())), &clsid, NULL);
}
}
在GIF生成函数OnCreateGIF()中使用了while循环机制,图像选择文件对话框会一直跳出,即用户可以不断地添加图像。当点击取消时终止图像添加过程,对话框自动关闭,动画——GIF文件自动生成并保存。
整个流程大致就是如此,对图像处理方面的要求比较高一点。
VS2008+GDI实现多幅图像的GIF动画制作的更多相关文章
- CSS3动画制作
CSS3动画制作 rotate 绕中心旋转 fadeInPendingFadeOutUp 先渐现,停留2s,再向上滑动并逐渐消失 fadeInUp2D 向上滑动并渐现, 因Animate.css的fa ...
- 关于HTML5在动画制作工具Animatron的一些问题
Animatron是国外一款在线HTML5动画制作工具,网址:www.animatron.com 当然,想使用的话,是需要FQ的. 用animatron制作好的动画是可以下载为代码和GIF的,这时候付 ...
- MFC实现Gif动画制作工具
每天来博客园逛,看里面各种好文章,发现自己已经许久没有分享点什么了. 前几天用MFC设计了一个小型的Gif动画制作工具,思路如下: 1.支持图片格式:"*.jpg","* ...
- 分享2D Unity游戏的动画制作经验
作者:Alex Rose Unity近期宣布推出额外的2D游戏支持,加入了Box 2D物理和一个精灵管理器. 但这里还是有些技巧须要牢记在心.逐帧更改图像仅仅是动画制作的冰山一角,若要让你的游戏出色执 ...
- 手游[追忆之青]动画导演:2D动画制作技巧
转自:http://www.gamelook.com.cn/2016/09/264591 GameLook报道/由一般法人计算机娱乐协会(CESA)主办的CEDEC2016日前在日本横滨举行,诸多开发 ...
- unity中的动画制作方法
Unity中的动画制作方法 1.DOTween DoTween在5.0版本中已经用到了,到官网下载好插件之后,然后通过在项目中导入头using DG.Tweening;即可. 一些常用的API函数 D ...
- 纯干货!live2d动画制作简述以及踩坑
本文来自网易云社区,转载务必请注明出处. 1. 概述 live2d是由日本Cybernoids公司开发,通过扭曲像素位置营造伪3d空间感的二维动画软件.官网下载安装包直接安装可以得到两种软件,分别是C ...
- 2014 mathtype分块列向量输入 PPT动画制作
1.mathtype分块列向量的输入 http://zhidao.baidu.com/link?url=pV7TazWe-Ld5qgxNcJCQdRaA8ILEgmXRP211F5U0Cst0xNfU ...
- SVG动画制作工具 , 从此抛弃臃肿的gif
VG简介 只要是程序员的你,你不得不知道svg图片,它可以无限任意放大拉伸都不会损失画质,就像系统字体一样可以无限矢量放大,svg更高级是可以用来制作矢量动画,现在各大浏览器和系统基本对svg已经支持 ...
随机推荐
- nginx 负载均衡示例
一.nginx nginx是一个轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,以开源形式发布.nginx的性能稳定,功能丰富,配置简单,且占用系统资源低.可支持多个 ...
- 烂泥:KVM虚拟机随KVM服务器的启动而启动
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 要使KVM虚拟机随KVM服务器的启动而启动,我们所需要做的工作很少.只需要把KVM虚拟机的XML配置文件做一个软连接到/etc/libvirt/qemu ...
- Add Binary
Add Binary https://leetcode.com/problems/add-binary/ Given two binary strings, return their sum (als ...
- C++ 数组长度 以及 数组名作为参数传递给函数 以及 为什么不在子函数中求数组长度
在看排序,首先是插入排序,思路理清后想用代码实现,然后问题来了: 如何求数组长度? 如果没记错,在Java中应该是有直接可用的方法的, Python中(序列)也有.len,在C/C++中,字符串倒是有 ...
- TEZ安装试用
下载地址:http://pan.baidu.com/s/1ZNpyI 第一次使用maven编译 tez的时候到tez ui部分报错,google后发现有人遇到类似问题是因为maven版本的问题, 当时 ...
- Java 开发环境部署
1.下载Java开发环境工具包JDK,下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 下载后,双击jdk ...
- RCNN (Regions with CNN) 目标物检测 Fast RCNN的基础
Abstract: 贡献主要有两点1:可以将卷积神经网络应用region proposal的策略,自底下上训练可以用来定位目标物和图像分割 2:当标注数据是比较稀疏的时候,在有监督的数据集上训练之后到 ...
- Codeforces Round 261 Div.2 D Pashmak and Parmida's problem --树状数组
题意:给出数组A,定义f(l,r,x)为A[]的下标l到r之间,等于x的元素数.i和j符合f(1,i,a[i])>f(j,n,a[j]),求有多少对这样的(i,j). 解法:分别从左到右,由右到 ...
- POJ 3481Double Queue Splay
#include<stdio.h> #include<string.h> ; ],data[N],id[N],fa[N],size,root; void Rotate(int ...
- java 20 - 5 字节输出流写出数据的一些方法
首先回顾下 字节输出流操作步骤: A:创建字节输出流对象 B:调用write()方法 C:释放资源 创建字节流输出对象 FileOutputStream fos = new FileOutput ...