前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用SDL2显示一张图片,SDL2上手贴

使用SDL2显示一张图片,SDL2上手贴

作者头像
俺踏月色而来
发布2018-06-15 11:37:06
1.7K0
发布2018-06-15 11:37:06
举报
文章被收录于专栏:月色的自留地

SDL全名Simple DirectMedia Layer,是一个跨平台的底层音频、视频、键盘、鼠标操作库,操作实际通过更底层的OpenGL/Direct3D完成,在保留跨平台的兼容性之外提供了非常高的效率,所以广泛的应用在多种游戏和对速度敏感的应用中,比如鼎鼎大名的steam平台/ffmpeg/qemu/模拟器等,当前的版本是2.0。更详细的资料可以访问官网:https://www.libsdl.org/。 SDL2的编程理念清晰易用,代码简洁高效,这里用显式一副图片的最简代码来作为入门的示例,正式的教学可以搜索很多国内的教学网站。 老办法,让代码自己来说话:

代码语言:javascript
复制
#include <stdio.h>
//引入SDL头文件
#include <SDL.h>        
//显式bmp之外的图片需要用到sdl_image库,需要单独引入头文件
#include <SDL_image.h>

#define bool int
#define false 0
#define true (!false)

int main(int argc, char ** argv)
{
    bool quit = false;
    SDL_Event event;

     //SDL初始化,这里只显示图片,所以只初始化VIDEO系统,更多的支持查看官方文档
    SDL_Init(SDL_INIT_VIDEO);
    //为了显示png图片,额外使用了图片库,所以要单独初始化
    IMG_Init(IMG_INIT_JPG); 

    //建立SDL窗口
    SDL_Window * window = SDL_CreateWindow("SDL2 Displaying Image",
                SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
    //渲染层
    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
    //如果只是显示一张bmp图片,使用sdl内置的功能即可
    //SDL_Surface * image = SDL_LoadBMP("only_support_BMP.bmp");
    //因为要显示png图片,所以使用了外部库,sdl_image库当前支持jpg/png/webp/tiff图片格式
    SDL_Surface * image = IMG_Load("/Users/andrew/Downloads/webFavorite/3481980_orig.png");
    //载入的图片生成SDL贴图材质
    SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);
 
    while (!quit)
    {//主消息循环
        SDL_WaitEvent(&event);
 
        switch (event.type)
        {   //用户从菜单要求退出程序
            case SDL_QUIT:
                quit = true;
                break;
        }
 
       //如果指定显示位置使用下面注释起来的两句
        //SDL_Rect dstrect = { 5, 5, 320, 240 };
        //SDL_RenderCopy(renderer, texture, NULL, &dstrect);
        //把贴图材质复制到渲染器
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        //显示出来
        SDL_RenderPresent(renderer);
    }
     //典型的三明治结构,清理各项资源
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(image);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    //退出image库
    IMG_Quit();
    //退出SDL
    SDL_Quit();
 
    return 0;
}

代码中基本算是逐句注释,所以读起来应当很清晰。主要需要说明的有两点,但其实跟这段代码并没有直接关系,而是有关在众多的绘图技术、架构、方案中,SDL处于一个什么位置: 1.首先是绘图哲学,使用过OpenGL及Direct3D的看这些代码应当不陌生,但这里要单独给传统GUI绘图的同学多说两句。通常使用GUI绘图,大概是这样一个逻辑,请看伪代码:

代码语言:javascript
复制
准备画板();

画一个点(x,y);
画一条线(x1,y1,x2,y2,c);
画一个圆(x,y,r,c);
贴一张图(x,y,w,h,bmp);

结束绘图();

在伪代码的过程中,每执行一条命令,比如画了线,在屏幕上就会看到结果,然后那条线也会一直存在,直到程序清掉它或者其它屏幕元素遮住它。 而SDL所使用的模式用伪代码表示大致是这样的逻辑:

代码语言:javascript
复制
准备工作();
主循环 {
    游戏逻辑处理();
    界面元素1进场();
    界面元素2进场();
    界面元素n进场();
    渲染();
    是否退出判断();
}

比较主要的区别是,每一个界面元素,或者说屏上的元素进场,并非真正的绘图,只是像拍电影一样准备一个场景。等到所有屏幕元素都到齐,场景完全准备好,再一次性渲染,这时候是真正的绘制到屏幕上。更形象的比喻就好像演员都准备好了,相机快门按下,才真正成像。这个成像称为一帧,随后循环起来,一次次的准备好场景、渲染成像,就形成了连续不断的帧从而形成了帧动画,也就是我们熟悉的屏幕游戏画面。这里面每一秒钟能够进行多少次循环,就成为了游戏玩家熟悉的帧率,追求高帧率是大多游戏玩家对电脑的要求。 这两种绘图的方式,各有优劣,但依据特征,有不同的应用方向。前者多用于打印、绘图输出相关的办公、平面设计等场合,传统软件的界面也多用这种方式,还有比如我们都熟悉的上网浏览器页面也是采用这种渲染方式。这种方式对速度不敏感,虽然有可能硬件加速,但实际上大多工作是由CPU完成的。 后者也就是SDL所采用的方式,则在游戏、视频、3D动画、VR、AR等领域大放异彩,我们耳熟能详的OpenGL、Direct3D也都采用这种方式,这种方式的流程逻辑,也更适合把大量的数据和素材交给GPU去完成更耗时的计算。 显而易见,从绘图哲学的角度看,SDL/OpenGL/Direct3D所采用的绘图方式,显然更适合3D类绘图、动画的加速,那么这种技术对平面绘图,比如就是单纯的视频播放,是如何加速的呢?其实很简单,我们知道所有的3D绘图都包括至少两个主要的部分,一是3D物体的构造模型,比如是一个球体还是一个圆柱体;另一部分则是这个3D物体表面看起来是什么样子的,比如是一个石膏的球体还是一个毛绒玩具的球体。这第二部分就需要用到材质,材质实际上主要是由三维物体的表面积在二维展开的图片。所以3D绘图对二维的加速实际上就是在屏幕上绘制一个全屏幕的平面,然后把二维图像当做材质贴图上去的结果。你看上面SDL代码中载入的png图片,实际最后就是当做一副材质(texture)来使用了。 2.SDL/OpenGL/Direct3D同GTK/MFC/QT/Cocoa是什么关系? 刚才其实比较清楚的讲了SDL/OpenGL/Direct3D在绘图上的作用,其实它们就是一套绘图的体系。 GTK/MFC/QT/Cocoa也是显示相关这没错,但是它们主要是提供用户程序的界面管理、显示及事件处理。更具体一点说,比如你看到屏幕上的菜单、窗口、对话框、按钮、文字,几乎都是这些界面管理器来实现的,我们点了一个按钮、拖动一个窗口,都会产生事件,这些事件会由这些界面管理器收集、分类、排序,调用响应用户响应函数做出最后的处理。所以平常我们所见的应用程序,其实都是基于这一类软件库完成的。而重要的是,这些界面管理库,实际上最终也是经由OpenGL/Direct3D或者类似功能更底层一些的显示绘图库来完成界面部分的绘制功能。但是这些显示系统往往太庞大、臃肿了,对于对速度极为敏感的游戏、视频类应用而言,通常我们见到的这些界面所占比重又比较小,所以游戏类的应用,往往不采用或者较少部分采用这些传统的界面管理库。 这两类系统往往不是独立存在的,比如举例说一个视频播放器,播放器的窗口界面、菜单、文件打开等界面和操作,都是由界面管理器比如Windows上的MFC或者Mac上的Cocoa来完成的,到真正视频播放的环节,在窗口中给定的区域,则是由SDL、OpenGL、Direct3D出马,完成视频的逐帧绘制的功能。

回到今天的主题。上面的代码在编译的时候,因为使用了SDL2/SDL_image两个额外的附加库,所以在编译、执行代码之前,首先要安装这两个软件库。在mac电脑上安装这两个库的命令是:brew install sdl2 sdl2_image。 编译代码使用:

代码语言:javascript
复制
gcc -o sdlpng sdlpng.c $(pkg-config --cflags --libs sdl2_image)

后面$(pkg-config --cflags --libs sdl2_image)的意思是,将sdl2_image代码库及其依赖库(这里当然就是sdl2库)的编译参数和引用库参数全部显示出来,作为字符串加入到编译命令中去。这个功能是由pkg-config这个包管理器完成的。如果不需要处理png图片,只是bmp图片,则不需要使用sdl2_image库,仅适用sdl2库即可。这个时候可以使用$(pkg-config --cflags --libs sdl2)。sdl2也提供了自己的包参数工具sdl2-config可以完成类似的功能,但仅对自己有效,所以为了通用起见,我们还是使用pkg-config更方便一些。 谈到附加包的编译参数,我们也经常看到一些教科书上写成类似:`pkg-config --cflags --libs sdl2`这样的形式,这是因为在bash下面,反单引号`就是用来执行命令、并将结果当做字符串返回的功能。但是这种方式在别的shell,比如fish中是不起作用的,但是$( ... )这样的方式就有了更好的通用性。

参考链接: http://gigi.nullneuron.net/gigilabs/loading-images-in-sdl2-with-sdl_image/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档