文字列の描画

drawText_image.png


SDL_ttf で SDL 用のサーフェスを作成し、それを OpenGL 用のテクスチャーに変換してから描画します。

SDL_ttf の利用

SDL_ttf を用いることで、指定したフォントの文字サーフェスを作ることができます。SDL_ttf を利用した文字列描画の流れは、以下のようになります。

これらは BMP 画像のテクスチャー表示 で解説した内容において SDL_Surface を作るのに SDL_LoadBMP() でなく SDL_ttf の関数を用いること以外は、基本的に同じです。

フォントファイルの管理

SDL_ttf では TTF_OpenFont(const char *file, int ptsize) のようにフォント資源をフォントと、そのフォントサイズで指定します。以下では、フォント資源を boost::shared_ptr 管理するクラスを作成してみましょう。

作成するクラスの簡単な仕様としては、

となります。
コンストラクタは、以下のように定義し、背景の色や透過情報は Font に格納されているとします。

Font(const char* font_file, size_t font_size = DefaultSize); 


Font.h
Font.cpp

#include "DetectOS.h"
#include "Font.h"
#include "SdlTtfInit.h"
#include "log_printf.h"
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <map>
#include <string>

#if defined(MSC)
#define snprintf _snprintf
#endif

using namespace qrk;
using namespace boost;
using namespace std;


namespace
{
  class TtfFont
  {
    TTF_Font* ttf_font_;

  public:
    TtfFont(const char* font_file, size_t font_size)
      : ttf_font_(TTF_OpenFont(font_file, static_cast<int>(font_size)))
    {
      if (! ttf_font_) {
        log_printf("TtfFont: %s\n", TTF_GetError());
      }
    }


    ~TtfFont(void)
    {
      if (ttf_font_) {
        TTF_CloseFont(ttf_font_);
      }
    }


    TTF_Font* resource(void)
    {
      return ttf_font_;
    }
  };


  class FontResource : private SdlTtfInit
  {
    typedef map<string, weak_ptr<TtfFont> > FontMap;
    FontMap ttf_fonts_;


  public:
    static FontResource* object(void)
    {
      static FontResource singleton_object;
      return &singleton_object;
    }


    shared_ptr<TtfFont> find(const char* file_name, size_t font_size)
    {
      char buffer[13];
      snprintf(buffer, sizeof(buffer), "%d", font_size);
      string key = string(buffer) + file_name;

      FontMap::iterator it = ttf_fonts_.find(key);
      if ((it != ttf_fonts_.end()) && (! it->second.expired())) {
        // 見つかれば、その資源を返す
        return shared_ptr<TtfFont>(it->second);
      }

      // 見つからなければ、新規に作成して返す
      shared_ptr<TtfFont> ttf_font(new TtfFont(file_name, font_size));
      ttf_fonts_[key] = ttf_font;
      return ttf_font;
    }
  };
}


struct Font::pImpl
{
  FontResource* font_resources_;

  string font_file_;
  size_t font_size_;
  Color foreground_;
  Color background_;
  bool transparent_;
  bool is_valid_;

  shared_ptr<TtfFont> resource_;


  pImpl(const char* font_file, size_t font_size, bool transparent)
    : font_resources_(FontResource::object()),
      font_file_(font_file), font_size_(font_size),
      foreground_(1.0, 1.0, 1.0), background_(0.0, 0.0, 0.0),
      transparent_(transparent), is_valid_(false)
  {
  }
};


Font::Font(const char* font_file, size_t size, bool transparent)
  : pimpl(new pImpl(font_file, size, transparent))
{
  // DefaultSize のフォントを生成し、フォントリソースが有効かを確認する
  pimpl->is_valid_ = (resource()) ? true : false;
}


Font::Font(const Font& rhs)
  : pimpl(new pImpl(rhs.pimpl->font_file_.c_str(),
                    rhs.pimpl->font_size_,
                    rhs.pimpl->transparent_))
{
  // pimpl 自体がコピーされないようにするため
  pimpl->foreground_ = rhs.pimpl->foreground_;
  pimpl->background_ = rhs.pimpl->background_;
}


Font& Font::operator = (const Font& rhs)
{
  // pimpl 自体がコピーされないようにするため
  this->pimpl->font_file_ = rhs.pimpl->font_file_;
  this->pimpl->font_size_ = rhs.pimpl->font_size_;
  this->pimpl->foreground_ = rhs.pimpl->foreground_;
  this->pimpl->background_ = rhs.pimpl->background_;
  this->pimpl->transparent_ = rhs.pimpl->transparent_;

  return *this;
}


Font::~Font(void)
{
}


Font Font::duplicate(void)
{
  return Font(*this);
}


bool Font::isValid(void) const
{
  return pimpl->is_valid_;
}


TTF_Font* Font::resource(void) const
{
  // !!! スレッド環境で動作させる場合には、適切にロックすること
  // LockGuard guard(pimpl->font_resources_->mutex_);

  pimpl->resource_ = pimpl->font_resources_->
    find(pimpl->font_file_.c_str(), pimpl->font_size_);

  return pimpl->resource_.get()->resource();
}


void Font::setFontSize(size_t font_size)
{
  pimpl->font_size_ = font_size;
}


size_t Font::fontSize(void) const
{
  return pimpl->font_size_;
}


void Font::setForegroundColor(const Color& color)
{
  pimpl->foreground_ = color;
}


Color Font::foregroundColor(void) const
{
  return pimpl->foreground_;
}


void Font::setBackgroundColor(const Color& color)
{
  pimpl->background_ = color;
}


Color Font::backgroundColor(void) const
{
  return pimpl->background_;
}


void Font::setTransparent(bool on)
{
  pimpl->transparent_ = on;
}


bool Font::transparent(void) const
{
  return pimpl->transparent_;
}

少し長いですが、処理自体は TTF_Font リソースを管理しているだけです。
TTF_Font を boost::shared_ptr で管理し boost::weak_ptr を使ってそのフォント資源が有効であるかどうか、を判定しています。

ここまでに説明した内容で、"こんにちはー" と表示するサンプルは、以下のようになります。


drawText.cpp

#include "Screen.h"
#include "Font.h"
#include "TextSurface.h"
#include <vector>

using namespace qrk;
using namespace std;


int main(int argc, char *argv[])
{
  Screen screen;
  screen.show(SDL_OPENGL);

  // フォントの読み出し
  const char* font_file = (argc <= 1) ? "font.ttf" : argv[1];

  // サイズを変更しながらフォントを描画する
  int font_size[] = { 16, 32, 64 };

  vector<Surface*> surfaces;
  int y = 0;
  size_t n = sizeof(font_size) / sizeof(font_size[0]);
  for (size_t i = 0; i < n; ++i) {
    Font font(font_file, font_size[i]);
    if (! font.resource()) {
      exit(1);
    }
    TextSurface* text = new TextSurface(font, "こんにちはー");
    surfaces.push_back(text);

    Rect<long> dest_rect = text->rect();
    dest_rect.y = y;
    text->draw(NULL, &dest_rect);
    y += dest_rect.h;
  }

  // 描画内容を反映させる
  SDL_GL_SwapBuffers();
  SDL_Delay(1000);

  for (vector<Surface*>::iterator it = surfaces.begin();
       it != surfaces.end(); ++it) {
    delete *it;
  }

  return 0;
}

実行結果

drawText_image.png

M+ FONTS フォントを使用

簡単ですね。
実行においては 日本語フォントの含まれる TrueType ファイルが必要なのと、ソースコードのエンコードが UTF-8 以外だと動作しない点にご注意下さい。

このサンプルを実行するためのソースコードは、以下リポジトリから取得できます。
svn checkout http://svn.sourceforge.jp/svnroot/qrobosdk/trunk/libs


コメントページ


参考



Generated on Mon May 18 11:11:10 2009 by  doxygen 1.5.7.1