SDL2入门教程(02_Getting an Image on the Screen)

来源:互联网 发布:mac如何进行文件管理 编辑:程序博客网 时间:2024/06/08 19:20

注: 本教程翻译自 lazyfoo的教程,原网址戳这里.可能语言不准确,仅供参考。

在屏幕上展示图片

上一篇教程中粗略地讲了如何创建窗口,现在让我们在窗口里面展示一张图片。
NOTE:从这篇教程开始,代码示例将会只包含关键的部分。如果想要完整版的源代码,请戳文尾链接

//Starts up SDL and creates window//函数功能:初始化SDL并创建窗口bool init();//Loads media//函数功能:加载多媒体文件bool loadMedia();//Frees media and shuts down SDL//函数功能:释放多媒体文件并关闭SDLvoid close();

在第一篇教程中,我们把所有的代码都放在了主函数里面,处理简单的问题(比如创建窗口)时可以暂时这样做,但是在实际的编程中尤其是大型项目(比如游戏)里请一定要模块化自己的代码。这样做之后,区块化的代码相互独立不仅复用率高,debug时也会方便很多。

这段代码中一开始的声明这三个函数的目的是为了让它们处理初始化,载入多媒体文件和关闭SDL的工作。

原作者在这里提到:我收到很多使用C语言进行编程的人的邮件说这里使用的函数名close会导致冲突,编译报错。实际上是因为C并不支持函数重载,这也是为什么这些教程我都使用C++的原因之一,所以如果这里的函数名为close是没有bug的。

//初始化我们将要渲染的窗口SDL_Window* gWindow = NULL;//The surface contained by the window//窗口包含的surfaceSDL_Surface* gScreenSurface = NULL;//我们将会载入并在屏幕上展示的图片SDL_Surface* gHelloWorld = NULL;

在这块代码中定义了一些全局变量,同样地在大工程里面请尽量避免全局变量的使用,因为它会让事情变得更加复杂。而我们现在仍要使用全局变量的理由是我们想让这个教程的代码尽可能的简单化,方便理解,而且他也不是什么大项目(只是一个拥有一个源文件的project而已)。

这里的新的数据类型叫做SDL Surface,SDL surface就是一种包含图像所有的像素点以及一些需要用于渲染这个图像的数据的图像数据类型。
Here’s a new data type called an SDL Surface. An SDL surface is just an image data type that contains the pixels of an image along with all data needed to render it.

SDL surface使用软件渲染,这就意味着它会使用CPU进行渲染。当然也可以进行硬件渲染,但是会更加复杂一些,所以还是先学简单的方法吧。以后的教程中会讲到如何GPU加速渲染图像。

我们这里要处理的图像就是要在屏幕中显示的图像(你在窗口中看到的),我们将从文件中加载它。

注意代码中使用的是指向SDL surface的指针,理由如下:
1. 我们会动态地分配内存来加载图像
2. 引用内存中的图片会更好。想象一下你做的一个游戏里面有一堵由同样的砖块多次渲染而成的砖墙(就像超级玛丽兄弟)。如果在内存中反复载入多组相同砖块的图像来组成墙是很浪费内存的,更好的方法是通过引用同一个图像再多次渲染成墙。

同样,一定要记得初始化你的指针,就像代码里面一样在声明的时候直接初始化。

bool init(){    //初始化标记变量    bool success = true;    //初始化 SDL    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )    {        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );        success = false;    }    else    {        //创建 window        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );        if( gWindow == NULL )        {            printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );            success = false;        }        else        {            //获取 window surface            gScreenSurface = SDL_GetWindowSurface( gWindow );        }    }    return success;}

正如你所看到的,我们把SDL的初始化和窗口创建工作放到了相应的函数内。而新一点的东西就是对函数SDL_GetWindowSurface()的调用了。

我们想在窗口内展示图片,为了能完成这个目标,首先需要获取窗口内的surface,所以我们调用了SDL_GetWindowSurface()去获取窗口内包含的surface。

bool loadMedia(){    //用来标记载入成功的变量    bool success = true;    //载入 splash image    gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );    if( gHelloWorld == NULL )    {        printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );        success = false;    }    return success;}

在载入多媒体文件的函数中我们调用了SDL_LoadBMP()函数,SDL_LoadBMP()接受一个存有BMP图像文件的地址的字符串做为参数并返回载入完成的surface。如果这个函数返回NULL,那就表示载入失败了,我们同样可以用之前提到的SDL_GetError()函数来获得错误信息,并使用printf函数打印到控制台。

需要注意的一点是,这段代码假设你有一个名为“02_getting_an_image_on_the_screen”的目录,它在你的工作目录中包含一个名为“hello_world.bmp”的图像。 工作目录是你的应用程序认为它正在运行的地方。 通常,你的工作目录是可执行文件的目录,但是像Visual Studio这样的程序将工作目录更改到vcxproj文件所在的位置。 所以如果你的应用程序找不到图像,请确保它在正确的位置。

void close(){    //释放 surface    SDL_FreeSurface( gHelloWorld );    gHelloWorld = NULL;    //销毁窗口    SDL_DestroyWindow( gWindow );    gWindow = NULL;    //退出 SDL subsystems    SDL_Quit();}

在这段清理工作的代码里面,我们销毁了窗口并退出了SDL就像前一篇中做的一样,但是我们依然要考虑到已经载入文件的surface。直接调用函数SDL_FreeSurface()就好了,它会帮我们做好清理工作的。

记得养成确定指针不再使用或不再指向任何东西的时候将指针赋NULL的习惯。

int main( int argc, char* args[] ){    //初始化 SDL 并创建窗口    if( !init() )    {        printf( "Failed to initialize!\n" );    }    else    {        //加载多媒体文件        if( !loadMedia() )        {            printf( "Failed to load media!\n" );        }        else        {            //应用这个图像            SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL );

在主函数中,我们调用了之前自己的定义init()函数来初始化SDL并加载图像。成功之后就通过调用SDL_BlitSurface()把加载完成的surface位块传送(blit)到screen surface。

位块传送(blit)所做的事就是将获得的源surface进行复制并将复制出的东西放到目标处的surface。SDL_BlitSurface()函数的第一个参数就是源surface,第三个参数就是目标位置。第二个和第四个参数在后面的教程中再进行讲解。

做完surface的绘制工作后,到目前为止仍不能让他出现在窗口里。还有一步:

            //更新surface            SDL_UpdateWindowSurface( gWindow );

在屏幕上绘制所有内容后,我们要使用SDL_UpdateWindowSurface()更新屏幕。通常当你尝试绘制在屏幕上时,你并非直接绘制在你所见的窗口中的图像上的(也就是说并不能直接看到)。默认情况下,大多数渲染系统都是双缓冲机制的,这两个缓冲区分为前、后缓冲区。

当你调用SDL_BlitSurface()这类函数时,你是在后缓冲区中进行渲染的。而你在屏幕上所见的则是在前缓冲区的内容。使用这样的双缓冲机制是因为很多帧中需要在屏幕上绘制不同的大量的对象,如果我们只有一个前缓冲区,我们就会在一帧一帧中看到对象被画在屏幕上的过程也就是未渲染完毕的帧。所以我们就在后缓冲区中绘制所有需要绘制的东西然后再交换前后缓冲区让用户看到完成的帧。

这也意味着如果你不必在每次位块传送(blit)之后都调用SDL_UpdateWindowSurface()这个函数,只需要在所有当前帧的绘制工作完成后调用就可以了。

            //保持窗口两秒            SDL_Delay( 2000 );        }    }    //释放资源并关闭SDL    close();    return 0;}

现在我们已经渲染了所有需要渲染的东西到窗口中了,我们使窗口保持两秒钟的目的是窗口不会一闪而逝。当所有的工作都完成后,代码自动结束整个程序。

下载本篇教程中的代码及多媒体文件,戳原作者链接

注: 本教程翻译自 lazyfoo的教程,原网址戳这里.可能语言不准确,仅供参考。

原创粉丝点击