Ownway.Info
 
HOMESDL + OpenGLでグラフィックス → フルスクリーン切り替え
フルスクリーン切り替え

球、画像、日本語文字列を描画するで説明した各プログラムに フルスクリーンの切り替え機能を付加する方法について説明する。 特に重要なのはテクスチャの取り扱いである。

以下にこのページで説明するコードを示す。

フルセットには、以下に示す5つのプロジェクトが入っている。

FullScreenSphere球を描画するプログラムにフルスクリーン切り替え機能を付けたもの。
TextureErrorフルスクリーン切り替え機能に問題がある例。
TextureCorrectTextureErrorから問題を取り除いた例。
FullScreenImage画像を描画するプログラムにフルスクリーン切り替え機能を付けたもの。
FullScreenText日本語を描画するプログラムにフルスクリーン切り替え機能を付けたもの。

1: 球を描画するプログラムにフルスクリーン切り替え機能を付ける

この節で説明するコード

■ FullScreenSphere/main.cpp

SDLで画面のモードをフルスクリーンにするには、 SDL_SetVideoModeの第四引数にSDL_FULLSCREEN(もしくはその他のフラグとSDL_FULLSCREENとの論理和)を指定する。 今回の目的はフルスクリーンの切り替えなので、 現在のモードがフルスクリーンモードなのかどうかを調査し、 それによって切り替えを行う。 関連するコードとして、FullScreenSphere/main.cppの一部を以下に示す。

bool initializeVideo(int width, int height, int flags) {
  // ビデオモードの設定をする
  if(0==SDL_SetVideoMode(width, height, 0, flags)) {
    fprintf(stderr, "%s\n", SDL_GetError());
    return false;
  }

  return true;
}

bool initializeOpenGL(int width, int height, int flags) {
  if(!initializeVideo(width, height, flags)) {
    return false;
  }

  // ...
}

// ...

int main(int argc, char** args) {
  // ...

      case SDL_KEYDOWN:
        switch(event.key.keysym.sym) {
        case SDLK_F5:
          {
            SDL_Surface* surface=SDL_GetVideoSurface();
            if((surface->flags & SDL_FULLSCREEN)!=0) {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL);
            } else {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL|SDL_FULLSCREEN);
            }
          }
          break;

  // ...
}

このコードでは、F5キーが押されたときにフルスクリーンの切り替えが起こる。

2: テクスチャに起こる問題

この節で説明するコード

■ TextureError/main.cpp
■ TextureCorrent/main.cpp

しかし、以上のコードをそのままテクスチャを使っているプログラムに使用すると問題が発生する。 TextureError/main.cppは、問題を発生させる例である。 TextureError/main.cppをコンパイルし、 できたプログラムでフルスクリーン切り替えを行う前と後のスクリーンショットを以下に示す。

フルスクリーン切り替えを行った後、テクスチャが無くなってしまった。 これはスクリーンモードの切り替えによって、ビデオメモリが初期化されてしまったからである。 従って、テクスチャを使用しているプログラムでは、 以下の手順に従ってフルスクリーン切り替えを行わなければならない。

  1. 既存のテクスチャをすべて破棄する。
  2. スクリーンモードを切り替える。
  3. テクスチャを再読み込みする。

以上を踏まえた上でTextureError/main.cppを修正したコードとして、 TextureCorrect/main.cppの一部を以下に示す。

int main(int argc, char** args) {
  // ...

      case SDL_KEYDOWN:
        switch(event.key.keysym.sym) {
        case SDLK_F5:
          {
            // テクスチャを解放する
            image=TexturePtr();
            SDL_Surface* surface=SDL_GetVideoSurface();
            if((surface->flags & SDL_FULLSCREEN)!=0) {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL);
            } else {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL|SDL_FULLSCREEN);
            }
            // テクスチャを再び読み込む
            image=Texture::getImage("test.jpg", false, GL_LINEAR);
          }
          break;

  // ...
}
3: テクスチャも考慮に入れたフルスクリーン切り替え

この節で説明するコード

■ FullScreenImage/main.cpp
■ FullScreenText/main.cpp
■ tools/Texture.cpp
■ tools/Texture.hpp
■ tools/texture/ImageTexture.cpp
■ tools/texture/ImageTexture.hpp
■ tools/texture/TextTexture.cpp
■ tools/texture/TextTexture.hpp

一つのプログラムで使用するテクスチャは一般的に複数で、また局面によって使用する枚数も変化することから、 テクスチャに起こる問題で紹介したような個々のテクスチャに対応するような方法は現実的ではない。 そこで使用中のテクスチャを管理する機能をTextureクラスに設けることで、これに対処する。 Textureクラスは、存在するすべてのテクスチャを管理するためのクラスメンバとしてテクスチャのリストを持つ。 関連するコードとして、tools/Texture.hppの一部を以下に示す。

  class Texture {
  private:
    static std::list<Texture*> textures_;

    // ...

  protected:
    /// テクスチャを破棄します。
    void clear();

    /// テクスチャを再読み込みします。
    virtual bool refresh()=0;

  public:
    /// すべてのテクスチャを破棄します。
    static void clearAll();

    /// すべてのテクスチャを再読み込みします。
    static bool refreshAll();

    // ...
  };

また、個々のテクスチャを破棄・再読み込みするclear・refreshメソッドおよび textures_を使ってすべてのテクスチャを破棄・再読み込みするclearAll・refreshAllクラスメソッドを定義する。 再読み込みの方法は、 場合*1 によって異なるのでrefreshメソッドを仮想関数として宣言する。 現在使用中のテクスチャは、Textureクラスのコンストラクタとデストラクタを使用して管理できる。 以下に関連するコードとして、Texture.cppの一部を示す。

  Texture::Texture(bool useAlphablend, GLenum mode) :
  texture_(0), useAlphablend_(useAlphablend), mode_(mode) {
    iterator_=textures_.insert(textures_.end(), this);
  }

  Texture::~Texture() {
    textures_.erase(iterator_);

    clear();
  }

iterator_は、リスト内での自分自身の位置を示す反復子を記憶するためのメンバである。 std::listの反復子は不揮発性であり、また反復子を使用した削除はO(1)で処理されることから、 オーバーヘッドを最小限に抑えて、使用中のテクスチャリストを管理できる。

この機構によって使用中のテクスチャリストが正常に取得できるため、 clearAllとrefreshAllはこのテクスチャリストを使用して全テクスチャの破棄と再読み込みを行えばよい。 以下にTextureクラスのこの機能を使用しフルスクリーン切り替えを行うコードとして、 FullScreenImage/main.cppの一部を以下に示す。

int main(int argc, char** args) {
  // ...

      case SDL_KEYDOWN:
        switch(event.key.keysym.sym) {
        case SDLK_F5:
          {
            Texture::clearAll();
            SDL_Surface* surface=SDL_GetVideoSurface();
            if((surface->flags & SDL_FULLSCREEN)!=0) {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL);
            } else {
              initializeOpenGL(surface->w, surface->h, SDL_OPENGL|SDL_FULLSCREEN);
            }
            Texture::refreshAll();
          }
          break;

  // ...
}

Textureクラスを使用してテクスチャを扱う限り、以上のコードは正常にフルスクリーン切り替えを実現する。

*1: 画像の場合はImageTextureクラスを、文字列の場合はTextTextureクラスを参照するとよい。
戻る: SDL + OpenGLでグラフィックス