现在的位置: 首页 > 综合 > 正文

实现QQ表情功能(1)

2013年10月05日 ⁄ 综合 ⁄ 共 4788字 ⁄ 字号 评论关闭

                                                 作者:小鹏

 

0、前言
相信大家对QQ的表情不陌生。像这样:

QQ的文本框控件支持各种图片格式的显示和插入。看到QQ这个有趣功能,我也想实现下。不过,实现起来却不是太容易,在我写代码的过程中,我也在网上查找了很久的资料,不过总算让我成功实现了。
要实现这个功能,关键的技术是要实现一个控件,这个控件能够播放GIF。
下面开始详细介绍我的写代码的过程。

1、观察QQ的实现方式
先从QQ是怎么实现的讲起。
QQ的这个控件叫ImageOle.dll.在QQ的安装目录下可以找到这个dll.
用regsvr32这个命令可以注册这个dll.


注册成功后可以用Ole View这个功能查看注册的相关信息。这个工具是VC自带的。


名字叫GifAnimator Class的就是QQ的那个控件。
选择GifAnimator,再右键,选择View Type Information.
可以看到ImageOle控件的类型库的内容。


这些内容就是QQ那控件的接口的方法。
我的目标就是实现一个同样的控件,支持其中一个方法,LoadFromFile.

2、创建一个ATL工程

下面就是我的具体实现的过程。
第一步,新建一个ATL工程。输入名称GifOle


下一步,保留默认选项,Server Type的类型是Dynamic Link Library(DLL).点击Finish.


3、添加一个控件

右键,选择New ATL Object.


在Category中选择Controls,在Objects中选择Full Control。


选择next.


在Short Name中输入CifCtl.
Miscellaneous中,把View Status中Opaque的选择去掉。Opaque是不透明的意思。QQ表情是在透明背景下显示的,所以要把那个选项去掉。


点击确定。

4、加入GDI+类库

下面实现播放Gif的功能。我用的是Gdi+。
在VC6要使用Gdi+的话,得下个库。
GDI+ for VC6.0 SDK下载

地址:
http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip

下载回来后,把里面的头文件和GdiPlus.lib复制到gdiplus的文件夹,再把这个文件夹复制到GifOle工程目录下。
在stdafx.h文件中加入以下代码
#define UNICODE
#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include "gdiplus//GdiPlus.h"
using namespace Gdiplus;
#pragma comment(lib,

"gdiplus//gdiplus.lib")
效果如下图:

5、加入ImageEx类

另外,我还有用到一个ImageEx类来显示gif.

这个类的头文件是:
// ImageEx.h: interface for the ImageEx class.
//
//////////////////////////////////////////////////////////////////////

#if

!defined(AFX_IMAGEEX_H__D639CC73_8200_42E0_9386_617AD4B0A2E9__INCLUDED_)
#define

AFX_IMAGEEX_H__D639CC73_8200_42E0_9386_617AD4B0A2E9__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class ImageEx : public Image
{
public:
ImageEx(const

WCHAR* filename, BOOL useEmbeddedColorManagement = FALSE);
~ImageEx();
public:
BOOL

IsAnimatedGif();    //判断是否是动态GIF文件
   long

GetFrameTime();     //获取当前帧应该显示的时间长度
void

ActiveNextFrame();  //激活下一帧为应该显示的帧
protected:
UINT  m_nFrameCount;     //帧数
UINT  m_nFramePosition;  //当前帧的序号
   PropertyItem*

m_pPropertyItem;   //属性项,仅用来测试是否是动态图片
};

#endif // !defined(AFX_IMAGEEX_H__D639CC73_8200_42E0_9386_617AD4B0A2E9__INCLUDED_)
把以上内容复制到ImageEx.h文件中,入到工程目录下。
实现文件的内容是:
// ImageEx.cpp: implementation of the ImageEx class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ImageEx.h"

ImageEx::ImageEx(const WCHAR* FileName, BOOL

useEmbeddedColorManagement)
:Image(FileName, useEmbeddedColorManagement)
{
m_nFramePosition =

0;
m_nFrameCount = 0;
m_pPropertyItem =

NULL;

UINT count = 0;
count =

GetFrameDimensionsCount();      //获得维数
GUID* pDimensionIDs

= new GUID[count];  //分配维ID数组

GetFrameDimensionsList(pDimensionIDs,

count);//获得各维的ID
m_nFrameCount =

GetFrameCount(&pDimensionIDs[0]);//获得第一维的帧数

   //获得属性项
int nSize =

GetPropertyItemSize(PropertyTagFrameDelay);
m_pPropertyItem =

(PropertyItem*) malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay,

nSize, m_pPropertyItem);  
delete pDimensionIDs;    
}

ImageEx::~ImageEx()
{
free(m_pPropertyItem);
m_pPropertyItem =

NULL;
}

BOOL ImageEx::IsAnimatedGif()
{
return m_nFrameCount

> 1;
}

long ImageEx::GetFrameTime()
{
long lPause =

((long*) m_pPropertyItem->value)[m_nFramePosition] * 10;
return lPause;
}

void ImageEx::ActiveNextFrame()
{
if (IsAnimatedGif()

== FALSE)
    return;

GUID  pageGuid = FrameDimensionTime;
SelectActiveFrame(&pageGuid,

m_nFramePosition++);

if

(m_nFramePosition == m_nFrameCount)
    m_nFramePosition

= 0;
}
把以上内容复制到ImageEx.cpp文件中,复制到项目工程目录下。
再把这两个文件添加到工程。再在GifCtl.h的文件的开头加下以下语句:
#include "ImageEx.h"
效果如下图:

6、加入定时器类

另外,为了让gif表情动起来,还需要一个定时器类来定时刷新,实现动的效果。
// 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();
    return 0;
}

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();
    return 0;
}

// Attributes
public:
DWORD

m_dwTimerInterval;

// Implementation
private:
HANDLE m_hThread;
DWORD m_dwThreadID;
LPSTREAM m_pStream;
CComPtr<T>

m_spT;
BOOL m_bTimerOn;
};
把以上代码同样复制到#include "ImageEx.h"语句的下面
效果如下图:

再把这个定时器加入到CGifCtl控件中
在CGifCtl类开头加入以下代码
public CTimer<CGifCtl, IGifCtl, &IID_IGifCtl>,
如图:

抱歉!评论已关闭.