SDL2の初期化処理、ウィンドウ作成、描画処理


Table of Contents

 

1 SDL_Init

SDL_InitでSDLを初期化します。

int SDL_Init(Uint32 flags)

flagsにシステムに対応した値を設定することで、システムを初期化します。値にはSDL_INIT_VIDEO、SDL_INIT_EVENTS等があります。

SDL_INIT_VIDEOで初期化することで描画処理が実行可能になります。SDL_INIT_EVENTSで初期化することで入力処理が実行可能となります。

全てのシステムを有効にするSDL_INIT_EVERYTHINGがあり、通常はこちらを用いれば良いと思います。

もし、初期化を段階的に分けたい場合は、初期化処理の1つ目はSDL_Initを呼び出し、初期化処理の2つ目以降はSDL_InitSubSystemを呼び出します。

 

2 SDL_Quit

SDL_Quitで初期化したシステムを終了します。

void SDL_Quit(void)

段階的にシステムを終了する場合はSDL_QuitSubSystemを用います。通常はSDL_Quitのみを用いれば良いでしょう。

 

3 SDL_Window構造体

ウィンドウを管理する構造体です。

3.1 SDL_CreateWindow

SDL_CreateWindowでウィンドウを作成します。

SDL_Window* SDL_CreateWindow(const char* title, int x, int y, int w, int h, Uint32 flags)

開始位置の座標x, yにはSDL_WINDOWPOS_UNDEFINEDを指定でき、開始位置をシステム任せにすることが可能です。flagsにはサイズ変更可能かどうか、フルスクリーンにするかどうか等のフラグを設定します。作成するアプリケーションに合わせてフラグを設定することになります。

SDL_CreateWindowでSDL_Window構造体を作成後、終了処理まではSDL_Window構造体が必要になることはないでしょう。

 

3.2 SDL_DestroyWindow

SDL_DestroyWindowでウィンドウを破棄します。

void SDL_DestroyWindow(SDL_Window* window)

4 SDL_Renderer構造体

SDL_Renderer構造体はSDL2から追加された構造体で、描画処理を担うものです。

SDLでは以前からSDL_Window構造体が持つSDL_Surface構造体に対して描画対象をコピーすることで描画処理を実現していました(BMP画像のSDL_Surface構造体を作り、SDL_Window構造体のSDL_Surface構造体にコピーする)。

 

SDL2からSDL_Surface構造体の代わりにSDL_Texture構造体が描画処理に使われるようになりました。大きな違いはSDL_Surface構造体はメンバ変数を外部からアクセスできるのに対し、SDL_Texture構造体はアクセスできません。

 

これはSDL_Texture構造体のメンバ変数の振る舞いはSDLライブラリのコンパイル時に決定し(例えばSDL2を搭載したシステムがOpenGLを使ってる場合とDirectx使っている場合で扱いが異なる)、システムに合わせて最適化する為です。

 

よって、外部からメンバにアクセスすると不整合が起きる為、外部にメンバ変数を公開しておりません。逆に公開していた場合は、アプリケーション実行時(コンパイル時ではない)にSDL2ライブラリの状況を詳しく調べる必要が出てしまい、コードが増加してしまいます。システムがOpenGLを使おうが、Directxを使っていようが、同じソースコードでアプリケーションを構築可能というSDLの利点と矛盾します。

 

SDL_Renderer構造体はSDL_Texture構造体に対応したものです。SDL2からは、BMP画像のSDL_Texture構造体を作成し、SDL_Renderer構造体にコピーすることで描画を実現します。

 

4.1 SDL_CreateRenderer

SDL_CreateRendererにSDL_Window構造体を渡すことでSDL_Renderer構造体を作成します。

SDL_Renderer* SDL_Cre
ateRenderer(SDL_Window* window, int index, Uint32 flags)

SDL_CreateWindowAndRendererを用いれば、SDL_Window構造体とSDL_Renderer構造体を同時に作成できます。

int SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags, SDL_Window** window, SDL_Renderer** renderer)

ただし、SDL_Renderer構造体の属性を決定するフラグはSDL_CreateRendererのみで設定可能です(サンプルソースコードではSDL_RENDERER_SOFTWAREが必要である為、SDL_CreateRendererを使用しています)。

SDL_Texture構造体を作成する際にもSDL_Renderer構造体は必要となります。 SDL_CreateTextureFromSurfaceでSDL_Surface構造体からSDL_Texture構造体を作成します。

SDL_Texture* SDL_CreateTextureFromSurface(SDL_Renderer* renderer, SDL_Surface* surface)

描画処理はSDL_Renderer構造体の内容クリア、描画対象をSDL_Renderer構造体にコピー、SDL_Renderer構造体の内容を画面に反映、という流れで実行されます。

 

4.2 SDL_RenderClear

SDL_RenderClearでSDL_Renderer構造体の内容をクリアします。

int SDL_RenderClear(SDL_Renderer* renderer)

4.3 SDL_RendererCopy

SDL_RendererCopyでSDL_Texture構造体をSDL_Renderer構造体にコピーし、 SDL_RenderDrawRect等で四角形等の図形をSDL_Renderer構造体にコピーします。

int SDL_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect) int SDL_RenderDrawRect(SDL_Renderer* renderer, const SDL_Rect* rect)

SDL_Textureの回転処理を実行するにはSDL_RenderCopyExを使います。

int SDL_RenderCopyEx(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect, const double angle, const SDL_Point* center, const SDL_RendererFlip flip)

4.4 SDL_RenderPresent

SDL_RenderPresentでSDL_Renderer構造体の内容を画面に反映します。この際、ダブルバッファを使用しているので、画面描画のちらつきは発生しません。

void SDL_RenderPresent(SDL_Renderer* renderer)

よって、SDL2の描画処理の1フレームとは、SDL_RenderClearからSDL_RenderPresentまでの処理のことを表します。

 

4.5 SDL_DestroyRenderer

SDL_DestroyRendererでSDL_Renderer構造体を破棄します。

void SDL_DestroyWindow(SDL_Window* window)

4.6 SDL_Renderer構造体を扱うAPI群は同一スレッドで扱う必要がある

とても重要な点として、SDL_Renderer構造体を扱うAPI群は同一のスレッドである必要があります。

SDL_CreateRendererを実行したスレッドでないと、それ以降のSDLxxxRenderxxx関数を使うことできません。たまたま使えてしまう環境もあるようなので気をつけて下さい。マルチコア環境ではうまく動かない場合が多いです。これはOpenGLの仕様に伴うものだそうです。

よって、全てのコードを単一のスレッドで実行するか、SDL_Renderer構造体を扱うレンダリングスレッドを用意する必要があります。

 

5 サンプルプログラム

本プログラムは640 x 360サイズのウィンドウを作成し、白色の四角形を描画します。10000ミリ秒スリープした後、プログラムを終了します。

#include <SDL.h>#include <iostream>#define SDL_WINDOW_TITLE “SDL2″#define SDL_WINDOW_WIDTH (640)#define SDL_WINDOW_HEIGHT (360)/** NOTE: Windows on KVM cannot call direct3D. Then SDL_RENDERER_SOFTWARE will be needed. */#ifdef NEED_RENDERER_SOFTWARE#define SDL_RENDERER_FLAGS (SDL_RENDERER_SOFTWARE)#else#define SDL_RENDERER_FLAGS (0)#endif#define SDL_PrintError(name) do { std::cerr << #name << “: ” << SDL_GetError() << std::endl; } while (0)/** NOTE: SDL_Renderer is referered with draw function like SDL_RenderDrawRect, SDL_RenderCopy and etc. for drawing. These draw functions and SDL_CreateRenderer must be called by same thread. */static SDL_Window *gWindow;static SDL_Renderer *gRenderer;static bool initialize(){ if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { SDL_PrintError(SDL_Init); return false; } gWindow = SDL_CreateWindow(SDL_WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0); if (gWindow == nullptr) { SDL_PrintError(SDL_CreateWindow); goto err1; } gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_FLAGS); if (gRenderer == nullptr) { SDL_PrintError(SDL_CreateRenderer); goto err2; } return true; err2: SDL_DestroyWindow(gWindow); err1: SDL_Quit(); return false;}static void finalize(){ SDL_DestroyRenderer(gRenderer); SDL_DestroyWindow(gWindow); SDL_Quit();}int main(int argc, char *argv[]){ if (!initialize()) return 1; /** NOTE: Clear render with Color (0, 0, 0, 0) for erasing current drawing. */ SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0); SDL_RenderClear(gRenderer); /** NOTE: Draw rect (0, 0, 100, 100) with Color (254, 254, 254, 254) */ SDL_Rect r
ect = { 0, 0, 100, 100 }; SDL_SetRenderDrawColor(gRenderer, 254, 254, 254, 254); SDL_RenderFillRect(gRenderer, &rect); /** NOTE: Update drawing (switch double buffer). */ SDL_RenderPresent(gRenderer); /** NOTE: Sleep 10000 msec. */ SDL_Delay(10000); finalize(); return 0;}

 

本プログラムを実行すると以下のウィンドウが表示されます。

https://dl-web.dropbox.com/s/27u7nkle20y7d8c/0001_SampleProram.png

Android | Linux | SDL - Narrow Escape