现在的位置: 首页 > 综合 > 正文

【第二课】SDL读取并显示图像

2014年01月24日 ⁄ 综合 ⁄ 共 5672字 ⁄ 字号 评论关闭

再次欢迎大家来到这个小巧的指导教程,版本号仅仅比上个单元高一个点。在本单元中,大家将会学到怎样在您的SDL程序、游戏或者是效果展示等实例中载入并显示图像文件。学习过程中,我将带大家几乎逐行通读整个源代码文件,在阅读过程中,我会向大家介绍代码的每一部分都做了什么。当然,今天我们使用的例子是非常简单易懂的,首先绘制一个漂亮的背景,然后放上一个可以用键盘控制移动的方块。

我们还是先从这三个头文件开始吧stdio.h, stdlib.h 还有 SDL.h (这个stdlib.h稍后在使用atexit()函数时会用到)

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

在这之后我们就可以定义3个全局的 SDL_Surface(译者注:上篇里面提到了SDL_Surface即指针) ,全局意味着这些变量可以被代码中所有的函数拿来使用,要定义一个全局的东东,我们需要把它的定义放在整个源代码文件的顶部,函数之前。在定义完SDL_Surface之后,我们继续定义两个整形全局变量:xpos 和 ypos。在定位方块的时候会用到。

SDL_Surface *back;
SDL_Surface *image;
SDL_Surface *screen;

int xpos=0,ypos=0;

初始化图形函数用于从位图(bmp)中读取整个图像到SDL_Surface,InitImages()函数将由main函数稍后调用,在InitImages()函数中,我们要用到SDL_LoadBMP函数,SDL_LoadBMP 将文件名作为输入参数,返回一个SDL_Surface类型的指向该文件包含图像数据的指针,在这里,我们读取两幅bmp位图:将bg.bmp读入全局SDL_Surface back 以便稍后绘制背景,另外一张image.bmp读入后用于绘制盒子。

int InitImages()
{
  back = SDL_LoadBMP("bg.bmp");
  image = SDL_LoadBMP("image.bmp");
  return 0;
}

然后就要将读入的图像复移(复移指将一个平面的一部分或全部图象整块从这个平面复制到另一个平面;英: blit)到screen.两个函数都叫做DrawIMG。 第一个DrawIMG函数有三个参数:用于复移的SDL_Surface和用于确定移动位置的x,y坐标。然后我们使用函数SDL_BlitSurface()来将图像复移到screen surface. 在SDL文档里摘抄出来的函数SDL_BlitSurface()的原型如下:

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);

参数src是源复移平面(),参数dst是目的复移的平面。 SDL_BlitSurface()还拥有两个定义为SDL_Rect类型的参数: srcrect 和 dstrect. SDL_Rect 是一个包含四个16位整数x,y,w(宽度)和h(高度)的结构体类型. srcrect 用于指定源复移平面用于复移的区域(源复移区域)而dstrect指定移到目的复移平面的位置(目的区域). 如果指定第二项参数位NULL,那么整个源复移平面将全部被复移.dstrect结构体中的的 x 和 y 指定了源复移平面的目的区域而结构体中的w和h参数将被忽略,下面是我们的第一个DrawIMG
函数: void DrawIMG(SDL_Surface *img, int x, int y)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_BlitSurface(img, NULL, screen, &dest);
}

第二个DrawIMG函数将使用srcrect。srcrect结构体中的 x and y 指定了从哪里开始进行复移而结构体重的w和h指定了复移多少。

第二个DrawIMG函数用于将源复移平面的部分内容复移到屏幕(screen)上面。 让我们来看看他,并且试着理解下到底发生了什么。

void DrawIMG(SDL_Surface *img, int x, int y, int w, int h, int x2, int y2)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_Rect dest2;
  dest2.x = x2;
  dest2.y = y2;
  dest2.w = w;
  dest2.h = h;
  SDL_BlitSurface(img, &dest2, screen, &dest);
}

现在到了介绍函数DrawBG()的时候了。 为了拥有一个漂亮的绘图背景,我们必须让main函数在进入main循环(有关main循环参看上节)之前调用DrawBG函数。要注意的是将图片复移到屏幕上时我们不需要锁定屏幕。之所以如此,是因为只有当我们需要人工直接操作像素点的时候(例如上例中的在屏幕上绘制像素点)才需要锁定屏幕。然后我们就要吧背景平面复移到屏幕上来啦。这里要注意的是这次我们没有任何的屏幕更新(Update)函数(如SDL_Flip())。这是因为此函数仅仅是为了让我们有一个漂亮的绘画背景,我们根本不像让用户在程序的一开始看到一个没有任何衬饰的背景。
注意:你完全可以复移一个SDL_Surface到另外一个SDL_Surface,而不仅仅是到屏幕,你也可以编辑修改DrawIMG函数让他们复移到另外一个平面。

void DrawBG()
{
  DrawIMG(back, 0, 0);
}

首先,我们绘制出我们所在的背景部分,如果我们不这么做的话,在我们移动了我们的方块之后屏幕将会在他的移动轨迹上留下一大片空白。然而我们并不需要重绘整个背景,那样的话太慢了,我们仅仅绘制我们需要绘制的部分——绘制块方形的背景起点处要比方块高2像素且偏左2像素,终点要比方块低两像素且偏右两像素(其实就是绘制一个比方块四周都要大两像素的方形背景)。既然方块每次在每个方向只能移动一个像素,那么明显的,我们绘制一个两像素大于方块的背景就可以清除方块的路径了。因为方块是128X128像素的,那么方形背景就是 (128+2+2)x(128+2+2)
= 132x132 像素大小。注释掉第一个(绘制背景) DrawIMG 函数可以查看方块的轨迹。第二个DrawIMG函数仅仅是在x,y坐标绘制方块。使用SDL_Flip函数来弹出缓冲区(用大家所熟悉的方式说就是 更新屏幕)。

void DrawScene()
{
  DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);
  DrawIMG(image, xpos, ypos);

  SDL_Flip(screen);
}

最后剩下的就是main函数了,首先我们创建一个指向无符号8位整数的指针,在这里,我们不妨把它叫做“按键指示”,稍后在给定的时间内,我们用它来表示键盘按键状态。

int main(int argc, char *argv[])
{
  Uint8* keys;
 然后我们就要开始标准的SDL初始化工作了(什么?你不知道?去查看下上一课吧)。   if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
  {
    printf("Unable to init SDL: %s/n", SDL_GetError());
    exit(1);
  }
  atexit(SDL_Quit);

  screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
  if ( screen == NULL )
  {
    printf("Unable to set 640x480 video: %s/n", SDL_GetError());
    exit(1);
  }
 下面我们初始化所有的图片(就是把他们读入SDL_Surfaces)。完成后再绘制背景。记住,绘制背景无须更新(显示器)屏幕,但是它会让我们接下来用于绘制工作的SDL_Surface屏幕像一张漂亮的图片一样。     InitImages();
  DrawBG();

现在像上一节课提到的一样我们开始设计main循环并检测退出事件或ESC键的状态。   int done=0;

  while(done == 0)
  {
    SDL_Event event;

    while ( SDL_PollEvent(&event) )
    {
      if ( event.type == SDL_QUIT )  {  done = 1;  }

      if ( event.type == SDL_KEYDOWN )
      {
        if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
      }
    }
 现在我们来获得键盘按键的状态,SDL_GetKeyState()函数返回一个指向Uint8数组的指针,数组中的每个元素包含了一个特殊按键的信息(它按下与否)。之所以我们不在消息循环中检测是因为编写main循环是用来检测一个键是否按下过,而不是检测它是否一直被按着。现在我们检测←、→、↓、↑ 是否被压下。如果被按下, 我们就把方块左移、右移、下移或者上移一格。因为可能会同时按下许多按键,这次我们就不用“else if”组了。     keys = SDL_GetKeyState(NULL);
    if ( keys[SDLK_UP] ) { ypos -= 1; }
    if ( keys[SDLK_DOWN] ) { ypos += 1; }
    if ( keys[SDLK_LEFT] ) { xpos -= 1; }
    if ( keys[SDLK_RIGHT] ) { xpos += 1; }
 现在我们来绘制屏幕。      DrawScene();
  }

循环结束的时候我们就该结束程序咯。    return 0;
}

这就对了!现在你可能还不是一个功夫型的复移高手,但是你(我希望是)确确实实的掌握了基础内容。
 
全部程序如下(运行时把两张图盘bg.bmp(640X480)、image.bmp(128X128)放到exe目录下):

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

SDL_Surface *back; SDL_Surface *image;
SDL_Surface *screen;

int xpos=0,ypos=0;

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
                        SDL_Surface *dst, SDL_Rect *dstrect);
int InitImages()
{
  back = SDL_LoadBMP("bg.bmp");
  image = SDL_LoadBMP("image.bmp");
  return 0;
}

void DrawIMG(SDL_Surface *img, int x, int y)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_BlitSurface(img, NULL, screen, &dest);
}
void DrawIMG(SDL_Surface *img, int x, int y,
                                int w, int h, int x2, int y2)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_Rect dest2;
  dest2.x = x2;
  dest2.y = y2;
  dest2.w = w;
  dest2.h = h;
  SDL_BlitSurface(img, &dest2, screen, &dest);
}
void DrawBG()
{
  DrawIMG(back, 0, 0);
}
void DrawScene()
{
  DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);
  DrawIMG(image, xpos, ypos);

  SDL_Flip(screen);
}

int main(int argc, char *argv[])
{
  Uint8* keys; if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
  {
    printf("Unable to init SDL: %s/n", SDL_GetError());
    exit(1);
  }
  atexit(SDL_Quit);

  screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
  if ( screen == NULL )
  {
    printf("Unable to set 640x480 video: %s/n", SDL_GetError());
    exit(1);
  }
   int done=0;

  while(done == 0)
  {
    SDL_Event event;

    while ( SDL_PollEvent(&event) )
    {
      if ( event.type == SDL_QUIT ) { done = 1; }

      if ( event.type == SDL_KEYDOWN )
      {
        if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
      }
    }
    keys = SDL_GetKeyState(NULL);
    if ( keys[SDLK_UP] ) { ypos -= 1; }
    if ( keys[SDLK_DOWN] ) { ypos += 1; }
    if ( keys[SDLK_LEFT] ) { xpos -= 1; }
    if ( keys[SDLK_RIGHT] ) { xpos += 1; }
     DrawScene();
  } return 0;
}

抱歉!评论已关闭.