专栏首页编程之路的专栏(译)SDL编程入门(2)在屏幕上显示图像

(译)SDL编程入门(2)在屏幕上显示图像

在屏幕上显示图像

现在你已经打开了一个窗口,让我们在上面放一张图片。

注意:从现在开始,教程将只涉及源代码的关键部分。如果想看完整的程序,你必须下载完整的源码。

//启动SDL并创建窗口
bool init();

//加载媒体
bool loadMedia();

//释放媒体并关闭SDL
void close();

在第一个教程中,我们把所有的东西都放在主函数中。由于这是一个小程序,我们可以摆脱这种做法,但在真实的程序中(比如视频游戏),你希望你的代码尽可能的模块化。这意味着你希望你的代码是整齐的块,每个块都易于调试和重用。

在这里,这意味着我们用函数来处理初始化、加载媒体和关闭SDL应用程序。我们在源文件的顶部声明这些函数。

我收到很多邮件,说在C语言中调用这个函数 "close "会引起冲突,因为不支持函数重载。这也是我在本教程中使用C++的原因之一。所以这个函数被称为 "close "并不是bug。

//我们要渲染的窗口
SDL_Window* gWindow = NULL;
    
//窗口所包含的表面
SDL_Surface* gScreenSurface = NULL;

//我们将加载并显示在屏幕上的图像。
SDL_Surface* gHelloWorld = NULL;

这里我们声明一些全局变量。通常情况下,你要避免在大型程序中使用全局变量。我们之所以在这里这样做,是因为我们希望源代码尽可能的简单,但是在大型项目中,全局变量会使事情变得更加复杂。由于这是一个单一的源文件程序,我们不用太担心这个问题。

这里有一个新的数据类型,叫做SDL表面。SDL表面只是一种图像数据类型,它包含了图像的像素以及渲染所需的所有数据。SDL表面使用软件渲染,这意味着它使用CPU来渲染。可以渲染硬件图像,但是比较困难,所以我们先从简单的方法来学习。在以后的教程中,我们将介绍如何渲染GPU加速的图像。

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

请注意,这些都是指向 SDL 表面的指针。原因是:

  1. 我们将动态分配内存来加载图像
  2. 最好通过内存位置来引用图像。想象一下,你有一个游戏,游戏中的砖墙由同一个砖头图像多次渲染组成(比如《超级马里奥兄弟》)。当你可以拥有一个图像副本并反复渲染时,在内存中拥有几十个图像副本是很浪费的。

另外,一定要记得初始化你的指针。我们在声明它们的时候会立即将它们设置为NULL。

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
    {
        //创建窗口
        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
        {
            //获取窗口表面
            gScreenSurface = SDL_GetWindowSurface( gWindow );
        }
    }

    return success;
}

如你在这里看到的,我们已经采用了SDL初始化和窗口创建代码,并将其放在自己的函数中。新功能是调用了SDL_GetWindowSurface。

我们想在窗口内部显示图像,为了做到这一点,我们需要得到窗口内部的图像。所以我们调用SDL_GetWindowSurface来获取窗口包含的表面。

bool loadMedia(){
    //加载成功标志
    bool success = true;

    //加载图片
    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;
}

在 load media 函数中,我们使用 SDL_LoadBMP 加载图像。SDL_LoadBMP 接收 bmp 文件的路径并返回加载的表面。如果函数返回NULL,意味着它失败了,所以我们使用SDL_GetError向控制台打印一个错误。

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

同样,如果程序正在运行,但它无法加载镜像,你可能有一个工作目录的问题。工作目录的功能因操作系统和IDE而异。如果上网搜索如何找到或修复工作目录都找不到解决办法,我建议把 "02_getting_an_image_on_thescreen "文件夹里的 "hello_world.bmp "挪来挪去,直到程序最终能加载它。

void close(){
    //释放表面
    SDL_FreeSurface( gHelloWorld );
    gHelloWorld = NULL;

    //销毁窗口
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;

    //退出SDL子系统
    SDL_Quit();
}

在我们的清理代码中,我们像之前一样销毁窗口并退出 SDL,但我们还必须处理我们加载的表面。我们通过SDL_FreeSurface来释放它。不要担心屏幕表面,SDL_DestroyWindow会处理它。

当你的指针没有指向任何东西的时候,一定要养成让它们指向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 );
            //更新表面
            SDL_UpdateWindowSurface( gWindow );
            //等待2秒
            SDL_Delay( 2000 );
        }
    }

    //释放资源和关闭SDL
    close();

    return 0;
}

在我们的主函数中,我们初始化SDL并加载图像。如果加载成功,我们就使用 SDL_BlitSurface 将加载的表面混合到屏幕表面。

blitting的作用是将一个源表面和一个拷贝标记到目标表面上。SDL_BlitSurface的第一个参数是源图像。第三个参数是目标图像。我们将在以后的教程中关注第二个和第四个参数。

现在,如果这是我们唯一的绘图代码,我们仍然不会在屏幕上看到我们加载的图像。还有一个步骤。

在屏幕上绘制了所有我们要显示的这一帧画面后,我们要使用SDL_UpdateWindowSurface来更新屏幕。当你画到屏幕上的时候,一般不是画到你所能看到的屏幕图像上。默认情况下,大部分的渲染系统都是双缓冲的。这两个缓冲区就是前缓冲区和后缓冲区。

当你进行SDL_BlitSurface这样的绘制调用时,你会渲染到后缓冲区。你在屏幕上看到的是前缓冲区。我们这样做的原因是因为大多数帧需要将多个对象绘制到屏幕上。如果我们只有一个前缓冲区,我们将能够看到正在绘制的帧,这意味着我们将看到未完成的帧。所以我们要做的是先把所有的东西都画到后面的缓冲区,一旦我们完成了,我们就把后面和前面的缓冲区交换一下,这样现在用户就可以看到完成的帧了。

这也意味着你不会在每次blit之后调用SDL_UpdateWindowSurface,只有在当前帧的所有blits都完成之后才会调用。

现在我们已经把所有的东西都渲染到窗口上了,我们延迟两秒钟,这样窗口就不会消失了。等待结束后,我们关闭程序。

这里[1]下载本教程的媒体和源代码。

参考资料

[1]

这里: http://www.lazyfoo.net/tutorials/SDL/02_getting_an_image_on_the_screen/02_getting_an_image_on_the_screen.zip

[2]

原文链接: http://www.lazyfoo.net/tutorials/SDL/02_getting_an_image_on_the_screen/index.php

本文分享自微信公众号 - 编程之路从0到1(artofprogram),作者:编程之路从0到1

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-09-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SDL简介

    SDL在结构上是将不同操作系统的库再封装成相同的函数,例如SDL在Windows平台上是DirectX的封装,而在使用X11的平台上(包括Linux),SDL则...

    arcticfox
  • (译)SDL编程入门(5)Surface 优化和软拉伸

    到现在为止,我们一直都是将我们的图像原始地融合在一起。因为我们只显示一张图片,所以这并不重要。当你在做游戏的时候,原始图像会导致不必要的减速。我们将把它们转换为...

    arcticfox
  • (译)SDL编程入门(7)纹理加载和渲染

    SDL2 的一个主要新功能是纹理渲染 API。这为您提供了快速、灵活的基于硬件的渲染。在本教程中,我们将使用这种新的渲染技术。

    arcticfox
  • 论企业如何快速建立SDL流程

    今年很多比较大的互联网公司开始试行SDL落地,但是由于相关资料文档有限,导致落地困难,今天这篇文章就旨在讨论一下企业SDL如何快速落地问题,题主落地SDL时间并...

    FB客服
  • android 游戏移植 (二) | SDL2.0适配 西游释厄传

    SDL系列讲解(一) 简介 SDL系列讲解(二) 环境搭建 SDL系列讲解(三) 工具安装 SDL是什么,能干什么,为什么我们要学习它? SDL系列讲解(四)...

    用户1263308
  • SDL系列讲解(六) SDL_Activity流程

    SDL系列讲解(一) 简介 SDL系列讲解(二) 环境搭建 SDL系列讲解(三) 工具安装 SDL是什么,能干什么,为什么我们要学习它? SDL系列讲解(四)...

    用户1263308
  • SDL系列讲解(九) 异常退出分析

    SDL系列讲解(一) 简介 SDL系列讲解(二) 环境搭建 SDL系列讲解(三) 工具安装 SDL是什么,能干什么,为什么我们要学习它? SDL系列讲解(四)...

    用户1263308
  • 【SDL最初实践】开篇

    记得在之前参加面试时,被问到SDL,勉强把其流程稍加解释的给背了出来,面试官一脸不屑的样子记忆犹新。因为那时负责的是乙方安服技术团队,针对甲方推崇但又没几家实际...

    aerfa
  • SDL2库(3)-Android 端源码简要分析(VideoSubSystem)参考

    项目位置 https://github.com/deepsadness/SDLCmakeDemo

    deep_sadness
  • SDL2和OpenGL使用踩坑笔记经验分享

    LFTK 是一个嵌入式GUI,为了开发方便,需要提供PC运行环境。我选择了SDL2+OpenGL+nanovg来实现底层的渲染,让LFTK可以运行在各个平台上。...

    砸漏

扫码关注云+社区

领取腾讯云代金券