SDL2.0でアルファブレンディング

フェードアウト、フェードインで使われるアルファブレンディングを、SDL で試してみました。

アルファブレンディングと色調補正について新しくページを設けました。

Table of Contents

1 SDL2のアルファブレンディング

SDL_SetTextureAlphaModでSDL_Textureのアルファ値を設定できます。

int SDL_SetTextureAlphaMod(SDL_Texture* texture,
                           Uint8        alpha)

SDL_LockTextureでピクセル単位でアルファ値を設定できます。

int SDL_LockTexture(SDL_Texture*    texture,
                    const SDL_Rect* rect,
                    void**          pixels,
                    int*            pitch)

以降ではSDL_SetTextureAlphaModeを用います。

 

2 アルファブレンディングを実行するコード

PNG画像で作成したSDL_Textureを2048回実行してFPSを表示します。描画毎 に実行するアルファブレンディングの有り無しで、FPSがどの程度変わるか を確認します。

 

2.1 アルファブレンディングの切り替え

コンパイル時に-DSAMPLE_WITH_ALPHAを指定するかしないかでアルファブレ ンディングの有り無しを切り替えます。

#ifdef SAMPLE_WITH_ALPHA
    SDL_SetTextureAlphaMod(sprite, SAMPLE_ALPHA);
#endif

2.2 Makefile

以下のようなMakefileを用意し、alpha.cppに対して、-DSAMPLE_WITH_ALPHA の指定がないバイナリとあるバイナリを作成します。

TARGET := $(patsubst %.cpp,%,$(wildcard *.cpp))
UNAME := $(shell uname)

ifeq ($(UNAME),Linux)
CXXFLAGS += $(shell sdl2-config --cflags)
LDLIBS   += $(shell sdl2-config --libs) -lSDL2_ttf -lSDL2_image
else
CXXFLAGS += -framework SDL2 -framework SDL2_ttf -framework SDL2_image
CXXFLAGS += -I/Library/Frameworks/SDL2.framework/Headers/
CXXFLAGS += -I/Library/Frameworks/SDL2_image.framework/Headers/
CXXFLAGS += -I/Library/Frameworks/SDL2_ttf.framework/Headers/
endif

CXXFLAGS += -std=c++0x $(EXTRA_CXXFLAGS)

alpha:
  $(CXX) $(CXXFLAGS) alpha.cpp -o without-alpha $(LDLIBS)
  $(CXX) $(CXXFLAGS) alpha.cpp -o with-alpha \
    -DSAMPLE_WITH_ALPHA $(LDLIBS)

all: $(TARGET)

clean:
  rm -rf $(TARGET)

2.3 コードの内容

alpha.cppの内容は以下の通りです。

#include <iostream>
#include <sys/time.h>
#include <SDL.h>
#include <SDL_ttf.h>
#include <SDL_image.h>

#define SAMPLE_SPRITE "sample.png"
#define SAMPLE_REPEAT_TIME (1024)
#define SAMPLE_ALPHA (128)

static SDL_Window *gWindow;
static int gWindowWidth;
static int gWindowHeight;

static SDL_Renderer *gRenderer;

static bool initialize(int width, int height)
{
  if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
    return false;

  if (SDL_CreateWindowAndRenderer(width, height, 0,
                                  &gWindow, &gRenderer) < 0)
    goto err;

  SDL_GetWindowSize(gWindow, &gWindowWidth, &gWindowHeight);
  return true;

 err:
  SDL_Quit();
  return false;
}

static void finalize()
{
  SDL_DestroyRenderer(gRenderer);
  SDL_DestroyWindow(gWindow);
  SDL_Quit();
}

SDL_Texture *createSprite(const char *spriteName)
{
  SDL_RWops *rwops;
  SDL_Surface *surface;
  SDL_Texture *texture;

  rwops = SDL_RWFromFile(spriteName, "rb");
  if (rwops == nullptr)
    return nullptr;

  surface = IMG_LoadPNG_RW(rwops);
  if (surface == nullptr)
    goto err;

  texture = SDL_CreateTextureFromSurface(gRenderer, surface);
  SDL_FreeSurface(surface);
 err:
  SDL_RWclose(rwops);
  return texture;
}

unsigned long getCurrentUsec()
{
  struct timeval timeval;
  gettimeofday(&timeval, NULL);
  return timeval.tv_sec * 1e6 + timeval.tv_usec;
}

int main()
{
  SDL_Texture *sprite;
  int ret = 1;
  unsigned long start, end;
  double fps;

  if (!initialize(400, 400)) {
    std::cerr << "Initialize error" << std::endl;
    return 1;
  }

  sprite = createSprite(SAMPLE_SPRITE);
  if (sprite == nullptr) {
    std::cerr << "Sprite error" << std::endl;
    goto err;
  }

  start = getCurrentUsec();

  for (size_t time = 0; time < SAMPLE_REPEAT_TIME; ++time) {
#ifdef SAMPLE_WITH_ALPHA
    SDL_SetTextureAlphaMod(sprite, SAMPLE_ALPHA);
#endif
    SDL_RenderClear(gRenderer);
    SDL_RenderCopy(gRenderer, sprite, nullptr, nullptr);
    SDL_RenderPresent(gRenderer);
  }

  end = getCurrentUsec();
  fps = 1e6 * SAMPLE_REPEAT_TIME / (end - start);
  std::cout << fps << std::endl;
  ret = 0;

  SDL_DestroyTexture(sprite);
 err:
  finalize();
  return ret;
}

3 実行結果

実行するマシン、OS、ミドルウェアパッケージの環境に依存しますが、私の 環境ではアルファブレンディングなしの場合は以下の通りになりました。

715.498

アルファブレンディングありの場合は以下の通りになりました。

499.116

内部の動作の詳細は調べていないので憶測になりますが、SDL_Texutureの全 ピクセルに対してアルファブレンディングの計算を実行しており、描画毎に アルファブレンディングを実行すると3割程度の性能が劣化することになります。

以上の結果から、できる限りアルファブレンディングの実行を減らす必要が あります。

 

4 SDL2でのアルファブレンディングの方針

アルファブレンディングありとなしでSDL_Textureを共有した場合、アルファ 値を255に戻す操作が必要になってしまう為、SDL2でアルファブレンディン グを実現する場合は下記の方針が考えられると思います。

  • PNG画像に対してアルファブレンディングあり用のSDL_Textureとアルファ ブレンディング無しようのSDL_Textureを作成する。
  • アルファブレンディングが必要な個所でのみアルファブレンディング用の SDL_Textureを用いてSDL_SetTextureAlphaModを実行し、 SDL_RenderCopyを実行する。必要ない箇所ではアルファブレンディング用 ではないSDL_Textureを利用する。

速度よりもメモリを減らしたい場合にはSDL_Textureを共有すべきでしょう。