diff --git a/.gitignore b/.gitignore index 1f47fdb..6b2ca5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ out *.o *.d +*.ttf diff --git a/FontCache.cpp b/FontCache.cpp index e69de29..da6fa7e 100644 --- a/FontCache.cpp +++ b/FontCache.cpp @@ -0,0 +1,180 @@ +#include "FontCache.h" + +CFontNode* CFontMap::operator[](int index){ + if(index < 0 || index >= map_length) + return nullptr; + + CFontNode* node = start; + + while(index > 0){ + node = node->next; + index --; + } + + return node; +} + +CFontGlyph CFont::CreateGlyph(char c, CFontGlyphInfo* info){ + CFontGlyph glyph; + + SDL_Surface* surface = TTF_RenderGlyph_Blended(font, c, color); + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + + int w = surface->w; + int h = surface->h; + + SDL_FreeSurface(surface); + + glyph.texture = texture; + + SDL_Rect rect = {0, 0, w, h}; + + info->rect = rect; + + return glyph; +} + +CFontGlyph* CFont::GetGlyph(char c, CFontGlyphInfo* info){ + CFontNode* node = nodeMap[c - 0x20]; + + *info = node->glyphInfo; + + return &node->glyph; +} + +SDL_Rect CFont::GetTextSize(std::string text){ + SDL_Rect rect = {0, 0, 0, 0}; + + for(char c : text){ + CFontGlyphInfo info; + GetGlyph(c, &info); + + rect.w += info.rect.w; + if(info.rect.h > rect.h) + rect.h = info.rect.h; + } + return rect; +} + +CFont::CFont(SDL_Renderer* _renderer, const char* _fontfile, int _fontsize, SDL_Color _color) + : fontfile(_fontfile), + fontsize(_fontsize), + color(_color), + renderer(_renderer) +{ + font = TTF_OpenFont(fontfile, fontsize); + + nodeMap.map_length = 0; + nodeMap.start = nullptr; + + CFontNode* fontnode = new CFontNode; + CFontGlyphInfo info; + fontnode->glyph = CreateGlyph(0x20, &info); + fontnode->glyphInfo = info; + + nodeMap.start = fontnode; + nodeMap.map_length ++; + + for(int c = 0x21; c <= 0x7E; c++){ + CFontNode* new_fontnode = new CFontNode; + new_fontnode->glyph = CreateGlyph(c, &info); + new_fontnode->glyphInfo = info; + + fontnode->next = new_fontnode; + new_fontnode->next = nullptr; + + fontnode = new_fontnode; + + nodeMap.map_length ++; + } +} + +CFont::~CFont(){ + CFontNode* node = nodeMap.start; + do{ + if(node){ + if(node->glyph.texture) + node->glyph.texture; + } + node = node->next; + } while(node); + + TTF_CloseFont(font); +} + +void CFont::DrawText(int x, int y, const char* text){ + int w = 0; + + SDL_Rect dst = {x, y, 0, 0}; + + for(int c = 0; c < strlen(text); c++){ + CFontGlyphInfo info; + CFontGlyph* glyph = GetGlyph(text[c], &info); + + dst.w = info.rect.w; + dst.h = info.rect.h; + + SDL_RenderCopy(renderer, glyph->texture, &info.rect, &dst); + + dst.x += dst.w; + } +} + +void CFont::DrawTextCentered(int center_x, int center_y, const char* text){ + SDL_Rect text_size = GetTextSize(text); + + DrawText( center_x - text_size.w / 2, center_y - text_size.h / 2, text); +} + +void CFont::DrawWrappedText(int x, int y, int w, std::string text){ + std::string line_buffer = ""; + + bool first_in_line = true; + + while(text.length() > 0){ + std::string word; + int space_index = text.find_first_of(' '); + + if(space_index == -1) + word = text; + else + word = text.substr(0, space_index); + + if(!first_in_line) + line_buffer += ' '; + + line_buffer += word; + + SDL_Rect line_size = GetTextSize(line_buffer); + + if(line_size.w > w){ + if(first_in_line){ + text = text.substr(line_buffer.length() + 1); + } + else{ + line_buffer = line_buffer.substr(0, line_buffer.length() - word.length()); + } + + DrawText(x, y, line_buffer.c_str()); + + y += line_size.h; + + first_in_line = true; + + line_buffer.clear(); + } + else{ + if(text.length() <= word.length()) + text = ""; + else + text = text.substr(word.length() + 1); + + first_in_line = false; + } + + } + + if(!line_buffer.empty()){ + DrawText(x, y, line_buffer.c_str()); + } +} diff --git a/FontCache.h b/FontCache.h index e69de29..a5cd4ad 100644 --- a/FontCache.h +++ b/FontCache.h @@ -0,0 +1,58 @@ +#ifndef __FONTCACHE_H__ +#define __FONTCACHE_H__ + +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +#include +#include +#include + +struct CFontGlyph{ + SDL_Texture* texture; +}; + +struct CFontGlyphInfo{ + SDL_Rect rect; +}; + +struct CFontNode{ + CFontGlyph glyph; + CFontGlyphInfo glyphInfo; + CFontNode* next = nullptr; +}; + +struct CFontMap{ + int map_length; + CFontNode* start = nullptr; + + CFontNode* operator[](int index); +}; + +class CFont{ +private: + const char* fontfile; + int fontsize; + SDL_Color color; + + SDL_Renderer* renderer; + TTF_Font* font; + + CFontMap nodeMap; + + CFontGlyph CreateGlyph(char c, CFontGlyphInfo* info); + + CFontGlyph* GetGlyph(char c, CFontGlyphInfo* info); + + SDL_Rect GetTextSize(std::string text); + +public: + CFont(SDL_Renderer* _renderer, const char* _fontfile, int _fontsize, SDL_Color _color); + ~CFont(); + + void DrawText(int x, int y, const char* text); + void DrawTextCentered(int center_x, int center_y, const char* text); + void DrawWrappedText(int x, int y, int w, std::string text); +}; + +#endif // __FONTCACHE_H__ \ No newline at end of file diff --git a/Makefile b/Makefile index e69de29..de24ceb 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,2 @@ +main: + g++ -o out *.cpp -lSDL2 -lSDL2_ttf \ No newline at end of file diff --git a/demo_fontcache.cpp b/demo_fontcache.cpp index e69de29..9a41fb7 100644 --- a/demo_fontcache.cpp +++ b/demo_fontcache.cpp @@ -0,0 +1,57 @@ +#include "SDL2/SDL.h" +#include "SDL2/SDL_ttf.h" + +#include +#include +#include + +#include "FontCache.h" + +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGHT = 600; + +int main(int, char*[]){ + + SDL_Init(SDL_INIT_VIDEO); + TTF_Init(); + + SDL_Window* window = SDL_CreateWindow("FontCache Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); + + SDL_Color white = {255, 255, 255, 255}; + + { + CFont font(renderer, "verdana.ttf", 65, white); + + bool quit = false; + while(!quit){ + SDL_Event e; + while(SDL_PollEvent(&e)){ + if(e.type == SDL_QUIT){ + quit = true; + } + } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF); + SDL_RenderClear(renderer); + + std::stringstream ss; + ss << (int)SDL_GetTicks(); + + std::string text = u8"number: " + ss.str(); + + font.DrawText(30, 10, text.c_str()); + + std::string text2 = "This is some text that I just wrote, test test test test test test test test test"; + font.DrawWrappedText(0, 70, 800, text2); + + SDL_RenderPresent(renderer); + } + } + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + + TTF_Quit(); + SDL_Quit(); + return 0; +}