您的位置:首页 > 其它

向Richedit插入动态Gif的实现(关于QQ表情功能的制作)

2012-09-18 18:14 537 查看
http://www.cnblogs.com/tony-law/archive/2011/09/16/2178944.html

要做一个类似QQ表情的东西,用BCB6.0来做,本来这个工具也不是熟悉,用得多还是VC吧,难!
上网查了一下,因为要播放GIF文件,那个在CSDN说的几乎都是说用QQ的ImageOle来做,然后就是一大堆的链接,说哪里哪里有的,怎样引用法
但是版权啊
而且全都是05年的帖子,奇怪了,难道是百度的错,还是现在没有人做这个了
无耐,继续百度 google,
找到了一位哥们做的
类似 MSN 信息发送框的制作(上)
http://www.vckbase.com/document/viewdoc/?id=1087
可惜啊,不能显示GIF 而且那个表情的选择面板也不太对劲,都是用Button来做的,图片多了那不是慢得要死,这种方法不可取吧
决定学习如何做一个ATL的com组件,自己实现一下这个组件吧
这时又找到了这篇文章http://blog.csdn.net/wheatfield/article/details/5833532
这篇文章说了很多关于ATL这些相关的内容知识
比如如何插入OLE对象
  



TCHAR Filter[]=_T("JGP文件(*.jpg)|*.jpg;*.jpeg|GIF文件(*.gif)|*.gif|BMP文件(*.bmp)|*.bmp|所有文件(*.*)|*.*||");
CFileDialog dlgOpen(TRUE,0,0,OFN_HIDEREADONLY|OFN_FILEMUSTEXIST,(LPCTSTR)Filter,NULL);
if(dlgOpen.DoModal()==IDOK)
{
IRichEditOle* lpRichEditOle = m_RichEdit.GetIRichEditOle(); //m_RichEdit为您的RichEdit对象

IStorage*  lpStorage  = NULL;//存储接口
IOleObject*  lpOleObject  = NULL;//OLE对象
LPLOCKBYTES  lpLockBytes  = NULL;//LOCKBYTE
IOleClientSite* lpOleClientSite = NULL;
CComPtr<IMaiYuanOleImage> IPic;
CLSID   clsid;
REOBJECT  reobject;
HRESULT   hr;

if(lpRichEditOle == NULL)
return;
//创建IMaiYuanOleImage对象并获取接口
hr = ::CoCreateInstance(CLSID_MYOleImage,NULL,CLSCTX_INPROC,IID_IMaiYuanOleImage,(LPVOID*)&IPic);
if( IPic == NULL )
{
return;
}

BOOL bRet = TRUE;

try{
hr = IPic->QueryInterface(IID_IOleObject, (void**)&lpOleObject);//获得数据对象接口
if( hr != S_OK )
AfxThrowOleException(hr);
hr = lpOleObject->GetUserClassID(&clsid);
if ( hr != S_OK)
AfxThrowOleException(hr);

hr = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);//创建LOCKBYTE对象
if (hr != S_OK)
AfxThrowOleException(hr);
ASSERT(lpLockBytes != NULL);

hr = ::StgCreateDocfileOnILockBytes(lpLockBytes,//创建复合文档
STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &lpStorage);
if (hr != S_OK)
{
VERIFY(lpLockBytes->Release() ==0);
lpLockBytes = NULL;
AfxThrowOleException(hr);
}

lpRichEditOle->GetClientSite(&lpOleClientSite);

ZeroMemory(&reobject, sizeof(REOBJECT));//初始化一个对象
reobject.cbStruct =sizeof(REOBJECT);
reobject.clsid  = clsid;
reobject.cp   = REO_CP_SELECTION;
reobject.dvaspect = DVASPECT_CONTENT;
reobject.dwFlags = REO_BELOWBASELINE;
reobject.poleobj = lpOleObject;
reobject.polesite = lpOleClientSite;
reobject.pstg  = lpStorage;

lpOleObject->SetClientSite(lpOleClientSite);//
hr = lpRichEditOle->InsertObject( &reobject );
if (hr != S_OK)
AfxThrowOleException(hr);
OleSetContainedObject(lpOleObject,TRUE);
IPic->Load(_bstr_t(dlgOpen.GetPathName())); //装载要显示的图像
}

catch( COleException* e )
{
TRACE(_T("OleException code:%d"),e->m_sc);
e->Delete();
bRet = FALSE;
}

// release the interface
//if( IPic     != NULL )  IPic->Release();
if( lpOleObject  != NULL )  lpOleObject->Release();
if( lpOleClientSite != NULL ) lpOleClientSite->Release();
if( lpStorage  != NULL ) lpStorage->Release();

lpRichEditOle->Release();
}




还有个ATL实现播放GIF的源代码
看了,还明白了如何去做了,但他的实现方式有问题,用线程去做的话,播放几次那个CPU就无限地增大,卡死机
他是用pictureEx那个类来做的,本以为是ipicture这个东西的问题,后来改成了GDI+,image->draw(),但还是出现了这种情况,无耐啊
网上又有人说了,这个可能用定时器来做的话会好一点,
这位仁兄的,不知道是原创还是转载,反正就标着一个原字
ATL 开发ActiveX控件之定时器使用
http://blog.csdn.net/gxulg/article/details/299115
还有这位先生
ATL 开发ActiveX控件之定时器使用(改进,含源码)
http://blog.csdn.net/willnow/article/details/6456106
这个定时器类



// CTimer
template <class Derived, class T, const IID* piid>
class CTimer
{
public:

CTimer()
{
m_bTimerOn = FALSE;
}

HRESULT TimerOn(DWORD dwTimerInterval)
{
Derived* pDerived = ((Derived*)this);

m_dwTimerInterval = dwTimerInterval;
if (m_bTimerOn) // already on, just change interval
return S_OK;

m_bTimerOn = TRUE;
m_dwTimerInterval = dwTimerInterval;
m_pStream = NULL;

HRESULT hRes;

hRes = CoMarshalInterThreadInterfaceInStream(*piid, (T*)pDerived, &m_pStream);

// Create thread and pass the thread proc the this ptr
m_hThread = CreateThread(NULL, 0, &_Apartment, (void*)this, 0, &m_dwThreadID);

return S_OK;
}

void TimerOff()
{
if (m_bTimerOn)
{
m_bTimerOn = FALSE;
AtlWaitWithMessageLoop(m_hThread);
}
}

// Implementation
private:
static DWORD WINAPI _Apartment(void* pv)
{
CTimer<Derived, T, piid>* pThis = (CTimer<Derived, T, piid>*) pv;
pThis->Apartment();
return0;
}

DWORD Apartment()
{
CoInitialize(NULL);
HRESULT hRes;

m_spT.Release();

if (m_pStream)
hRes = CoGetInterfaceAndReleaseStream(m_pStream, *piid, (void**)&m_spT);

while(m_bTimerOn)
{
Sleep(m_dwTimerInterval);
if (!m_bTimerOn)
break;

m_spT->_OnTimer();
}
m_spT.Release();

CoUninitialize();
return0;
}

// Attributes
public:
DWORD m_dwTimerInterval;

// Implementation
private:
HANDLE m_hThread;
DWORD m_dwThreadID;
LPSTREAM m_pStream;
CComPtr<T> m_spT;
BOOL m_bTimerOn;
};




 折腾得够惨了,哈哈
无意中也发现了这位兄弟的博客,找不到了地址了
但下载了他的源码,而且还做了一个讲解的PDF,够用心的了,附上他的总结
/*****************************************************
实现 QQ表情这个功能的资料比较缺乏,我也在网上找了比较久,根据网上找到的一些

相关资料,一点点尝试实现的。 

以上所述只是实现 QQ的表情功能的第一步,更深入的内容我也在尝试中,欢迎有兴趣

的网友跟我一起探讨。 

项目工程代码我放在 QQ 群:44177419,70934898,37950043 的群共享上面。需要代码

的可以去那下载,也可以直接发邮件来索取。代码有错误的地方,欢迎发邮件指正。 

我的联系方式: 

QQ:xpmo@qq.com

MSN:msn@msn.com

邮箱:xpmo@qq.com

欢迎转载,不过希望转载时注明出处。 

 *****************************************************/
他的心声跟我一样,资料少啊
另外还有一个介绍 用VS2008+ATL开发Gif的ActiveX控件的步骤
http://blog.csdn.net/crybird/article/details/4049047
/****************************************************************
用VS2008+ATL开发显示Gif的ActiveX控件
base:MSDN:开发语言-VS文档-VC++-参考信息-库参考-ATL-Concepts-ATL Tutorial

1   创建解决方案

创建一个空的解决方案,名称GifSolution。将来包含控件项目和测试项目。

2   创建ATL项目

解决方案视图-右击解决方案-添加-新建项目,弹出的对话框中选ATL项目,名称为GifAnimate确定。弹出的对话框中选择DLL服务器(可选允许合并代理存根,这样不会产生代理dll),完成后编译,这样工程框架就完成了。

3   添加新组件

3.1 一点要说明的

组件分有窗口的和无窗口的,这里应该用有窗口的,所以添加组件的时候,外观不要基于none,最好基于CStatic。因为gif是动态的,应该用另一个线程绘制,这样不影响主线程响应消息,绘图更加流畅。
两种窗口的区别:无窗口的控件用父窗口做自己的窗口;用父窗口的DC绘图;通过DEFAULT_REFLECTION_HANDLER()把消息反馈给父窗口以进行响应。有窗口的控件,多一个CContainedWindow m_ctlStatic变量作为显示窗口;在BEGIN_MSG_MAP / END_MSG_MAP之间,通过ALT_MSG_MAP(1)宏映射消息处理(1为控件m_ctlStatic构造时候的内部标记,因为可能有多个窗口,默认1个,用户可以自己添加)。

3.2 添加组件对象

为GifAnimate项目添加类-选择ATL控件,点击添加,弹出对话框。
第一卡:组件名称GifAniControl
第二卡:支持连接点(因为要通知客户端)
第三卡:默认接口
第四卡:外观基于Static,可插入(在注册表注册为OLE插入对象,可以插入Office)
第五卡:实现固有属性-背景色、边框色、边框可见性
确定后,编译通过。

3.3 为组件添加方法

类视图-右键点击控件接口-添加-方法,分别添加Play Stop Pause Continue PrevFrame NextFrame函数,以操作gif图片。添加IsPlaying函数,添加LoadGifFromFile
LoadGifFromStream函数

3.4 为组件添加属性

注意:在idl文件里定义一个枚举,指明播放方向。
 
类视图-右键点击控件接口-添加-属性,分别添加Loop(循环次数) DelayTimeFactor(时间因子) PlayDirection(播放方向)等属性。
 
对于每一个属性,控件类增加了一对函数,去读写属性get/put_XXX,真正的变量要我们自己写的。

4   实现绘图

添加GifImage类,用于实现绘图功能。
在控件类里,添加一个GifImage类型的变量,主要用于对图片的控制。
完成组件接口函数和属性的调用。
把窗口的句柄给GifImage,让它实现绘图。
(对于无窗口控件,修改组件的OnDraw函数,实现绘图)
编译通过就可以测试了(由于用到了GDI+,用ActiveX控件测试容器无法调用函数)。

5   添加测试项目

右键点击解决方案-添加-新建项目,弹出的对话框中选MFC EXE项目,名称GifTestDlg。创建一个基于对话框的项目。
导入DLL:#import "../GifAnimate/Debug/GifAnimate.dll"
初始化COM和GDI+
在对话框初始化的时候创建控件,并设置属性等。
编译运行成功。

6   添加事件

第二步已经支持连接点,idl文件里会有事件接口

6.1 为事件接口添加函数(声明)

类视图-右键点击lib接口里的事件接口-添加-方法,添加一个Click函数,参数为long型的x,y坐标。产生的效果是,在idl文件里,向导为事件接口添加了一个点击函数。

6.2 生成代理类函数

生成一个代理类,实现函数。[Only in VC6, VS2008自动完成]
类视图-右键点击控件类-添加-添加连接点,在对话框选中以上提到的连接点,确定。
代理类会包装一个函数Fire_Click,触发客户端。

6.3 触发事件(调用函数)

响应控件类的WM_LBUTTONDOWN消息,调用代理类的Fire_Click函数。

6.4 客户端关注事件

客户端用ATL或者MFC完成一个接收器GifSink,连接、工作、断开。

7   添加属性页

完全可以不做而通过程序设置属性,做这个是给懒程序员用的,或者只是让组件使用更方便。

7.1 添加属性页卡片

添加-类-ATL属性页-确定,弹出对话框中添加简称,修改组件属性和标题,确定。
系统生成相关的对话框卡片资源、对应对卡片类、idl相关内容、注册脚本等。卡片类有一个Apply函数,用于接受属性设置。

7.2 设置属性

修改对话框,增加新控件等等。然后处理数据变化,例如:处理Edit的EN_CHANGE事件,设置page组件的脏数据标记(修改但没有确定)。最好并完成Apply函数。

7.3 手工把属性页添加到控件

在控件类.h添加如下代码
PROP_ENTRY("Sides", 1, CLSID_GifProp)

8   说明

1.  本文只是举例说明开发过程,抛砖而已。
2.  样例只是框架,不能直接使用,所有代码没有经过测试
3.  样例接收器GifSink和属性页卡片没有完成,懒了,呵呵
4.  样例背景等很多属性没有实现
5.  使用了GDI+,但GDI+的Image类的SetActiveFrame函数有问题,网上貌似没有解决办法。Debug版本不影响使用,Release版本没有测试。如果有兴趣,可以使用IPicture接口,网上很多。
6.  本文和本样例版权crybird所有,引用处请标记crybird.
*****************************************************************************************************
 
说着说着,又找到那位兄弟的博客网址了附上
实现QQ表情功能(1)(2) http://blog.csdn.net/moxiaopeng/article/details/4211839
但经我实践,有错,是在运行时插入成功,但双击那张GIF图片的时候,程序崩溃,但基本上也是按照上面的方法,用定时器去做的,总言之感谢分享!
那些高手都是滴水不漏的啊!
还有最后的QQ表情选择面板
博客园里面也有位同学实现 了,还真不错,感谢了
QQ表情选择面板的VC实现
http://www.cnblogs.com/hoodlum1980/archive/2010/01/09/1642732.html
 
他是用cximage这个库来实现 的,哎呀,BCB里面也可以用,但我怎样试都是链接错误,NND,人品太差了
不过也太大了吧,不爽,应该用GDI+,因为网上也说了BCB里面可以用GDI+
但....
我就是用不了,很多方法了,csdn的老妖也有个说明,如何如何用的
 也给贴上吧
用GDI+实现半透明渐变的特效窗口
http://www.ccrun.com/article.asp?i=643&d=n5u8o4
/**************************************************************************************
方法:在C++Builder中使用GDI+的方法和代码网上遍地都是,这里为了完整性,简单说说流程:
1.) 在BCB6中已自带了ghiplus.h文件,故只需要生成gdiplus.lib文件就可以:

    在命令行下运行implib gdiplus.lib gdiplus.dll。(如果ghiplus.dll不在当前文件夹下,注意写完整路径)

2.) 在工程的编译选项中加入STRICT条件编译:

    Project-->Options-->Directories/Conditionals-->Condtionals-->点击旁边的"..."按钮-->输入STRICT,然后Add。

3.) 在工程中加入Gdiplus.lib:

    Project-->Add To Project-->找到Gdiplus.lib添加进来。

4.) 在工程的.h文件中包含所需的头文件,注意先后顺序:

    #include "math.hpp"

    #include <algorithm>

    using std::min;

    using std::max;

    #include "gdiplus.h"

    using namespace Gdiplus;
 ****************************************************************************/
也按上面的方法做了,也可以在image对象调用draw方法了
但在设置显示帧的时候
 



UINT nCount = pImg->GetFrameDimensionsCount();
GUID* pDimensionIDs =new GUID[nCount];
pImg->GetFrameDimensionsList(pDimensionIDs, nCount);
m_nTotalFrame = pImg->GetFrameCount(&pDimensionIDs[0]);
//PropertyTagFrameDelay是GDI+中预定义的一个GIG属性ID值,表示标签帧数据的延迟时间
UINT nSize = pImg->GetPropertyItemSize(PropertyTagFrameDelay);
m_pPropertyItem = (PropertyItem*)malloc(nSize);
pImg->GetPropertyItem(PropertyTagFrameDelay,nSize,m_pPropertyItem);




[Linker Error] Unresolved external '_FrameDimensionTime' referenced from
 
这个你也太欺负人了吧,不是坑爹吗?!
 
我又改成pictureEx那个类的显示GIF,终于是做好了,上个图


 
还有很多东西要做啊
 
哈哈,利用BCB编译的时间来写这个,TMD BCB你也编译得太快了吧
 
总论BCB不是个好东西!!!
 
第一次写博客,还给博客吓了一跳,以为保存到草稿箱的找不出来的呢,哈哈!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  qq null 测试 linker winapi mfc