SDL第二课画面的载入和位图传输的优化

来源:互联网 发布:淘宝店铺总流量怎么看 编辑:程序博客网 时间:2024/04/29 14:38

画面的载入和位图传输的优化

 

最后更新时间:2009/12/28

在上一课,我们已经可以在屏幕上显示一张图片。接下来,我们将采用更有效的方法载入画面和传输位图。

//头文件

#include "SDL/SDL.h"

#include <string>

/*这是这个程序的头文件。包含SDL.h头文件明显的是因为我们要使用SDL的函数,使用string头文件是因为……呃,只是比起char*来,我更喜欢std::string*/

 

//设置屏幕属性

const int SCREEN_WIDTH = 640;

const int SCREEN_HEIGHT = 480;

const int SCREEN_BPP = 32;

/*这些是屏幕的各种属性。我完全相信你可以猜出SCREEN_WIDTH SCREEN_HEIGHT表示的是什么。SCREEN_BPP是像素的位数。在所有的教程里,我们使用32位色。*/

 

//声明将要使用的画面

SDL_Surface *message = NULL;

SDL_Surface *background = NULL;

SDL_Surface *screen = NULL;

/*这些是将要使用的3个图片的声明。"background"明显将作为背景图片, "message" 是一个显示“Hello”的位图,而 "screen"明显是一个屏幕。

牢记:如果指针没有指向目标时,最好的方法是让它们指向NULL*/

 

SDL_Surface *load_image( std::string filename )

{

    //临时储存载入的图片

    SDL_Surface* loadedImage = NULL;

   

    //将使用的优化的图片

SDL_Surface* optimizedImage = NULL;

/*这里,我们使用图片加载函数。这个函数用来载入图片,返回一个指针,用来优化加载图像版本。参数"filename"是图片加载的路径,"loadedImage"是我们加载图片时的画面。"optimizedImage"是我们将要使用的画面。*/

 

    //加载图片

    loadedImage = SDL_LoadBMP( filename.c_str() );

/*首先用SDL_LoadBMP()加载图片。但是不能立即使用,因为位图是24位,而屏幕是32位的。把格式不同的画面加载到另外一个画面上并不是一个好的方法,因为SDL必须在程序运行中实时的转化格式,导致运行缓慢。*/

 

    //如果加载图片的过程中没有任何错误

    if( loadedImage != NULL )

    {

        //Create an optimized image

        optimizedImage = SDL_DisplayFormat( loadedImage );

       

        //Free the old image

        SDL_FreeSurface( loadedImage );

    }

/*下一步,我们检查图片是否正确的载入,如果出错,loadedImage将为NULL。如果图片载入正确,调用SDL_DisplayFormat()创建一个新版本的"loadedImage",格式与屏幕相同。我们这么做的原因是因为我们把不同格式的画面粘贴到另一个画面上,SDL将它们转化为相同的格式。每次传输位图的时候,创建转化后的画面会占用进程影响速度。因此我们在加载画面时,转化画面的格式。当你想把画面显示到屏幕上时,画面已经转化和屏幕同样的格式。这样SDL就不需要进行实时转化了。所以,我们有两个画面,一个之前加载的图片,一个新的优化后的图片。

 

SDL_DisplayFormat()创建一个新的优化后的画面,但是并没有出去之前的画面。因此,我们需要调用SDL_FreeSurface()除去之前加载的图片。*/

 

    //返回优化后的图片指针

    return optimizedImage;

}

/*然后,返回加载新的优化了的版本的图片。*/

 

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination )

{

    //用一个临时的矩形来储存偏移位置

    SDL_Rect offset;

   

    //设置矩形的偏移位置

    offset.x = x;

    offset.y = y;

/*这里,我们用画面的位图传输函数。包括你要传输画面的坐标,你要传输的画面以及要传输到的目的画面。首先,我们采用偏移量,偏移范围在一个SDL_Rect矩形结构内。因为SDL_BlitSurface()函数只接受SDL_Rect矩形结构范围内的偏移量。一个SDL_Rect矩形结构是一个结构数据类型,表示一个矩形。它有四个成员,分别代表XY的偏移量,矩形的宽和高。这里,我们只使用xy的数据成员。*/

 

    //传输画面

    SDL_BlitSurface( source, NULL, destination, &offset );

}

/*这里,我们调用SDL_BlitSurface()函数,真正的传输画面。第一个参数是我们使用的画面(源画面),别着急,第二个参数我们目前只是设置为NULL。第三个参数是我们要传输到的目的画面(把源画面传输到目的画面上)。第四个参数是源画面放置到目的画面上的偏移位置。*/

 

int main( int argc, char* args[] )

{

/*现在开始main函数,使用SDL的时候,我们要一直使用int main( int argc, char* args[] )或者int main( int argc, char** args )来声明main函数,使用int main(), void main()或其他的声明,程序都无法编译。*/

    //初始化SDL 子系统

    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )

    {

        return 1;   

    }

/*我们调用SDL_Init()函数,启动SDL。采用SDL_INIT_EVERYTHING作为参数调用SDL_Init(),能都初始化所有的SDL子系统。SDL子系统包括:视频、音频、定时器等等,包括用来制作游戏的特殊引擎。虽然我们不打算使用所有的子系统,但是初始化了它们也对我们没有什么影响。如果SDL不能初始化,会返回-1,这时,结束程序,并返回1来进行出错处理。*/

 

    //新建屏幕

    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

/*开始生成一个窗口,并返回窗口画面的指针screen,这样我们才能把图片传输到屏幕screen上。我们已经知道前3个参数的作用。第四个参数在系统内存中创建一个屏幕画面。*/

 

    //如果新建屏幕的过程中出错

    if( screen == NULL )

    {

        return 1;   

    }

/*如果在生成屏幕screen时有突发的问题,screen就会被设置为NULL*/

 

    //设置窗口的标题

    SDL_WM_SetCaption( "Hello World", NULL );

/*设置标题为:“Hello World”,标题就是窗口的这部分:

 

 

    //加载所有图片

    message = load_image( "hello.bmp" );

    background = load_image( "background.bmp" );

/*我们用我们构造的加载函数来加载图片。*/

 

    //把背景图片放置到屏幕上

    apply_surface( 0, 0, background, screen );

/*用我们构造的函数来放置背景图片。在传输背景图片之前,屏幕看起来这样:

 

但是我们传输背景图片以后,屏幕在内存中看起来是这样:

 

当你开始传输的时候,从一个画面复制像素到另一个画面上。因此现在背景图片显示在屏幕的左上角。但是我们要填满整个屏幕。这是不是意味我们要再载入3次图片?*/

    apply_surface( 320, 0, background, screen );

    apply_surface( 0, 240, background, screen );

    apply_surface( 320, 240, background, screen );

/*是的,我们只能再载入3次相同的画面*/

 

    //把消息图片放置到屏幕上

    apply_surface( 180, 140, message, screen );

/*我们将把message画面放置到screen画面上,位置是:x偏移180y偏移140SDL的坐标系不是这样的:

 

SDL的坐标系是这样的:

 

所以,原点(0,0)在左上角而不是左下角。所以,当你传输message画面时,将传输到与左上角原点相对向右偏移180像素,向下偏移140像素。

 

SDL的坐标系最初用起来会不方便,但是相信你会习惯它的。 */

    //刷新屏幕

    if( SDL_Flip( screen ) == -1 )

    {

        return 1;   

    }

/*尽管放置了我们的画面,可是屏幕依然是黑的。现在我们调用SDL_Flip()来刷新屏幕,这样内存中的画面才会显示到屏幕上。如果出错,将返回-1*/

 

    //等待2

    SDL_Delay( 2000 );

/*我们调用SDL_Delay()函数来避免屏幕一闪而过,SDL_Delay()的参数是微秒,或者说千分之一秒。所以窗口将会停留2000/1000=2秒。*/

 

    //释放画面内存

    SDL_FreeSurface( message );

    SDL_FreeSurface( background );

   

    //退出SDL

    SDL_Quit();

   

    //返回0

    return 0;

}

/*我们在程序结束时做清理工作。调用SDL_FreeSurface()来清除我们不再使用的画面。如果不释放我们使用的内存,会导致内存泄露。调用SDL_Quit()来退出SDL。然后返回0,结束程序。你也许会问:“为什么不是放内屏幕的画面?”。不用担心,因为SDL_Quit()会为我们做这些工作。

如果你运行程序,并没有显示图片或者窗口一闪而过,在stderr.txt里会发现:

Fatal signal: Segmentation Fault (SDL Parachute Deployed)

这是因为程序访问了不允许访问的内存。可能是调用apply_surface()时,试图访问NULL。这意味着你要检查位图文件是否和程序在同一个目录下面。如果弹出窗口,却没有显示图片,再确认一次,位图文件是否和程序在同一个文件夹内或者有没有在工程所在的目录里。*/

原创粉丝点击