前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(译)SDL编程入门(2)在屏幕上显示图像

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

作者头像
arcticfox
发布2020-09-24 15:26:12
2.5K0
发布2020-09-24 15:26:12
举报

在屏幕上显示图像

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

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

代码语言:javascript
复制
//启动SDL并创建窗口
bool init();

//加载媒体
bool loadMedia();

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

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

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

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

代码语言:javascript
复制
//我们要渲染的窗口
SDL_Window* gWindow = NULL;
    
//窗口所包含的表面
SDL_Surface* gScreenSurface = NULL;

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

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

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

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

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

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

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

代码语言:javascript
复制
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来获取窗口包含的表面。

代码语言:javascript
复制
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 "挪来挪去,直到程序最终能加载它。

代码语言:javascript
复制
void close(){
    //释放表面
    SDL_FreeSurface( gHelloWorld );
    gHelloWorld = NULL;

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

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

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

当你的指针没有指向任何东西的时候,一定要养成让它们指向NULL的习惯。

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程之路从0到1 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 在屏幕上显示图像
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档