SDL_TTFで文字列を表示 その2


前回紹介しましたSDL_TTFの使い方ですが、

一部ロジック的におかしな所がありましたので、

再度作り直しました。



SDL_TTF

SDL_ttf




サンプルプログラムのダウンロードはこちら:

SDL_TTF_sample



キーボードを操作するごとに、プログラム内に埋め込んだ文字列を表示します。

マウスボタンを操作するごとに、表示されている文字列の色を変更します。


libttf.c

/****************************************
    概要:TTFフォント読み込み・描画
    詳細:SDL_TTFを使用して描画を行う
    ファイル名:libttf.c
*****************************************/

#include "libttf.h"
#include <SDL/SDL_ttf.h>
#include <stdio.h>
#include <string.h>

#pragma comment(lib,"SDL_ttf.lib")

#define MAX_COUNT   (512)

/****************************************/
/*              構造体宣言              */
/****************************************/

/****************************************/
/*  作成済みの文字列データがあれば      */
/*  使いまわす為、リストにして保存      */
/*      フォントデータのリスト          */
/****************************************/
typedef struct ttf_list {
    SDL_Surface *text_img;  /*  作成された文字データを保存  */
    char *text;     /*  文字列データ                */
    struct ttf_list *next;  /*  次の文字列データへ          */
    Uint32  color;          /*  現在の描画色                */
}TTF_List;

/****************************************/
/*              フィールド              */
/****************************************/
static SDL_Surface *screen;
static TTF_Font *font;
static TTF_List *top_imglist;
static Uint32 defColor;
static SDL_Rect screen_range;

/****************************************/
/*              関数定義                */
/****************************************/
static void create_list(SDL_Surface *surface, const char *str);
static void delete_list(void);
static SDL_Surface *img_get(const char *str);
static SDL_Surface *render_text(const char *str,Uint32 color);
static int copy_surface(SDL_Surface *src,SDL_Surface *dst);
static int make_range(SDL_Rect *rect);
static int remake_color(SDL_Surface *img, Uint32 col);
static int lock(void);
static void unlock(void);
static Uint32 length(const char *str);
static TTF_List *get_list(const char *str);

/****************************************/
/*              関数実装                */
/****************************************/

/****************************************/
/*  概要:SDL_TTFの初期化               */
/*  詳細:描画先スクリーンとパス        */
/*         フォントサイズを設定        */
/*  引数:s         描画先スクリーン    */
/*       file_path ファイルのパス      */
/*        Uint8     フォントサイズ      */
/*  返却:0:初期化失敗                  */
/*        1:初期化成功                  */
/****************************************/
int ttf_init(SDL_Surface *s,const char *file_path, Uint8 size)
{
    if( !TTF_WasInit() && s != NULL && file_path != NULL)
    {
        if(TTF_Init() == -1)
            return 0;

        font = TTF_OpenFont
            (file_path,size%TTF_MAXSIZE);

        if(font == NULL)
        {
            TTF_Quit();
            return 0;
        }

        screen = s;
        defColor = 0x00000000;
    }else{
        return 0;
    }

    return 1;
}

/****************************************/
/*  概要:ライブラリの終了              */
/*  詳細:SDL_TTFライブラリの終了       */
/*  引数:無し                          */
/*  返却:無し                          */
/****************************************/
void ttf_dest(void)
{
    delete_list();
    
    if(font != NULL)
        TTF_CloseFont(font);
    
    if( TTF_WasInit() )
        TTF_Quit();

    font = NULL;
}

/****************************************/
/*  概要:フォントファイルを初期化      */
/*  詳細:フォントサイズを変える        */
/*  引数:ファイルパス                  */
/*  返却:成功:1 失敗:0            */
/****************************************/
int ttf_remake(const char *file_path, Uint8 size)
{
    if( !TTF_WasInit() )
    {
        if(TTF_Init() == -1 || screen == NULL)
            return 0;
    }

    if(font != NULL)
        TTF_CloseFont(font);

    font = NULL;
    font = TTF_OpenFont
        (file_path,size % TTF_MAXSIZE);

    if( font == NULL)
        return 0;

    return 1;
}

/****************************************/
/*  概要:通常のフォントカラーを設定    */
/*  詳細:通常のフォントカラーを設定    */
/*  引数:32ビットカラー                */
/*  返却:無し                          */
/****************************************/
void ttf_color(Uint32 color)
{
    defColor = color;
}

/****************************************/
/*  概要:文字列をスクリーンに描画      */
/*  詳細:座標指定して文字列描画        */
/*  SDL_TTFの関数を利用して作成した     */
/*  文字列データをリストに保存          */
/*  同じ文字列を再描画する場合は、      */
/*  バッファに格納された文字列イメージ  */
/*  を使い描画する                      */
/*  引数:文字列、横軸、縦軸            */
/*  返却:文字列描画先の矩形情報        */
/****************************************/
SDL_Rect ttf_put(const char *str,int x,int y)
{
    SDL_Surface *img;
    SDL_Rect rect = {
        0,0,0,0
    };

    /********************************/
    /*      文字列イメージを取得    */
    /********************************/
    img = img_get(str);
    if(img == NULL)
        return rect;

    rect.h = img->h;
    rect.w = img->w;
    rect.x = x;
    rect.y = y;

    /********************************/
    /*      デフォルトカラーに変更  */
    /********************************/
    if( get_list(str)->color != defColor )
        remake_color(img, defColor);

    /********************************/
    /*      描画範囲を作り直す      */
    /********************************/
    make_range( &rect );

    SDL_BlitSurface(img, NULL, screen, &rect);

    return rect;
}

/****************************************/
/*  概要:色を指定して文字列を描画      */
/*  詳細:色を指定して文字列を描画      */
/*  引数:文字列、横軸、縦軸、描画色    */
/*  返却:文字列描画先の矩形情報        */
/****************************************/
SDL_Rect ttf_put_color(const char *str,int x,int y, Uint32 color)
{
    SDL_Rect rect = {
        0,0,0,0
    };

    return rect;
}

/****************************************/
/*  概要:文字列をグラデーションで描画  */
/*  詳細:文字列をグラデーションで描画  */
/*  引数:文字列、フォントの情報        */
/*  返却:文字列描画先の矩形情報        */
/****************************************/
SDL_Rect ttf_put_effect(const char *str,TTF_Info *info)
{
    SDL_Rect rect = {
        0,0,0,0
    };

    return rect;
}

/****************************************/
/*  概要:文字列をグラデーションで描画  */
/*  詳細:文字列をグラデーションで描画  */
/*  引数:文字列、フォントの情報        */
/*  返却:文字列描画先の矩形情報        */
/****************************************/
SDL_Rect ttf_put_effect2(const char *str,TTF_Info *info)
{
    SDL_Rect rect = {
        0,0,0,0
    };

    return rect;
}

/****************************************/
/*  概要:保存された文字列バッファ消去  */
/*  詳細:リストをクリアする            */
/*  引数:無し                          */
/*  返却:無し                          */
/****************************************/
void ttf_clean(void)
{
    delete_list();
}

/****************************************/
/*  概要:リスト生成                    */
/*  詳細:イメージリストの生成          */
/*  引数:生成する文字列                */
/*  返却:無し                          */
/****************************************/
static void create_list(SDL_Surface *surface, const char *str)
{
    TTF_List *new_font = (TTF_List*)malloc(sizeof(TTF_List));
    TTF_List **tmp;

    if(new_font == NULL)
        return;

    new_font->color     = defColor;
    new_font->text_img  = surface;
    new_font->next      = NULL;
    new_font->text      = (char*)malloc(length(str));
    memcpy(new_font->text, str, length(str));

    for(tmp = &top_imglist; *tmp != NULL; tmp = &(*tmp)->next);

    *tmp = new_font;
}

/****************************************/
/*  概要:リスト削除                    */
/*  詳細:画像リスト削除                */
/*  引数:無し                          */
/*  返却:無し                          */
/****************************************/
static void delete_list(void)
{
    TTF_List *node = top_imglist;
    TTF_List *tmp;

    while(node != NULL)
    {
        tmp = node->next;                       /*  次のリストを退避    */

        if(node->text_img != NULL)
            SDL_FreeSurface(node->text_img);    /*  画像イメージの解放  */
        if(node->text != NULL)
            free(node->text);                   /*  文字列領域の解放    */
        memset(node,0,sizeof(TTF_List));        /*  Clean Memory        */
        free(node);                             /*  メモリの解放        */

        node = tmp;                             /*  次のリストへ        */
    }
}

/****************************************/
/*  概要:文字列イメージバッファの取得  */
/*  詳細:文字列画像データを取得        */
/*  引数:文字列                        */
/*  返却:文字列画像データ              */
/****************************************/
static SDL_Surface *img_get(const char *str)
{
    SDL_Surface *img;
    TTF_List    *tmp;

    tmp = get_list(str);
    if( tmp == NULL )
    {
        img = render_text(str,defColor);
        create_list(img,str);

    } else {

        img = tmp->text_img;
    }


    return img;
}

/****************************************/
/*  概要:テキストイメージの作成        */
/*  詳細:テキストイメージの作成        */
/*  引数:文字列、カラー                */
/*  返却:生成されたイメージ            */
/****************************************/
static SDL_Surface *render_text(const char *str,Uint32 color)
{
    SDL_Color c;
    SDL_Surface *img,*tmp;

    SDL_GetRGB(color, screen->format, &c.r, &c.g, &c.b);

    /********************************/
    /*  フォントイメージを作る      */
    /********************************/
    tmp = TTF_RenderUTF8_Solid(font, str, c);

    /********************************/
    /*  画像を画面フォーマットに変換*/
    /********************************/
    img = SDL_DisplayFormatAlpha(tmp);

    /********************************/
    /*  Release The Font image      */
    /********************************/
    SDL_FreeSurface( tmp );

    return img;
}

/****************************************/
/*  概要:描画先矩形情報の調整          */
/*  詳細:描画先から溢れた場合の調整    */
/*  引数:矩形情報                      */
/*  返却:0:未調整     1:調整済み     */
/****************************************/
static int make_range(SDL_Rect *rect)
{
    if(screen != NULL)
    {
        Uint16 h    = (Uint16)screen->h;
        Uint16 w    = (Uint16)screen->w;
        
        if(rect->h >= h)
            rect->h = h;
    
        if(rect->w >= w)
            rect->w = w;

        if(rect->y >= h - rect->h)
            rect->y = h - rect->h;
        else if(rect->y < 0)
            rect->y = 0;

        if(rect->x >= w - rect->x)
            rect->x = w - rect->x;
        else if(rect->x < 0)
            rect->x = 0;

        return 1;
    }
    
    return 0;
}

/****************************************/
/*  概要:画像の色を再調整する          */
/*  詳細:バッファに確保された色を変更  */
/*  引数:文字列イメージ                */
/*  返却:0:変更失敗   1:変更成功      */
/****************************************/
static int remake_color(SDL_Surface *img, Uint32 col)
{
    Uint32 x,y;                                 /*  マスクする画像の横軸、縦軸      */  
    Uint32 w,h;                                 /*  マスクする画像の幅、高さ        */
    SDL_PixelFormat *fmt    = img->format;      /*  ピクセルフォーマットを取得      */
    Uint8       bit_pxl = fmt->BitsPerPixel;    /*  1ピクセルのビット数を算出       */
    Uint8       byt_pxl = fmt->BytesPerPixel;   /*  1ピクセルのバイト数を算出       */
    Uint32      *pixels = img->pixels;          /*  ピクセルデータの先頭アドレス    */
    Uint16      pitch   = img->pitch;           /*  1スキャンラインのバイト数      */
    Uint16      bpp     = img->format->BytesPerPixel;
    Uint32      *msk_pxl;                       /*  マスクするピクセルのアドレス    */
    
    if( bit_pxl == 8 || !lock() )
    {
        return 0;
    }

    /****************************/
    /*  RGBカラー値をピクセル   */
    /*  フォーマットに写像後    */
    /*  変換後の値を取得        */
    /****************************/
    col = SDL_MapRGBA(fmt,
        (col&fmt->Rmask)>>fmt->Rshift,
        (col&fmt->Gmask)>>fmt->Gshift,
        (col&fmt->Bmask)>>fmt->Bshift,
        fmt->Amask>>fmt->Ashift
    );

    y = 0;                  /*  マスクする画像データの縦軸  */
    h = img->h;             /*  マスクする画像データの高さ  */
    while( y < h )
    {
        x = 0;              /*  マスクする画像データの横軸  */
        w = img->w;         /*  マスクする画像データの幅    */
        while( x < w )
        {   
            /****************************************/
            /*      マスクするアドレスを取得        */
            /****************************************/
            msk_pxl = (Uint32*)((Uint32)pixels + (x * bpp) + (pitch * y));

            /****************************************/
            /*          アルファ値を抽出する        */
            /*  アルファ値0でなければマスクする    */
            /****************************************/
            if( *msk_pxl & fmt->Amask )
            {   
                /************************************************/
                /*      1ピクセルのビット数により処理を分ける   */
                /************************************************/
                switch( bit_pxl )
                {
                /*      8ビットピクセルデータの色を変更         */
                case 8: /*  8_BitsPerPixels NoSupport   */  break;
                /*      16ビットピクセルデータの色を変更        */
                case 16:    *(Uint16*)msk_pxl = col;        break;
                /*      24ビットピクセルデータの色を変更        */
                case 24:
                /*      エンディンアンにより処理を分ける        */
                /*  何故かここだけエンディアンを意識する...(*´д`*) */
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
                        /*      Mac系OSの場合       */
                        msk_pxl[0] = (col >> 16)    & 0xff;
                        msk_pxl[1] = (col >> 8)     & 0xff;
                        msk_pxl[2] = col            & 0xff;
#else
                        /*      Windows系OSの場合   */
                        msk_pxl[0] = col            & 0xff;
                        msk_pxl[1] = (col >> 8)     & 0xff;
                        msk_pxl[2] = (col >> 16)    & 0xff;
#endif
                                                            break;
                /*      32ビットピクセルデータの色を変更        */
                case 32:    *(Uint32*)msk_pxl = col;        break;
                /*      その他のピクセルデータの色を変更        */
                default:    /*  No Support  */              break;
                }
            }
            x++;            /*  次の横軸にアクセスする          */
        }
        y++;                /*  次の縦軸にアクセスする          */
    }                       /*  カラー変更の終了                */

    unlock();

    return 1;
}

static int lock(void)
{
    if(SDL_MUSTLOCK(screen))
    {
        if(SDL_LockSurface(screen) < 0)
            return 0;
    }
    return 1;
}

static void unlock(void)
{
    if(SDL_MUSTLOCK(screen))
    {
        SDL_UnlockSurface(screen);
    }
}

/****************************************/
/*  概要:文字数返却                    */
/*  詳細:strnlen()使用                 */
/*  引数:文字列                        */
/*  返却:文字数(\0を含む)            */
/****************************************/
static Uint32 length(const char *str)
{
    return (Uint32)strnlen(str,MAX_COUNT) + 1;
}

/****************************************/
/*  概要:画像保存リスト返却            */
/*  詳細:strnlen()使用                 */
/*  引数:文字列                        */
/*  返却:画像リスト                    */
/****************************************/
static TTF_List *get_list(const char *str)
{
    TTF_List    *tmp;
    char        *text;

    if( top_imglist != NULL )
    {
        tmp = top_imglist;

        do {
            text = (char*)tmp->text;

            if( !strncmp(text, str, length(text)) )
                return tmp;

            tmp = tmp->next;

        } while(tmp != NULL);
    }

    return NULL;
}

/****************************************
            end libttf.c
*****************************************/