SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出输入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、MacOSX等)的应用软件。目前 SDL 多用于开发游戏、 模拟器、 媒体播放器等多媒体应用领域。
即使是开发游戏,是不是有时也需要画点曲线呢?下面就看看在怎么在SDL引擎里面使用CChart画图。
首先需要安装SDL库。
这里使用的是SDL2.0.16版本。在SDL官网www.libsdl.org上下载开发包SDL2-devel-2.0.16-VC,解压到硬盘,并根据解压位置设置相应的Visual Studio文件夹位置即可。
下面开始。
1、用Visual Studio 2010建立一个Win32控制台应用程序LessonA53,空项目。
2、添加一个cpp文件,并根据SDL2的模版编写如下代码。
#include <stdio.h>
#include <SDL.h>
#include <SDL_syswm.h>
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2Main.lib")
#include <Windows.h>
int main(int argc, char* argv[])
{
SDL_Surface *hello = NULL;
SDL_Surface *screen = NULL;
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window* window = NULL;
window = SDL_CreateWindow("SDL CChart",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480, SDL_WINDOW_SHOWN);
if (window == NULL)
{
fprintf(stderr, "Window could not be created: %s\n", SDL_GetError());
return 1;
}
screen = SDL_GetWindowSurface(window);
if(screen == NULL)
{
fprintf(stderr, "Could not get surface: %s\n", SDL_GetError());
return 1;
}
bool bQuit = false;
SDL_Event event;
WPARAM wParam;
LPARAM lParam;
while(!bQuit)
{
while( SDL_PollEvent( &event ) )
{
switch(event.type)
{
case SDL_QUIT:
bQuit = true;
break;
default:
break;
}
}
}
SDL_FreeSurface(hello);
SDL_Quit();
return 0;
}
运行结果如下,就一个空窗口!
3、把CChart库文件拷贝到LessonA54内层文件夹。
4、添加CChart的引用和包含,添加系统数学库的引用,定义一个绘图CChart变量。
#include "Chart.h"
#if defined(_UNICODE) || defined(UNICODE)
# pragma comment(lib,"CChartu.lib")
#else
# pragma comment(lib,"CChart.lib")
#endif
using namespace NsCChart;
#include <math.h>
CChart chart;
5、找一张漂亮的图片,命名为hello.bmp,拷贝到LessonA53内层文件夹,将作为背景图像。
6、设置背景图像。在bool bQuit = false;这一行之前,插入如下代码。
hello = SDL_LoadBMP("hello.BMP");
SDL_BlitSurface(hello, NULL, screen, NULL);
7、添加曲线数据。在刚才的代码之后,插入如下代码。
double pi = 4.0*atan(1.0);
for(int i=0; i<720; ++i)
{
chart.AddPoint2D(i, 1.2*sin(i*2.0*pi/360));
}
chart.SetTitle(_T("在SDL2.0界面中使用CChart"));
chart.SetBkgndColor(RGB(10, 10, 10));
chart.SetTitleColor(RGB(245, 245, 245));
8、编写获得HWND和HDC句柄代码,包含两个函数,放置在main函数之前。这是最核心的部分!
HWND MyGetHWND(SDL_Window *window)
{
if(!window)return NULL;
SDL_SysWMinfo sys;
SDL_VERSION(&sys.version);
if(!SDL_GetWindowWMInfo(window, &sys))return NULL;
return sys.info.win.window;
}
HDC MyGetHDC(SDL_Window *window)
{
if(!window)return NULL;
SDL_SysWMinfo sys;
SDL_VERSION(&sys.version);
if(!SDL_GetWindowWMInfo(window, &sys))return NULL;
return sys.info.win.hdc;
}
可以看到,从SDL2的SDL_Window结构中可以提取出HWND和HDC句柄!
9、编写重绘代码。放置在上述代码之后。
int MyDraw(SDL_Window *window)
{
HWND hWnd = MyGetHWND(window);
if(!hWnd)return 0;
RECT rect;
GetClientRect(hWnd, &rect);
long w = rect.right-rect.left;
long h = rect.bottom-rect.top;
SDL_Rect rects[3];
rects[0].x = rect.left;
rects[0].y = rect.top;
rects[0].w = w/2;
rects[0].h = h/2;
rects[1].x = rect.left+w/2;
rects[1].y = rect.top;
rects[1].w = w/2;
rects[1].h = h/2;
rects[2].x = rect.left;
rects[2].y = rect.top+h/2;
rects[2].w = w/2;
rects[2].h = h/2;
int nRet = 0;
nRet = SDL_UpdateWindowSurfaceRects(window, rects, 3);
rect.left = (rect.left + rect.right)/2;
rect.top = (rect.top + rect.bottom)/2;
HDC hDC = MyGetHDC(window);
MyMemDC memdc(hDC, 0, &rect);
memdc.SetAutoUpdate(false);
chart.OnDraw(memdc, rect);
memdc.Update();
return nRet;
}
这里把主窗口分成了四块,CChart图像在右下位置绘制。
注意这里使用了前面提取HWND和HDC句柄的函数。
此外,由于SDL一般不直接操作屏幕,这里使用了CChart提供的内存DC。此内存DC和新版MFC提供的CMemDC功能相近,但CMemDC一般只能在MFC程序中使用,MyMemDC则没有这个限制。
10、实现重绘。在main()函数里面,chart.SetTitleColor(RGB(245, 245, 245));之后,插入如下代码。
MyDraw(window);
基本完工,效果如下。
看起来不错,但是呢,如果我们把窗口最小化,再复原,会发现窗口里面什么都没有了!
11、解决重绘问题
在while(!bQuit)循环体的末端位置,插入如下代码。
if( MyDraw(window) == -1 )
{
break;
}
现在上面的重绘问题解决了。但是目前图像还没有消息响应,让我们加上。
12、添加消息响应。
在while(!bQuit)循环的前面,插入如下代码。
HDC hDC = MyGetHDC(window);
在switch(event.type)的代码体内部,加上下列代码。
case SDL_KEYDOWN: //如果是键盘按下事件
if(event.key.keysym.sym == SDLK_ESCAPE) //如果按的是ESC键
{
bQuit = true; //退出循环
}
break;
case SDL_MOUSEBUTTONDOWN:
if(event.button.button == SDL_BUTTON_LEFT)
{
if(event.button.clicks == 1)
{
wParam = event.key.keysym.sym;
lParam = event.button.x + (event.button.y<<16);
chart.OnEvent(hDC, WM_LBUTTONDOWN, wParam, lParam);
}
else
{
wParam = event.key.keysym.sym;
lParam = event.button.x + (event.button.y<<16);
chart.OnEvent(hDC, WM_LBUTTONDBLCLK, wParam, lParam);
}
}
else if(event.button.button == SDL_BUTTON_RIGHT)
{
wParam = event.key.keysym.sym;
lParam = event.button.x + (event.button.y<<16);
chart.OnEvent(hDC, WM_CONTEXTMENU, wParam, lParam);
}
break;
case SDL_MOUSEBUTTONUP:
if(event.button.button == SDL_BUTTON_LEFT)
{
wParam = event.key.keysym.sym;
lParam = event.button.x + (event.button.y<<16);
chart.OnEvent(hDC, WM_LBUTTONUP, wParam, lParam);
}
break;
case SDL_MOUSEMOTION:
if(true)
{
wParam = event.key.keysym.sym;
lParam = event.button.x + (event.button.y<<16);
chart.OnEvent(hDC, WM_MOUSEMOVE, wParam, lParam);
}
break;
这里把SDL2的事件代码转换为Windows的标准消息,然后传递给CChart使用。
OK,现在正常了,除了右键菜单还没法调出来。