您的位置:首页 > 编程语言 > C语言/C++

C/C++实现显示GIF动态图片

2017-10-17 14:49 786 查看
语言:C/C++
编程软件:VS2015
字符集:UNICODE编码
说明:这里不使用MFC的任何类,全部为API


【1】实现步骤如下:

一、首先,去找一个GIF动态图片.

如:显示正在加载中(http://www.lanrentuku.com/gif/a/loading.html)

二、找一个在线GIF动态图片分解器(如:http://tool.chuanxincao.net/fenjie/)

分解GIF图片,bmp图片帧.

三、确保图片的格式是bmp格式的图片,如果你改个后缀(.bmp),但不是.bmp解析格式的,后面的函数加载将不成功.

使用画图软件对其他格式的图片转换成.bmp格式的图片.

四、实现代码如下:

//显示GIF动态图片 参数1:显示窗口,参数2:bmp图片帧所在的目录文件夹路径
BOOL ShowLoading(HWND hwnd,LPCTSTR path)
{
HBITMAP gif[12];//帧数,我这边只是12张bmp图片
TCHAR gifpath[MAX_PATH];
for (DWORD i = 0; i < 12; i++)
{//加载图片文件
swprintf_s(gifpath, MAX_PATH, _T("%s\\%u.bmp"), path, i + 1);
//如果不是.bmp格式(解析格式,不是换个后缀就行的)的图片,此函数加载将失败,而且GetLastError返回0,导致分析错误困难.
gif[i] = (HBITMAP)::LoadImage(NULL, gifpath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
}
//取一张图片的高宽信息
BITMAP obj;
GetObject((HANDLE)gif[0], sizeof(BITMAP),&obj);
//使用GDI画出来
HDC hdc = GetDC(hwnd);
HDC hdcMem = ::CreateCompatibleDC(hdc);
for (DWORD i = 0; TRUE;)
{
//每画一张图片,换一张,到尾部又回头部
if (i == 13)
i = 0;
::SelectObject(hdcMem, gif[i]);
//画出来
::BitBlt(hdc, 50, 50, obj.bmWidth, obj.bmHeight, hdcMem, 0, 0, SRCCOPY);
//动态图片的速度快慢,由Sleep控制.
Sleep(40);
i++;
}
return TRUE;
}


【2】基于上面实现的基础,我写了一个类,方便调用:

类的头文件: //需包含Windows.h

/*
类:显示GIF类.
须知:
1)为了能够关闭显示又不要共享全局变量,要使用堆内存创建对象.
2)图片的名称必须为 1.bmp ~ *.bmp.
使用步骤:
1)堆内存来创建对象.
2)调用Initiative函数提供对象所需的参数(如:显示窗口等信息).
3)调用LoadBitmap函数加载GIF的位图帧.
4)调用Thread_Start函数使用显示GIF线程来跑.
5)主线程调用End函数结束显示
优点:
1)可以重复多次使用Thread_Start和End函数用显示GIF的线程,显示图片,无需重新加载图片文件
2)可以扩张或缩放显示GIF图片
*/
class GIF
{
public:
//初始化参数
void Initiative(HWND DisplayWindow, POINT point, DWORD BmpFrameCount, DWORD Width, DWORD Hight, DWORD DisplayWidth, DWORD DisplayHight, DWORD Speed = 40);
//加载位图资源
BOOL LoadBitmap(LPCTSTR path);
//开启显示线程
BOOL Thread_Start();
public:
GIF();
~GIF();
public:
//结束显示GIF(这个函数给窗口线程使用)
BOOL End();
//开始显示GIF图片
BOOL Start();
public:
//释放位图资源
BOOL ReleaseBitmap();
//释放HDC
BOOL ReleaseHdc();
public:
//显示线程
static unsigned int _stdcall GIFThread(LPVOID data);
HANDLE dwThread; //线程句柄
private:
DWORD BmpCount; //图片总数
HBITMAP* gif;   //图片资源
TCHAR gifpath[MAX_PATH];//图片目录
DWORD Width;    //图片宽度
DWORD Hight;    //图片高度
DWORD DisplayWidth; //显示宽度
DWORD DisplayHight; //显示高度
HDC Wndhdc;     //窗口DC
HDC Memdc;      //存位图的内存DC
DWORD Speed;    //播放速度
HWND hwnd;      //显示窗口
POINT point;    //坐标
private:
volatile ULONG Open;      //显示开关
};


类的源文件:

/********************************************************
GIF动态图片类
*********************************************************/
GIF::GIF()
{
gif = NULL;
Width = 0;
Hight = 0;
Wndhdc = NULL;
Memdc = NULL;
Open = FALSE;
dwThread = NULL;
DisplayWidth = 0;
DisplayHight = 0;
}
//释放工作
GIF::~GIF()
{   //释放还存在的位图资源
ReleaseBitmap();
//释放线程计数
if (dwThread)
{
CloseHandle(dwThread);
dwThread = NULL;
}
}
//初始化参数  参数1:显示窗口  参数2:坐标      参数3:位图总数   参数4:图片宽度   参数5:图片高度
//            参数6:显示宽度  参数7:显示高度  参数8:播放速度(1000为1秒换一张图片)
void GIF::Initiative(HWND DisplayWindow, POINT point, DWORD BmpFrameCount, DWORD Width,DWORD Hight,DWORD DisplayWidth,DWORD DisplayHight, DWORD Speed)
{
this->hwnd = DisplayWindow;
this->point = point;
this->BmpCount = BmpFrameCount;
this->Width = Width;
this->Hight = Hight;
this->DisplayWidth = DisplayWidth;
this->DisplayHight = DisplayHight;
this->Speed = Speed;
}

//加载位图资源
BOOL GIF::LoadBitmap(LPCTSTR path)
{
gif = new HBITMAP[BmpCount];
if (gif == NULL)
return FALSE;
ZeroMemory(gif, sizeof(HBITMAP) * BmpCount);
//加载图片
for (DWORD i = 0; i < BmpCount; i++)
{
swprintf_s(gifpath, MAX_PATH, _T("%s\\%u.bmp"), path, i + 1);
gif[i] = (HBITMAP)::LoadImage(NULL, gifpath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
if (gif[i] == NULL)
{//如果有一个没有加载成功,那么将全部释放
ReleaseBitmap();
return FALSE;
}
}
//验证是否全部位图都是那个尺寸的
BITMAP obj;
for (DWORD i = 0; i < BmpCount; i++)
{
GetObject((HANDLE)gif[i], sizeof(BITMAP), &obj);
if (obj.bmWidth != this->Width || obj.bmHeight != this->Hight)
{//图片大小不对,释放位图
ReleaseBitmap();
return FALSE;
}
}
return TRUE;
}
//释放位图资源
BOOL GIF::ReleaseBitmap()
{
if (gif == NULL)
return FALSE;
//释放位图资源
for (DWORD i = 0; i < BmpCount; i++)
{
if (gif[i] != NULL)
{
if (!DeleteObject(gif[i]))
return FALSE;
gif[i] = NULL;
}
else
{
break;
}
}
//释放堆内存
if (gif)
{
delete[]gif;
gif = NULL;
}
return TRUE;
}
//开始
BOOL GIF::Start()
{
//使用GDI画出来
Wndhdc = GetDC(hwnd);
if (Wndhdc == NULL)
return FALSE;
Memdc = CreateCompatibleDC(Wndhdc);
if (Memdc == NULL)
{
ReleaseHdc();
return FALSE;
}
DWORD go = 0;
DWORD Max = BmpCount + 1; //回0变量
//设置为开始显示
InterlockedExchange(&Open, TRUE);
//设置拉伸或压缩的清晰度算法
SetStretchBltMode(Wndhdc, HALFTONE);
while (TRUE)
{
//结束
if (!Open)
break;
//每画一张图片,换一张,到尾部又回头部
if (go == Max)
go = 0;
SelectObject(Memdc, gif[go]);
//画出来
if (!StretchBlt(Wndhdc, point.x, point.y, DisplayWidth, DisplayHight, Memdc, 0, 0,Width,Hight ,SRCCOPY))
{//释放HDC和内存DC
ReleaseHdc();
return FALSE;
}
//播放速度控制
Sleep(Speed);
go++;
}
//清除窗口
InvalidateRect(hwnd, NULL, TRUE);
//画完释放内存DC和HDC.
if (!ReleaseHdc())
return FALSE;
return TRUE;
}

/*
功能:结束
返回值:成功结束返回TRUE,如果暂未开启返回FALSE
*/
BOOL GIF::End()
{
//设置结束
ULONG Started = InterlockedExchange(&Open, FALSE);
if (!Started)
{//表示还未开始
return FALSE;
}
else
{//成功设置,并等待10秒的GIF线程结束
DWORD Result = WaitForSingleObject(this->dwThread, 10000);
switch (Result)
{
case WAIT_TIMEOUT:
return FALSE;
case WAIT_OBJECT_0:
if (!GetExitCodeThread(this->dwThread, &Result))
return FALSE;
if (CloseHandle(this->dwThread))
this->dwThread = NULL;
if(Result == 0)
break;
else
return FALSE;
default:
return FALSE;
}
return TRUE;
}
}
//释放DC
BOOL GIF::ReleaseHdc()
{
//释放窗口HDC
if (Wndhdc)
{
ReleaseDC(hwnd, Wndhdc);
Wndhdc = NULL;
}
//删除内存DC
if (Memdc)
{
if (!DeleteDC(Memdc))
return FALSE;
Memdc = NULL;
}
return TRUE;
}

//使用其他线程显示图片
BOOL GIF::Thread_Start()
{
dwThread = (HANDLE)_beginthreadex(NULL, 0, GIF::GIFThread, (LPVOID)this, 0, NULL);
if (dwThread == NULL)
return FALSE;
return TRUE;
}
//显示GIF动态图片的线程
unsigned int _stdcall GIF::GIFThread(LPVOID Pgif)
{
GIF* pgif = (GIF*)Pgif;
//对此对象进行Initiative函数初始化检查.
if (pgif->hwnd == NULL||
pgif->BmpCount == 0 ||
pgif->Width == 0 ||
pgif->Hight == 0 ||
pgif->DisplayWidth == 0 ||
pgif->DisplayHight == 0 ||
pgif->Speed == 0
)
return -2;
//如果成功显示,并调用了End关闭函数返回0,否则显示失败返回-1
if (pgif->Start())
return 0;
else
return -1;
}


【3】调用例子:

//GIF图片帧的文件夹路径
#define LOADING_PATH _T("C:\\Users\\Aaron\\Desktop\\编程\\项目\\Gatherer\\res\\gif文件夹")
//显示坐标
POINT point;
point.x = 50;
point.y = 50;
//创建对象
GIF* gif = new GIF;
if (gif)
{
gif->Initiative(window.WndHwnd, point, 12,35,35,35,35, 40);
if (!gif->LoadBitmap(LOADING_PATH))
delete gif;
else
if (gif->Thread_Start())
{
Sleep(5000);
if (gif->End())
MessageBox(window.WndHwnd, _T("结束显示GIF成功!"), _T("OK"), MB_OK);
}
}
//清除对象
delete gif;


我使用的GIF素材:



bmp图片帧:

























【效果】

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息