Chapter 2. Graphics and Video

Table of Contents
Introduction to SDL Video
Using OpenGL With SDL

Introduction to SDL Video

ビデオはおそらく SDL が使われる最も一般的な部分でしょう。 そのため、最も完成されたサブシステムを持っています。 基本をデモンストレーションするためのいくつかの例を出します。

Initializing the Video Display

これはほとんどすべての SDL プログラムがどうにかして しなければいけないことでしょう。

Example 2-1. ビデオ表示の初期化


    SDL_Surface *screen;

    /* SDL ライブラリを初期化 */
    if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
        fprintf(stderr,
                "SDL が初期化できませんでした: %s\n", SDL_GetError());
        exit(1);
    }

    /* 終了時の後片付け */
    atexit(SDL_Quit);
    
    /*
     * ソフトウェアサーフェスを要求し、
     * 画面を 640x480 パレットモードで初期化
     */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
    if ( screen == NULL ) {
        fprintf(stderr, "640x480x8 のビデオモードに設定できませんでした: %s\n",
                        SDL_GetError());
        exit(1);
    }

Initializing the Best Video Mode

指定したいピクセル深度があるけれども、他でもよい場合は、 以下のように SDL_ANYFORMAT をつけて SDL_SetVideoMode を使います。 要求されるものに最も近い、ハードウェアがサポートするビデオモードを 見つけるために SDL_VideoModeOK() を使うこともできます。

Example 2-2. 最良のビデオモードの初期化


    /* 8-bit を指定したいが、どんな深度でも可能 */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
    if ( screen == NULL ) {
        fprintf(stderr, "640x480x8 のビデオモードに設定できませんでした: %s\n",
                        SDL_GetError());
        exit(1);
    }
    printf("640x480 at %d bpp のモードで 640x480 に設定しました\n",
           screen->format->BitsPerPixel);

Loading and Displaying a BMP File

ひとたび SDL が初期化されてビデオモードがセットされたら、 次に挙げる関数は指定された引数の BMP ファイルをロードして表示します。

Example 2-3. BMP ファイルのロードと表示


void display_bmp(char *file_name)
{
    SDL_Surface *image;

    /* BMP ファイルをサーフェスにロードする */
    image = SDL_LoadBMP(file_name);
    if (image == NULL) {
        fprintf(stderr, "%s: %s がロードできませんでした\n", file_name, SDL_GetError());
        return;
    }

    /*
     * パレット画面モードはデフォルトパレットを持つ(標準の 8*8*4 色立体)が、
     * 画像もパレット表現ならば、より良く色を合わせるため
     * そのパレットを使うことができる
     */
    if (image->format->palette && screen->format->palette) {
    SDL_SetColors(screen, image->format->palette->colors, 0,
                  image->format->palette->ncolors);
    }

    /* Blit onto the screen surface */
    if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)
        fprintf(stderr, "BlitSurface のエラー: %s\n", SDL_GetError());

    SDL_UpdateRect(screen, 0, 0, image->w, image->h);

    /* 確保した BMP サーフェスを解放 */
    SDL_FreeSurface(image);
}

Drawing Directly to the Display

以下の 2 つの関数はサーフェスの 1 ピクセルを取得/書込するために使われます。 それらは SDL によって現在サポートされているどんなピクセル深度においても動くよう 注意深く書き込まれます。

ピクセル値とその赤・緑・青チャンネルの値とを変換するためには、 SDL_GetRGB() と SDL_MapRGB() を使います。

Example 2-4. getpixel()


/*
 * (x, y) におけるピクセルを返す。
 * 注意: これを呼ぶ前にサーフェスがロックされている必要がある!
 */
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    /* この p は取得したいピクセルを指すアドレス */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        return *p;

    case 2:
        return *(Uint16 *)p;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;

    case 4:
        return *(Uint32 *)p;

    default:
        return 0;       /* 起こるはずがないが、ワーニングを回避 */
    }
}

Example 2-5. putpixel()


/*
 * (x, y) におけるピクセルに指定された値をセットする。
 * 注意: これを呼ぶ前にサーフェスがロックされている必要がある!
 */
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* この p は取得したいピクセルを指すアドレス */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

以下のコードは、画面の中央に黄色いピクセルを書込むために、 上記の putpixel() 関数を使っています。

Example 2-6. putpixel() を使う


    /* Code to set a yellow pixel at the center of the screen */	

    int x, y;
    Uint32 yellow;

    /* 黄色をこの画面に写像する (R=0xff, G=0xFF, B=0x00)
       注意: 画面がパレットで表現されているなら、まずパレットをセットする必要がある
    */
    yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);

    x = screen->w / 2;
    y = screen->h / 2;

    /* ピクセルへの直接アクセスのため画面をロック */ 
    if ( SDL_MUSTLOCK(screen) ) {
        if ( SDL_LockSurface(screen) < 0 ) {
            fprintf(stderr, "画面のロックができません: %s\n", SDL_GetError());
            return;
        }
    }

    putpixel(screen, x, y, yellow);

    if ( SDL_MUSTLOCK(screen) ) {
        SDL_UnlockSurface(screen);
    }
    /* 画面の書き換えた部分だけを更新 */
    SDL_UpdateRect(screen, x, y, 1, 1);

    return;