SDL编程入门(16)TrueType字体

TrueType字体

使用SDL渲染文本的一种方法是使用扩展库SDL_ttf。SDL_ttf允许你从TrueType字体中创建图像,我们将在这里使用它从字体文本中创建纹理。

// 使用SDL、SDL_image、SDL_ttf、标准IO、math和string
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string>
#include <cmath>

要使用 SDL_ttf,你必须设置 SDL_ttf 扩展库,就像你 设置 SDL_image一样。就像之前一样,只是把头文件、库文件和二进制文件放在正确的位置,并把编译器配置成使用它们。

//Texture wrapper class
class LTexture
{
    public:
        //Initializes variables
        LTexture();

        //Deallocates memory
        ~LTexture();

        //Loads image at specified path
        bool loadFromFile( std::string path );
        
        //Creates image from font string
        bool loadFromRenderedText( std::string textureText, SDL_Color textColor );

        //Deallocates texture
        void free();

        //Set color modulation
        void setColor( Uint8 red, Uint8 green, Uint8 blue );

        //Set blending
        void setBlendMode( SDL_BlendMode blending );

        //Set alpha modulation
        void setAlpha( Uint8 alpha );
        
        //Renders texture at given point
        void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );

        //Gets image dimensions
        int getWidth();
        int getHeight();

    private:
        //The actual hardware texture
        SDL_Texture* mTexture;

        //Image dimensions
        int mWidth;
        int mHeight;
};

在这里,我们在texture class中添加了另一个函数,叫做loadFromRenderedText。SDL_ttf的工作方式是从字体和颜色中创建一个新的图像。对于我们的纹理类来说,这意味着我们将从SDL_ttf渲染文本而不是从文件中加载图像。

//我们要渲染的窗口
SDL_Window* gWindow = NULL;

//窗口渲染器
SDL_Renderer* gRenderer = NULL;

//全局通用的字体
TTF_Font *gFont = NULL;

//渲染纹理
LTexture gTextTexture;

在本教程和未来的教程中,我们将使用全局字体进行文本渲染。在SDL_ttf中,字体的数据类型是TTF_Font。

我们还有一个纹理,它将由字体生成。

bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor ){
    //释放已有的纹理
    free();

    //渲染文本表面
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    if( textSurface == NULL ){
        printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
    }else{
        //从表面像素创建纹理
        mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
        if( mTexture == NULL ){
            printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
        }else{
            //Get image dimensions
            mWidth = textSurface->w;
            mHeight = textSurface->h;
        }

        //释放旧表面
        SDL_FreeSurface( textSurface );
    }
    
    //Return success
    return mTexture != NULL;
}

这里是我们实际创建文本纹理的地方,我们将从字体中渲染。这个函数接收我们想要渲染的文本字符串和我们想要用来渲染它的颜色。之后,这个函数的工作原理和从文件中加载文字的工作原理差不多,只是这次我们使用的是SDL_ttf创建的SDL_Surface而不是文件。

在释放任何预存在的纹理后,我们使用 TTF_RenderText_Solid 加载一个表面。这将从给定的字体、文本和颜色中创建一个纯色表面。如果表面创建成功,我们将从中创建一个纹理,就像之前从文件中加载表面时一样。在创建纹理后,我们可以像其他纹理一样用它进行渲染。

还有其他方法可以渲染出更平滑或混合的文本。实验一下SDL_ttf文档中概述的不同类型的渲染。Shaded/Blended 渲染可能对不同大小的文本效果更好。

//初始化PNG加载
int imgFlags = IMG_INIT_PNG;
if( !( IMG_Init( imgFlags ) & imgFlags ) ){
    printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
    success = false;
}

//初始化 SDL_ttf
if( TTF_Init() == -1 ){
    printf( "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError() );
    success = false;
}

就像SDL_image一样,我们必须对它进行初始化,否则字体加载和渲染功能将无法正常工作。我们使用TTF_init来启动SDL_ttf。我们可以使用TTF_GetError()来检查错误。

bool loadMedia(){
    //Loading success flag
    bool success = true;

    //Open the font
    gFont = TTF_OpenFont( "16_true_type_fonts/lazy.ttf", 28 );
    if( gFont == NULL ){
        printf( "Failed to load lazy font! SDL_ttf Error: %s\n", TTF_GetError() );
        success = false;
    }else{
        //Render text
        SDL_Color textColor = { 0, 0, 0 };
        if( !gTextTexture.loadFromRenderedText( "The quick brown fox jumps over the lazy dog", textColor ) ){
            printf( "Failed to render text texture!\n" );
            success = false;
        }
    }

    return success;
}

在我们的加载函数中,使用TTF_OpenFont加载字体。这需要字体文件的路径和我们想要渲染的点大小。

如果字体加载成功,我们要使用加载方法加载一个文本纹理。作为一般规则,你要尽量减少渲染文本的次数。只有在需要的时候才重新渲染,由于我们在整个程序中使用的是同一个文本表面,所以我们只想渲染一次。

void close(){
    //Free loaded images
    gTextTexture.free();

    //Free global font
    TTF_CloseFont( gFont );
    gFont = NULL;

    //Destroy window
    SDL_DestroyRenderer( gRenderer );
    SDL_DestroyWindow( gWindow );
    gWindow = NULL;
    gRenderer = NULL;

    //Quit SDL subsystems
    TTF_Quit();
    IMG_Quit();
    SDL_Quit();
}

在我们的清理函数中,希望使用TTF_CloseFont来释放字体。我们还想使用TTF_Quit来退出SDL_ttf库,以完成清理工作。

//While application is running
while( !quit ){
    //Handle events on queue
    while( SDL_PollEvent( &e ) != 0 ){
        //User requests quit
        if( e.type == SDL_QUIT ){
            quit = true;
        }
    }

    //Clear screen
    SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
    SDL_RenderClear( gRenderer );

    //Render current frame
    gTextTexture.render( ( SCREEN_WIDTH - gTextTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gTextTexture.getHeight() ) / 2 );

    //Update screen
    SDL_RenderPresent( gRenderer );
}

正如你所看到的,在我们渲染了文本纹理之后,我们可以像其他纹理一样渲染它。

这里下载本教程的媒体和源代码。

原文链接

关注我的公众号:编程之路从0到1
编程之路从0到1

血色v残阳 CSDN认证博客专家 编程达人
掌握C、Java、Python、Go、Dart语言等多种编程语言,擅长GUI开发,曾从事Android原生开发,Flutter跨平台开发等等工作
微信公众号:
“编程之路从0到1”
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页