跳轉到內容

SDL (簡單直接媒體層) - 渲染影像

來自華夏公益教科書,開放世界開放書籍

在本節中,我們將演示如何將 BMP 影像渲染到視窗。在本章中,我們將開始在多個檔案中編寫程式碼以保持專案的組織性。

您可以在這個 GitLab 儲存庫 中下載本節的原始碼。所有原始碼都儲存在 該組 中。

wikibook.h

[編輯 | 編輯原始碼]
#include <stdio.h>

#ifdef _WIN32
#include <SDL/SDL.h> /* Windows-specific SDL2 library */
#else
#include <SDL2/SDL.h> /* macOS- and GNU/Linux-specific */
#endif

/* Define constants for struct wikibook */
#define WB_NAME "WikiBook SDL"
#define WB_WIDTH 500
#define WB_HEIGHT 500
#define WB_IMAGE_PATH "wikibooks.bmp" /* File path to image relative to binary */

/* 
 * We'll be encapsulating the SDL objects into struct wikibook and write up 
 * functions into useable subroutines. Remember, to initialise wikibook as static
 * to make sure that the member pointers are declared to NULL. */
struct wikibook {
  SDL_Window *window; /* Window to be rendered */
  SDL_Surface *screen; /* Surface contained by the window */
  SDL_Surface *image; /* Surface to be loaded with the image */
};

/* Function prototypes for struct wikibook */
int wb_init(struct wikibook*);
int wb_loadImage(struct wikibook*);
void wb_close(struct wikibook*);

我們宣告一個 SDL_Window 和兩個 SDL_Surface 指標。我們使用第一個作為它被渲染到視窗,第二個作為載入影像的緩衝區。

SDL_Window *window; /* Window to be rendered */
SDL_Surface *screen; /* Surface contained by the window */
SDL_Surface *image; /* Surface to be loaded with the image */

wikibook.c

[編輯 | 編輯原始碼]

初始化

[編輯 | 編輯原始碼]
/* Initialise wikibook object */
int wb_init(struct wikibook *wb) {

  /* Initialise SDL video subsystem */
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    fprintf(stderr, "SDL failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  /* Create SDL_Window */
  wb->window = SDL_CreateWindow(WB_NAME,
			SDL_WINDOWPOS_UNDEFINED,
			SDL_WINDOWPOS_UNDEFINED,
			WB_WIDTH,
			WB_HEIGHT,
			0);

  if (wb->window == NULL) {
    fprintf(stderr, "WikiBook window failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  /* Get SDL_Surface of SDL_Window */
  wb->screen = SDL_GetWindowSurface(wb->window);

  if (wb->screen == NULL) {
    fprintf(stderr, "WikiBook screen failed to initialise: %s\n", SDL_GetError());
    return -1;
  }

  return 0;
}

我們使用 SDL_GetWindowSurfaceSDL_Window 設定 SDL_Surface

wb->screen = SDL_GetWindowSurface(wb->window);

if (wb->screen == NULL) {
  fprintf(stderr, "WikiBook screen failed to initialise: %s\n", SDL_GetError());
  return -1;
}

影像渲染

[編輯 | 編輯原始碼]
/* Load and render the image to wikibook object */
int wb_loadImage (struct wikibook *wb) {

  /* Load the image from its file path to a SDL_Surface */
  wb->image = SDL_LoadBMP(WB_IMAGE_PATH);

  if (wb->image == NULL) {
    fprintf(stderr, "WikiBook failed to load image: %s\n", SDL_GetError());
    return -1;
  }

  /* Blit the SDL_Surface, containing the image, to the SDL_Surface contained by
   * the SDL_Window  */
  SDL_BlitSurface(wb->image, NULL, wb->screen, NULL);

  /* Update SDL_Window so it shows the updated SDL_Surface therefore the image */
  SDL_UpdateWindowSurface(wb->window);
  
  return 0;
}

我們首先使用 SDL_LoadBMP 將 BMP 影像載入到 SDL_Surface 中。

wb->image = SDL_LoadBMP(WB_IMAGE_PATH);

if (wb->image == NULL) {
  fprintf(stderr, "WikiBook failed to load image: %s\n", SDL_GetError());
  return -1;
}

現在我們已經將影像載入到它的表面,我們將需要使用 SDL_BlitSurface 將其繪製到另一個表面,即視窗中包含的表面。然後,我們將不得不使用 SDL_UpdateWindowSurface 更新視窗以渲染影像。

/* Blit the SDL_Surface, containing the image, to the SDL_Surface contained by
 * the SDL_Window  */
SDL_BlitSurface(wb->image, NULL, wb->screen, NULL);

/* Update SDL_Window so it shows the updated SDL_Surface therefore the image */
SDL_UpdateWindowSurface(wb->window);

釋放記憶體

[編輯 | 編輯原始碼]
/* Free the memory of wikibook's member objects */
void wb_close(struct wikibook *wb) {

  /* Destroy surfaces */
  SDL_FreeSurface(wb->image);
  SDL_FreeSurface(wb->surface);
  wb->image = NULL;
  wb->surface = NULL;

  /* Destroy window */
  SDL_DestroyWindow(wb->window);
  wb->window = NULL;

  /* Shutdown SDL subsystems*/
  SDL_Quit();
}

銷燬表面與銷燬視窗幾乎相同。

#define TIME_DELAY 3000

int main (int argc, char **argv)
{
  static struct wikibook wb; /* Initialise as static as to initialise all members to 0 */

  if (wb_init(&wb) < 0) {
    fprintf(stderr, "WikiBook failed to initialise.");
    return -1;
  }

  if (wb_loadImage(&wb) < 0) {
    fprintf(stderr, "WikiBook failed to load image.");
    return -1;
  }

  SDL_Delay(TIME_DELAY);

  wb_close(&wb);

  return 0;
}

我們將時間延遲定義為 3000 毫秒,並將 wikibook 物件宣告為 static,確保其成員指標設定為 NULL。我們執行相應的成員函式,就是這樣!

華夏公益教科書