Continuando nossa série de artigo traduzido do site lazyfoo, veremos agora uma maneira de renderizar texto com SDL usando a biblioteca SDL_ttf. Essa biblioteca permite que você crie imagens a partir de fontes TrueType que podem ser usadas para criar texturas a partir de texto.
//Using SDL, SDL_image, SDL_ttf, standard IO, math, and strings #include <SDL.h> #include <SDL_image.h> #include <SDL_ttf.h> #include <stdio.h> #include <string> #include <cmath>
Para fazer uso da biblioteca SDL_ttf, você precisa configurar a biblioteca de extensão SDL_ttf da mesma forma que fizemos com a biblioteca SDL_image. Como anteriormente, é somente uma questão de ter arquivos de cabeçalho, de biblioteca e binários nos locais apropriados, além de configurar seu compilador para trabalhar com todos esse arquivos.
//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; };
Aqui adicionamos outra função à classe de textura com o nome loadFromRenderedText. O jeito da biblioteca SDL_ttf trabalhar é criar uma nova imagem a partir de uma fonte e uma cor. Em nosso exemplo, no caso de nossa classe de textura, isso tudo significa que iremos carregar a imagem a partir de um texto renderizado ao invés de um arquivo.
//The window we'll be rendering to SDL_Window* gWindow = NULL; //The window renderer SDL_Renderer* gRenderer = NULL; //Globally used font TTF_Font *gFont = NULL; //Rendered texture LTexture gTextTexture;
Neste e em artigos futuros, estaremos usando uma fonte global para nossas renderizações de texto. No SDL_ttf, o tipo de dados para fontes de texto é o TTF_Font.
Também temos um textura que será gerada a partir da fonte.
bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor ) { //Get rid of preexisting texture free(); //Render text surface 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 { //Create texture from surface pixels 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; } //Get rid of old surface SDL_FreeSurface( textSurface ); } //Return success return mTexture != NULL; }
Aqui é onde de fato criamos a textura que iremos renderizar a partir da fonte. Essa função recebe a string de texto que queremos renderizar e a cor que queremos usar para a renderização. Com tudo isso, essa função basicamente funciona da mesma forma que o carregamento a partir de um arquivo, apenas que dessa vez estamos usando um SDL_Surface criado por SDL_ttf ao invés de um arquivo.
Depois de liberar qualquer textura pré-existente, carregamos uma superfície usando TTF_RenderText_Solid. Isso cria uma superfície com uma cor sólida com a fonte, texto e cor informada. Se a superfície for criada com sucesso, ciamos uma textura a partir dela da mesma forma que fizemos quando carregamos a superfície de um arquivo. Depois que a textura do texto é criada, podemos renderiza-la como qualquer outra textura.
Existem outras maneiras de renderizar texto que são mais suaves. Experimente com os tipos diferentes de renderização listadas na documentação do SDL_ttf.
//Initialize PNG loading 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; } //Initialize SDL_ttf if( TTF_Init() == -1 ) { printf( "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError() ); success = false; }
Da mesma forma que no caso da biblioteca SDL_image, temos que inicializar SDL_ttf ou o carregamento de fontes e as funções de renderização não irão funcionar de forma apropriada. Iniciamos SDL_ttf usando TTF_init. Podemos checar por error usando 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; }
Em nossa função de carregamento, carregamos nossa fonte usando a função TTF_OpenFont. Essa função recebe o caminho para o arquivo da fonte e um tamanho do ponto para o qual queremos renderiza-la.
Se a fonte for carregada com sucesso, queremos carregar agora a textura do texto usando nossa método de carregamento. Como regra geral, você quer minimizar a quantidade de tempo gasto para renderizar texto. Apenas re-renderize quando você realmente precise e como estamos usando a mesma superfície de texto em todo o programa, iremos renderiza-lo apenas uma vez.
//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 ); }
Como você pode ver, após a renderização da textura do texto podemos continuar a renderização com fazemos com qualquer outra textura.
Baixe os arquivos de mídia e código fonte desse artigo aqui.