codecamp
案例二、在 surface 上面贴一张图片

上节课说过了,surface 是用来绘制 2D 图形的场所,而图片也是二维图形,所以可以直接把图片张贴在它上面。 本节课,我们探讨如何把一张图片贴在 surface 上。 程序运行结果(960x540)

程序结构

将一些代码封装成函数

我们把所有的代码都放在 main 函数里是十分不明智的做法,应当适当地把一些功能封装成单独的函数,能够让代码可读性更强。

我们把负责进行 SDL 初始化的一些操作步骤封装在以下函数体内:

bool mySdlInit();

再把加载媒体的操作步骤封装在:

bool mySdlLoadMedia();

最后再把一些析构函数封装在:

void mySdlClose();

前两个函数的具体实现请在文章尾部找到。

新知识:创建一个用于显示图片的 surface

我们不能直接在之前已经创建好了的 surface 上面显示图片,而必须另外再为图片创建一个 surface:

SDL_Surface* pictureSurface = NULL;

变量作用域

这次我们把一些变量放在全局范围中。但是在大型程序开发过程中,这并不是很好的习惯。 我们给出的建议是:尽量降低函数之间的耦合度。一旦我们赋予多个函数访问同一个变量的权力,那么就会非常容易让程序出现 bug。 但是由于本案例并不是很大的项目,定义且使用全局变量也不会出现任何 bug。

本案例中,我们把上面的 pictureSurface 变量就放在了全局范围。然后再把以下变量也定义在全局范围:

//想要创建的窗口
SDL_Window* window = NULL;
//窗口surface
SDL_Surface* screenSurface = NULL;

SDL_Surface* 的析构函数

当我们不再用到某个 surface 后,请记得及时释放掉它之前申请的内存。

SDL_Surface* 对象的析构函数是: void SDL_FreeSurface(SDL_Surface* surface); 它需要将 surface 对象作为参数传入,用于释放指定对象的内存。

前文说到,动态分配的内存要及时析构掉,这样可以最大化节约内存的占用,使程序运行更稳定。

关于野指针的问题

指向无效地址的指针就叫做“野指针”。析构完毕后,原来位置的内存将会被操作系统回收,且不可重新使用。

原来指向对象的指针将会变成野指针,而访问野指针是一件非常危险的事情,它可能会破坏掉其他进程的内存,引发灾难性后果。 为了安全起见,我们建议您将析构完毕的对象指针指向 零(NULL) 地址。

SDL_FreeSurface(pictureSurface);

新知识:加载 BMP 图像

SDL 原生只支持 BMP 图像的加载。 若要加载其他格式的图片,您需要另外使用一些函数库,例如 FreeImage 等,但这不在教程讨论的范围内。

首先准备好 BMP 格式的图像。要求窗口大小必须与 BMP 图像的尺寸一致,因此可能需要修改我们之前自己定义的 SCREEN_WIDTHSCREEN_HEIGHT 宏值。

我这里有 960x540 尺寸的 BMP 图片: 960x540

注:可以采用一些绘图软件调整图片尺寸,并导出 BMP 格式。

SDL2 加载 BMP 的函数

SDL_Surface* SDL_LoadBMP(const char* fileName);

该函数要求传入一个文件名称(相对或绝对路径)。 加载成功则返回 SDL_Surface 的实例,失败则返回空白值。

我们首先定义一个 BMP_FILE_NAME 宏,表示 BMP 文件名称:

#define BMP_FILE_NAME "bmp/hello.bmp"

我们可以轻松地写出 bool mySdlLoadMedia(); 的实现:

bool mySdlLoadMedia()
{
    //加载 BMP 文件
    pictureSurface = SDL_LoadBMP(BMP_FILE_NAME);
    if (!pictureSurface)
    {
        printf("Unable to load image: " BMP_FILE_NAME "SDL_LoadBMP Error: %s\n", SDL_GetError());
        return false;
    }


    return true;
}

新知识:将 BMP 图像贴到屏幕 surface 上

实现这个功能的函数是 SDL_BlitSurface,它有四个参数: 1: 图片 surface 对象 3: 屏幕 surface 对象 2、4: 这两个参数暂时用不到,将在以后讨论。

在本案例的 main 函数中,请写出以下语句:

SDL_BlitSurface(pictureSurface, NULL, screenSurface, NULL);

总结

本案例接触了三个新函数:SDL_FreeSurface、SDL_LoadBMP、SDL_BlitSurface,具备了基本的显示图像的能力。

本案例的完整代码

//以下预处理指令块用于模拟bool型变量
#ifndef __cplusplus//C++语言不需要此操作
typedef unsigned char bool;
## define true 1
## define false 0
#endif


#include <SDL2/SDL.h>
#include <stdio.h>


#define printf(...) fprintf(stderr,__VA_ARGS__)
#define puts(anything) fputs(anything,stderr)


//屏幕分辨率
#define SCREEN_WIDTH  960
#define SCREEN_HEIGHT 540


#define BMP_FILE_NAME "bmp/hello.bmp"


//准备工作
bool mySdlInit();
//加载媒体
bool mySdlLoadMedia();
//释放SDL的堆内存
void mySdlClose();


//想要创建的窗口
SDL_Window* window = NULL;


//窗口surface
SDL_Surface* screenSurface = NULL;
//图片surface(另创建的)
SDL_Surface* pictureSurface = NULL;


bool mySdlInit()
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("SDL_Init Error: %s\n", SDL_GetError());
        return false;
    }


    window = SDL_CreateWindow("SDL教程 - 显示一张图片", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_VULKAN | SDL_WINDOW_ALWAYS_ON_TOP);
    if (!window)
    {
        printf("SDL_CreateWindow Error: %s\n", SDL_GetError());
        return false;
    }


    screenSurface = SDL_GetWindowSurface(window);
    if (!screenSurface)
    {
        printf("SDL_GetWindowSurface Error: %s\n", SDL_GetError());
        return false;
    }
    return true;
}


bool mySdlLoadMedia()
{
    //加载 BMP 文件
    pictureSurface = SDL_LoadBMP(BMP_FILE_NAME);
    if (!pictureSurface)
    {
        printf("Unable to load image: " BMP_FILE_NAME "SDL_LoadBMP Error: %s\n", SDL_GetError());
        return false;
    }


    return true;
}


void mySdlClose()
{
    //析构 pictureSurface 对象
    SDL_FreeSurface(pictureSurface);
    //将已经释放完毕的指针指向空白
    pictureSurface = NULL;


    //析构 window
    SDL_DestroyWindow(window);
    window = NULL;


    //退出 SDL
    SDL_Quit();
}


int main(int argc, char* args[])
{
    //启动SDL并创建一个窗口
    if (!mySdlInit())
    {
        printf("Failed to initialize!\n");
        return -1;
    }
    //加载媒体文件
    if (!mySdlLoadMedia())
    {
        printf("Failed to load media!\n");
        return -1;
    }
    //将图片 surface 粘贴到原来的 surface 上
    SDL_BlitSurface(pictureSurface, NULL, screenSurface, NULL);


    SDL_UpdateWindowSurface(window);


    SDL_Delay(10000);


    //释放堆内存并关闭 SDL
    mySdlClose();


    return 0;
}
案例一、你的第一个SDL2程序:创建一个窗口
案例三、事件驱动式编程
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }