http://www.cnblogs.com/tony-law/archive/2011/09/16/2178944.html
要做一个类似QQ表情的东西,用BCB6.0来做,本来这个工具也不是熟悉,用得多还是VC吧,难!
上网查了一下,因为要播放GIF文件,那个在优快云说的几乎都是说用QQ的ImageOle来做,然后就是一大堆的链接,说哪里哪里有的,怎样引用法
但是版权啊
而且全都是05年的帖子,奇怪了,难道是百度的错,还是现在没有人做这个了
无耐,继续百度 google,
找到了一位哥们做的
类似 MSN 信息发送框的制作(上)
http://www.vckbase.com/document/viewdoc/?id=1087
可惜啊,不能显示GIF 而且那个表情的选择面板也不太对劲,都是用Button来做的,图片多了那不是慢得要死,这种方法不可取吧
决定学习如何做一个ATL的com组件,自己实现一下这个组件吧
这时又找到了这篇文章http://blog.youkuaiyun.com/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(),但还是出现了这种情况,无耐啊
网上又有人说了,这个可能用定时器来做的话会好一点,
这位仁兄的,不知道是原创还是转载,反正就标着一个原字
http://blog.youkuaiyun.com/gxulg/article/details/299115
还有这位先生
http://blog.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/moxiaopeng/article/details/4211839
但经我实践,有错,是在运行时插入成功,但双击那张GIF图片的时候,程序崩溃,但基本上也是按照上面的方法,用定时器去做的,总言之感谢分享!
那些高手都是滴水不漏的啊!
还有最后的QQ表情选择面板
博客园里面也有位同学实现 了,还真不错,感谢了
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不是个好东西!!!
第一次写博客,还给博客吓了一跳,以为保存到草稿箱的找不出来的呢,哈哈!