ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SDL2_ttf 사용하기
    프로그래밍/기타 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

    '프로그래밍 > 기타' 카테고리의 다른 글

    VT터미널에서 컬러 사용하기  (0) 2021.03.28

    댓글

Designed by Tistory.