实现QQ表情功能(1)

                                                 作者:小鹏

 

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>,
如图:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值