zoukankan      html  css  js  c++  java
  • 使用SDL2显示一张图片,SDL2上手贴


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

    #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绘图,大概是这样一个逻辑,请看伪代码:

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

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

    准备工作();
    主循环 {
    	游戏逻辑处理();
    	界面元素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
    编译代码使用:

    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/

  • 相关阅读:
    JAVA中的继承
    各种浏览器设置背景颜色线性渐变的方式
    JavaScript原型链
    JavaScript运算符
    QQ聊天:终结编程语言和编程职业
    virutal dom patch
    关于编辑器和语言的一些启示
    node-webkit 资源
    我的程序,你的生活
    过早优化是万恶之源
  • 原文地址:https://www.cnblogs.com/andrewwang/p/8635306.html
Copyright © 2011-2022 走看看