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

来源:互联网 发布:分析仪软件下载 编辑:程序博客网 时间:2024/05/04 23:37

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

这个定时器类

复制代码
// CTimertemplate <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 intervalreturn 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);        }    }            // Implementationprivate:    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;    }        // Attributespublic:    DWORD m_dwTimerInterval;        // Implementationprivate:    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不是个好东西!!!

 

第一次写博客,还给博客吓了一跳,以为保存到草稿箱的找不出来的呢,哈哈!