프로그래밍/기타

SDL2_ttf 사용하기

kyudoc 2021. 3. 29. 21:03
728x90

제가 SDL 라이브러리와 함께 많이 쓰는 라이브러리에는 SDL_image, SDL_ttf, SDL_net이 있습니다. 그중에 SDL2_ttf를 이용하여 FreeType 폰트를 출력하는 방법을 알아보겠습니다.

 

먼저 실행 화면을 보시겠습니다. SDL 사용법은 이전 글을 참조하여 주십시오.

이전 글 참조: Visual C++에서 SDL2 라이브러리 사용하기

 

위 화면의 소스 코드입니다.

#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"

#pragma comment(lib, "SDL2main.lib")
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2_ttf.lib")

SDL_Window* window;
SDL_Renderer* renderer;

int SDL_main(int argc, char* argv[])
{
	// Initialize SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
		printf("Could not initialize SDL! (%s)\n", SDL_GetError());
		return -1;
	}

	// Initialize Font
	if (TTF_Init() < 0) {
		printf("Could not initialize font! (%s)\n", TTF_GetError());
		return -1;
	}

	// Create window
	window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL);
	if (window == NULL) {
		printf("Could not create window! (%s)\n", SDL_GetError());
		return -1;
	}

	// Create renderer
	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
	if (renderer == NULL) {
		printf("Could not create renderer! (%s)\n", SDL_GetError());
		return -1;
	}

	// Clear renderer (white)
	SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
	SDL_RenderClear(renderer);

	// Draw text
	TTF_Font* font = TTF_OpenFont("C:\\Windows\\Fonts\\gulim.ttc", 16);
	if (font == NULL) {
		printf("Could not open font! (%s)\n", TTF_GetError());
		return -1;
	}

	SDL_Color color = {255, 0, 255, SDL_ALPHA_OPAQUE};
	SDL_Surface* surface = TTF_RenderText_Blended(font, "Test String", color);
	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
	SDL_FreeSurface(surface);
	SDL_Rect r = {0, 0, surface->w, surface->h};
	SDL_RenderCopy(renderer, texture, NULL, &r);
	SDL_DestroyTexture(texture);
	TTF_CloseFont(font);

	// Update screen
	SDL_RenderPresent(renderer);

	SDL_Event event;
	int done = 0;

	while (!done) {
		SDL_PollEvent(&event);

		if (event.type == SDL_QUIT) {
			done = 1;
		}
	}

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();

	return 0;
}

 

TTF 폰트를 열기위해 4가지 함수가 존재합니다.

extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFont(const char *file, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndex(const char *file, int ptsize, long index);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
extern DECLSPEC TTF_Font * SDLCALL TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);

 

TTF_OpenFont() 함수는 위 예제에서 사용하는 방법을 보여 드렸기에 넘어가고요.
TTF_OpenFontIndex() 함수는 TTF 폰트 한 파일에 여러 개의 폰트가 존재할 경우 index에 어떤 폰트를 사용할지를 지정하여 선택해줍니다.

TTF_OpenFontRW() 함수는 파일을 열고 닫는 API가 따로 있거나 ROM과 같은 메모리에 있을 때(또 다른 예는 폰트가 Visual C++라면 리소스에 있을 때) 폰트 데이터를 직접 지정하여 사용하는 함수입니다. 예를 들어 fopen와 같은 함수가 아니라 다른 방식으로 파일을 사용할 때 입니다.

	std::ifstream gulim("c:\\Windows\\Fonts\\gulim.ttc", std::ios::binary);

	gulim.seekg(0, std::ios_base::end);
	std::istream::pos_type file_size = gulim.tellg();
	gulim.seekg(0);

	std::vector<char> buffer(file_size);

	std::copy(std::istreambuf_iterator<char>(gulim), std::istreambuf_iterator<char>(), buffer.begin());

	SDL_RWops *ops_ctx = SDL_RWFromMem(&buffer[0], file_size);
	if (ops_ctx == NULL) {
		printf("SDL_RWFromMem() error! (%s)\n", SDL_GetError());
		return -1;
	}

	TTF_Font* font = TTF_OpenFontRW(ops_ctx, 0, 16);
	if (font == NULL) {
		printf("Could not open font! (%s)\n", TTF_GetError());
		return -1;
	}

	(... 중략 ...)

	TTF_CloseFont(font);
	SDL_RWclose(ops_ctx);
	gulim.close();

 

위 예제는 C++의 기능을 사용했지만 임베디드 시스템의 경우 C/C++ 표준 API가 아닌 시스템 고유의 API를 사용하지요. TTF_OpenFontRW() 함수는 그럴 때 사용할 수 있겠습니다.

하지만, 일부러 폰트 데이터를 모두 읽어 메모리에 넣고 TTF_OpenFontRW() 함수를 사용하지는 마십시오. 폰트 데이터를 모두 읽어 메모리에 복사하는 과정에 시간이 꽤 소요됩니다.

 

TTF_OpenFontIndexRW() 함수는 TTF_OpenFontIndex() 함수와 마찬가지로 폰트 인덱스를 지정할 수 있습니다.

 

다음은 폰트를 렌더링하는 함수에는 대표적으로 3가지 종류가 있습니다.

extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg);

 

특징을 설명 드리기 앞서 각각의 함수를 사용한 화면을 보시겠습니다.

 

Solid 함수는 Font 데이터를 가공하지 않고 그대로 출력해줍니다. Shaded 함수는 그림자 효과가 있구요. 예제에서도 사용했던 Blend 함수는 Anti-aliasing이 적용되어 깔끔한 모습을 보여 줍니다.

또, 텍스트 데이터는 Ansi, UTF-8, 유니코드 형태로 넘겨줄 수 있습니다.

extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg);

 

이 외에도 문자열에 굵은 글씨, 이탤릭 스타일이나 밑줄을 그을 수 도 있고, 외각선을 넣을 수도 있습니다.

/* Set and retrieve the font style */
#define TTF_STYLE_NORMAL        0x00
#define TTF_STYLE_BOLD          0x01
#define TTF_STYLE_ITALIC        0x02
#define TTF_STYLE_UNDERLINE     0x04
#define TTF_STYLE_STRIKETHROUGH 0x08
extern DECLSPEC int SDLCALL TTF_GetFontStyle(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontStyle(TTF_Font *font, int style);
extern DECLSPEC int SDLCALL TTF_GetFontOutline(const TTF_Font *font);
extern DECLSPEC void SDLCALL TTF_SetFontOutline(TTF_Font *font, int outline);

 

함수들에 대한 자세한 정보는 공식 사이트의 문서(링크)를 참조하여 보세요.

728x90