项目背景
随着互联网传播技术的普及和网络社交文化的繁荣,传统的文字传播在日常表达上不具有完整的信息,同样一句话在不同的场景或者情绪下会出现不同的效果,使用传统的文字聊天,我们无法通过聊天软件来判别聊天者的表情和动作来理解对方的情绪,因此,表情包在文字表达上可以更好地使对方理解我们此时的情绪,以方便更好的表达情感。
“一言不合就斗图”已经成为了大多数网民所必备的一项技能,一般常用于各式聊天软件,例如微信、QQ等,“斗图”开始于QQ,在进行聊天时,大家发送搞怪图片来表达自己想要表达的意思,后发展在各式社交论坛上,例如贴吧、知乎等;
可行性分析
可行性分析从三方面来分析:
经济可行性:成本比较低
操作可行性:操作简单
技术可行性:利用一些工具和第三方库
需求分析
要生成Gif动态图,我们有两种生成方式:图片生成和视频生成,借助的工具是ffmpeg工具,UI界面布局器,首先介绍一下ffmpeg工具:
ffmpeg是特别强大的专门用于处理音视频的开源库,既可以使用它的API对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑你的音视频文件;ffmpeg由以下几部分组成:
(1)libavcodec: 提供了一系列编码器的实现。
(2)libavformat: 实现在流协议,容器格式及其本IO访问。
(3)libavutil: 包括了hash器,解码器和各利工具函数。
(4)libavfilter: 提供了各种音视频过滤器。
(5)libavdevice: 提供了访问捕获设备和回放设备的接口。
(6)libswresample: 实现了混音和重采样。
(7)libswscale: 实现了色彩转换和缩放工能。
在学习了ffmpeg之后,我们知道ffmpeg是使用命令行的格式,响应Cmd来实现相关的操作,因此我们在实现Gif的制作,主要就是通过Cmd控制台来使用ffmpeg工具,向该工具发送对应的命令来实现各部分操作。
总体设计
本项目共有两种生成Gif的方式:图片生成和视频生成,如图为应该实现的流程:
我们需要依靠Duilib库,它的好处有:
(1)可以基于GDI在窗口上进行重绘,没有其他依赖,没有使用其他的系统调用,能够解决传统MFC界面的一系列问题;
(2)使用XML来描述界面风格与布局,实现了UI和逻辑代码的分离,同时可以实现各种界面效果,例如换肤、透明等;
(3)完全兼容ActiveX控件,也可以和MFC等界面库配合使用;
(4)可广泛用于互联网客户端、工具软件客户端、车载电脑系统等;
Duilib库是基于Win32系统的一套UI库,因此我们先来了解一下Win32相关知识。
- Win32程序介绍
1、首先建立一个Win32工程,如图:
建立工程,出现下图:
点击下一步,出现下图:
单击空项目,然后完成,这样一个完整的Win32项目创建完成。
2、实现代码:
#include <Windows.h>
#include <tchar.h>
//消息回调函数
POINT start;//起点
POINT end;//终点
int state = 0;//刚开始的状态,来控制画什么图形
//用户自己定义:函数的格式必须按照系统的固定格式来定义,
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MOUSEMOVE://鼠标移动,要获取起点,在移动期间,触发重绘
end.x = LOWORD(lParam);//改变终点
end.y = HIWORD(lParam);
InvalidateRect(hWnd, NULL, true);//触发重绘
break;
case WM_LBUTTONDOWN://按鼠标就要触发重绘
//MessageBox(NULL, _T("LBTNDOWN"), _T("Test"), IDOK);
{
start.x = LOWORD(lParam);
start.y = HIWORD(lParam);
return 0;
}
case WM_LBUTTONUP://放开鼠标
{
end.x = LOWORD(lParam);
end.y = HIWORD(lParam);
HDC hdc = GetDC(hWnd);
switch (state)
{
case 1://画直线
MoveToEx(hdc, start.x, start.y, NULL);//将光标移到起点的位置
LineTo(hdc, end.x, end.y);//画到终点的位置
break;
case 2://画矩形
Rectangle(hdc, start.x, start.y, end.x, end.y);//画矩形
break;
case 3://画椭圆
Ellipse(hdc, start.x, start.y, end.x, end.y);
break;
}
ReleaseDC(hWnd, hdc);//用完了要进行释放
start = end;//每次画完后将起点和终点放在同一位置
return 0;
}
case WM_PAINT://重绘
{
HDC hdc = GetDC(hWnd);
switch (state)
{
case 1://画直线
MoveToEx(hdc, start.x, start.y, NULL);//将光标移到起点的位置
LineTo(hdc, end.x, end.y);//画到终点的位置
break;
case 2://画矩形
Rectangle(hdc, start.x, start.y, end.x, end.y);//画矩形
break;
case 3://画椭圆
Ellipse(hdc, start.x, start.y, end.x, end.y);
break;
}
ReleaseDC(hWnd, hdc);//用完了要进行释放
return 0;
}
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int
nCmdShow)//hInstance相当于这个应用程序
{
//MessageBox(NULL,_T("hell Win32"),_T("Win32"),IDCANCEL);//IDCANCEL/IDOK/等改变按钮
//Step1:注册一个窗口类
HWND hwnd; //窗口的句柄
WNDCLASSEX wc; //窗口类结构
wc.cbSize = sizeof(WNDCLASSEX);//当前结构体总共占了多少个字节
wc.style = CS_VREDRAW | CS_HREDRAW;//窗口刷新机制
wc.lpszMenuName = 0;//是否有菜单
wc.lpszClassName = _T("Win32");//标记这个窗口
wc.lpfnWndProc = WinProc; //消息回调函数,用户必须提供
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
wc.hIconSm = NULL;
RegisterClassEx(&wc); //注册窗口
//Step2:创建一个窗口
hwnd = CreateWindow(
_T("Win32"), //窗口的类名,也就是上面我们自定义的窗口类的名字
_T("我的第一个Win32程序"), //窗口的标题
WS_OVERLAPPEDWINDOW, //窗口style
500, //窗口位置x坐标
300, //窗口位置y坐标
800, //窗口宽度
600, //窗口高度
NULL, //父窗口句柄
NULL,//菜单句柄,没有时设置为NULL
hInstance, //实例句柄
NULL //创建数据
);
if (!hwnd)
{
return FALSE;
}
ShowWindow(hwnd, SW_SHOW); //显示窗口
UpdateWindow(hwnd); //刷新
//Step3:消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
其中Win32程序入口函数为WinMain;
从上面的代码可以看出,Win32程序一般流程为:
(1)设计窗口类:完善窗口类的结构体;将WNDCLASSEX结构体转到定义,为:
其中cbSize表示该结构体的大小;style表示窗口类的样子,通常是CS_HREDRAW |
CS_VREDRAW,表示水平和垂直重绘;IpfnWndProc表示窗口的过程处理函数,这个函数拦截用户需要处理的消息;cbClsExtra和cbWndExtra一般设置为0;HInstance表示窗口实例;hIcon表示窗口类的图标;hCur