├── test ├── utf8_sample.txt ├── fonts │ └── FreeSans.ttf ├── test.cbp └── main.c ├── README.md ├── TODO.md ├── LICENSE ├── CMakeLists.txt ├── SDL_FontCache.h └── SDL_FontCache.c /test/utf8_sample.txt: -------------------------------------------------------------------------------- 1 | Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. -------------------------------------------------------------------------------- /test/fonts/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grimfang4/SDL_FontCache/HEAD/test/fonts/FreeSans.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDL_FontCache 2 | A generic font caching C library with loading and rendering support for SDL. 3 | 4 | SDL_FontCache loads, caches, and renders TrueType fonts using SDL_ttf. 5 | It fully supports UTF-8 strings and includes some utility functions for manipulating them. 6 | 7 | An example using SDL_Renderer: 8 | 9 | ``` 10 | FC_Font* font = FC_CreateFont(); 11 | FC_LoadFont(font, renderer, "fonts/FreeSans.ttf", 20, FC_MakeColor(0,0,0,255), TTF_STYLE_NORMAL); 12 | 13 | ... 14 | 15 | FC_Draw(font, renderer, 0, 0, "This is %s.\n It works.", "example text"); 16 | 17 | ... 18 | 19 | FC_FreeFont(font); 20 | ``` 21 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | # Bugs 3 | Why is the font line height so big? 4 | 5 | # Features 6 | Make font->ignore_newlines so you can render lines with newlines in them without going onto extra lines. 7 | Add functions to get/set glyph data 8 | Is this enough to make it generally useful? 9 | Can I implement a bitmap font thing on top of it? 10 | Needs functions to: 11 | Set texture cache (e.g. from custom bitmap) 12 | Set glyph data (could just be the source which is called as needed) 13 | Set glyph data source and texture cache generator 14 | Functions for manipulating UTF-8 text 15 | U8_stroffset(iter->value, i) // convert pos to offset 16 | U8_strreplace(s, p, c) // Replaces the character there 17 | Is string overwrite more useful? 18 | Scaled box/column text 19 | Dynamic kerning calculation stored in 2D codepoint array (render "XY" and compare to width of "X"+"Y") if TTF_GetFontKerning(ttf) is true. Render without kerning lookup if that is off, too. Could dig into FreeType for getting this info more directly. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jonathan Dearborn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(SDL_FontCache VERSION 0.10.0 LANGUAGES C) 3 | 4 | find_path(sdl2_INCLUDE_DIR SDL.h) 5 | find_library(sdl2_LIBRARY SDL2) 6 | find_library(sdl2main_LIBRARY SDL2main) 7 | find_path(sdl2_ttf_INCLUDE_DIR SDL_ttf.h) 8 | find_library(sdl2_ttf_LIBRARY SDL2_ttf) 9 | 10 | 11 | if (NOT sdl2_INCLUDE_DIR) 12 | message(FATAL_ERROR "SDL2 include dir not found") 13 | endif() 14 | if (NOT sdl2_LIBRARY) 15 | message(FATAL_ERROR "SDL2 library dir not found") 16 | endif() 17 | if (NOT sdl2main_LIBRARY) 18 | message(FATAL_ERROR "SDL2main library dir not found") 19 | endif() 20 | if (NOT sdl2_ttf_INCLUDE_DIR) 21 | message(FATAL_ERROR "SDL2_TTF include dir not found") 22 | endif() 23 | if (NOT sdl2_ttf_LIBRARY) 24 | message(FATAL_ERROR "SDL2_TTF library dir not found") 25 | endif() 26 | 27 | 28 | add_library(FontCache 29 | SDL_FontCache.h 30 | SDL_FontCache.c 31 | ) 32 | target_include_directories(FontCache PUBLIC ${sdl2_INCLUDE_DIR}) 33 | target_include_directories(FontCache PUBLIC .) 34 | target_link_libraries(FontCache ${sdl2_LIBRARY}) 35 | target_link_libraries(FontCache ${sdl2main_LIBRARY}) 36 | target_include_directories(FontCache PUBLIC ${sdl2_ttf_INCLUDE_DIR}) 37 | target_link_libraries(FontCache ${sdl2_ttf_LIBRARY}) 38 | 39 | add_executable(FontCache-tests 40 | test/main.c 41 | ) 42 | file(COPY test/utf8_sample.txt DESTINATION ${CMAKE_BINARY_DIR}) 43 | file(COPY test/fonts/FreeSans.ttf DESTINATION ${CMAKE_BINARY_DIR}/fonts) 44 | target_link_libraries(FontCache-tests FontCache) 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/test.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 89 | 90 | -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | #include "SDL.h" 2 | #include "SDL_FontCache.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // UTF-8 Sample from http://www.columbia.edu/~fdc/utf8/ 10 | 11 | 12 | #ifdef SDL_GPU_VERSION_MAJOR 13 | GPU_Target* screen; 14 | #else 15 | SDL_Window* window; 16 | SDL_Renderer* renderer; 17 | #endif 18 | 19 | 20 | void draw_rect(FC_Rect rect, SDL_Color color) 21 | { 22 | #ifdef SDL_GPU_VERSION_MAJOR 23 | GPU_Rectangle(screen, rect.x, rect.y, rect.x + rect.w - 1, rect.y + rect.h - 1, color); 24 | #else 25 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 26 | SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); 27 | SDL_RenderDrawRect(renderer, &rect); 28 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); 29 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 30 | #endif 31 | } 32 | 33 | void fill_rect(FC_Rect rect, SDL_Color color) 34 | { 35 | #ifdef SDL_GPU_VERSION_MAJOR 36 | GPU_RectangleFilled(screen, rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, color); 37 | #else 38 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); 39 | SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); 40 | SDL_RenderFillRect(renderer, &rect); 41 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); 42 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 43 | #endif 44 | } 45 | 46 | void set_clip(FC_Rect rect) 47 | { 48 | #ifdef SDL_GPU_VERSION_MAJOR 49 | GPU_SetClipRect(screen, rect); 50 | #else 51 | SDL_RenderSetClipRect(renderer, &rect); 52 | #endif 53 | } 54 | 55 | void unset_clip() 56 | { 57 | #ifdef SDL_GPU_VERSION_MAJOR 58 | GPU_UnsetClip(screen); 59 | #else 60 | SDL_RenderSetClipRect(renderer, NULL); 61 | #endif 62 | } 63 | 64 | char* get_string_from_file(const char* filename) 65 | { 66 | char* result; 67 | int size = 0; 68 | int i; 69 | SDL_RWops* rwops = SDL_RWFromFile(filename, "r"); 70 | 71 | char c; 72 | while(SDL_RWread(rwops, &c, 1, 1) > 0) 73 | { 74 | ++size; 75 | } 76 | 77 | result = (char*)malloc(size+1); 78 | memset(result, 0, size+1); 79 | SDL_RWseek(rwops, 0, RW_SEEK_SET); 80 | 81 | i = 0; 82 | while(SDL_RWread(rwops, &c, 1, 1) > 0) 83 | { 84 | result[i] = c; 85 | ++i; 86 | } 87 | 88 | SDL_RWclose(rwops); 89 | return result; 90 | } 91 | 92 | void loop_drawSomeText() 93 | { 94 | FC_Font* font = FC_CreateFont(); 95 | FC_Font* font2 = FC_CreateFont(); 96 | FC_Font* font3 = FC_CreateFont(); 97 | 98 | //FC_SetLoadingString(font, FC_GetStringASCII_Latin1()); 99 | 100 | #ifdef SDL_GPU_VERSION_MAJOR 101 | FC_LoadFont(font, "fonts/FreeSans.ttf", 20, FC_MakeColor(0,0,0,255), TTF_STYLE_NORMAL); 102 | FC_LoadFont(font2, "fonts/FreeSans.ttf", 18, FC_MakeColor(0,200,0,255), TTF_STYLE_NORMAL); 103 | FC_LoadFont(font3, "fonts/FreeSans.ttf", 22, FC_MakeColor(0,0,200,255), TTF_STYLE_NORMAL); 104 | #else 105 | FC_LoadFont(font, renderer, "fonts/FreeSans.ttf", 20, FC_MakeColor(0,0,0,255), TTF_STYLE_NORMAL); 106 | FC_LoadFont(font2, renderer, "fonts/FreeSans.ttf", 18, FC_MakeColor(0,200,0,255), TTF_STYLE_NORMAL); 107 | FC_LoadFont(font3, renderer, "fonts/FreeSans.ttf", 22, FC_MakeColor(0,0,200,255), TTF_STYLE_NORMAL); 108 | #endif 109 | char* utf8_string = get_string_from_file("utf8_sample.txt"); 110 | char input_text[2048]; 111 | sprintf(input_text, "Edit this text."); 112 | int input_position = U8_strlen(input_text); 113 | FC_Rect input_rect; 114 | int mode = 0; 115 | 116 | 117 | float target_w, target_h; 118 | #ifdef SDL_GPU_VERSION_MAJOR 119 | GPU_Target* target = screen; 120 | target_w = target->w; 121 | target_h = target->h; 122 | #else 123 | SDL_Renderer* target = renderer; 124 | { 125 | int w, h; 126 | SDL_GetWindowSize(window, &w, &h); 127 | target_w = w; 128 | target_h = h; 129 | } 130 | #endif 131 | 132 | FC_Rect leftHalf = {0, 0, 3*target_w/4.0f, target_h}; 133 | FC_Rect rightHalf = {leftHalf.w, 0, target_w/4.0f, target_h}; 134 | 135 | FC_Rect box1 = {215, 50, 150, 150}; 136 | FC_Rect box2 = {215, box1.y + box1.h + 50, 150, 150}; 137 | FC_Rect box3 = {215, box2.y + box2.h + 50, 150, 150}; 138 | 139 | SDL_Color black = {0, 0, 0, 255}; 140 | SDL_Color white = {255, 255, 255, 255}; 141 | SDL_Color gray = {0x77, 0x77, 0x77, 255}; 142 | SDL_Color blue = {0, 0, 127, 255}; 143 | 144 | int scroll = 0; 145 | 146 | const Uint8* keystates = SDL_GetKeyboardState(NULL); 147 | 148 | input_rect = FC_MakeRect(rightHalf.x, 175, rightHalf.w, 500); 149 | 150 | SDL_StartTextInput(); 151 | 152 | Uint8 done = 0; 153 | SDL_Event event; 154 | while(!done) 155 | { 156 | while(SDL_PollEvent(&event)) 157 | { 158 | if(event.type == SDL_QUIT) 159 | done = 1; 160 | else if(event.type == SDL_KEYDOWN) 161 | { 162 | if(event.key.keysym.sym == SDLK_ESCAPE) 163 | done = 1; 164 | if(event.key.keysym.sym == SDLK_BACKSPACE) 165 | { 166 | U8_strdel(input_text, input_position-1); 167 | 168 | --input_position; 169 | if(input_position < 0) 170 | input_position = 0; 171 | } 172 | else if(event.key.keysym.sym == SDLK_F1) 173 | { 174 | mode++; 175 | if(mode > 1) 176 | mode = 0; 177 | } 178 | else if(event.key.keysym.sym == SDLK_RETURN) 179 | { 180 | U8_strinsert(input_text, input_position, "\n", 2048); 181 | input_position++; 182 | } 183 | else if(event.key.keysym.sym == SDLK_TAB) 184 | { 185 | U8_strinsert(input_text, input_position, "\t", 2048); 186 | input_position++; 187 | } 188 | else if(event.key.keysym.sym == SDLK_LEFT) 189 | { 190 | --input_position; 191 | if(input_position < 0) 192 | input_position = 0; 193 | } 194 | else if(event.key.keysym.sym == SDLK_RIGHT) 195 | { 196 | ++input_position; 197 | int len = U8_strlen(input_text); 198 | if(input_position >= len) 199 | input_position = len; 200 | } 201 | } 202 | else if(event.type == SDL_TEXTINPUT) 203 | { 204 | if(U8_strinsert(input_text, input_position, event.text.text, 2048)) 205 | input_position += U8_strlen(event.text.text); 206 | } 207 | else if(event.type == SDL_MOUSEBUTTONDOWN) 208 | { 209 | if(FC_InRect(event.button.x, event.button.y, input_rect)) 210 | { 211 | input_position = FC_GetPositionFromOffset(font, event.button.x - input_rect.x, event.button.y - input_rect.y, input_rect.w, FC_ALIGN_LEFT, "%s", input_text); 212 | } 213 | } 214 | } 215 | 216 | if(keystates[SDL_SCANCODE_UP]) 217 | scroll--; 218 | else if(keystates[SDL_SCANCODE_DOWN]) 219 | scroll++; 220 | 221 | 222 | if(mode == 0 || mode == 1) 223 | { 224 | fill_rect(leftHalf, white); 225 | fill_rect(rightHalf, gray); 226 | 227 | FC_Draw(font2, target, 0, 0, "UTF-8 text: %s", utf8_string); 228 | 229 | FC_DrawAlign(font, target, rightHalf.x, 5, FC_ALIGN_LEFT, "draw align LEFT"); 230 | FC_DrawAlign(font, target, rightHalf.x, 25, FC_ALIGN_CENTER, "draw align CENTER"); 231 | FC_DrawAlign(font, target, rightHalf.x, 45, FC_ALIGN_RIGHT, "draw align RIGHT"); 232 | 233 | float time = SDL_GetTicks()/1000.0f; 234 | 235 | FC_DrawColor(font, target, rightHalf.x, 65, FC_MakeColor(128 + 127*sin(time), 128 + 127*sin(time/2), 128 + 127*sin(time/4), 128 + 127*sin(time/8)), "Dynamic colored text"); 236 | FC_Draw(font, target, rightHalf.x, 85, "Multi\nline\ntext"); 237 | 238 | FC_DrawBox(font, target, input_rect, "%s", input_text); 239 | draw_rect(FC_MakeRect(input_rect.x, input_rect.y + FC_GetLineHeight(font), FC_GetWidth(font, "%s", input_text), 2), black); 240 | 241 | FC_Rect input_cursor_pos = FC_GetCharacterOffset(font, input_position, input_rect.w, "%s", input_text); 242 | if(SDL_GetTicks()%1000 < 500) 243 | fill_rect(FC_MakeRect(input_rect.x + input_cursor_pos.x, input_rect.y + input_cursor_pos.y, input_cursor_pos.w, input_cursor_pos.h), FC_MakeColor(0, 0, 0, 255)); 244 | 245 | FC_DrawColumn(font, target, 0, 50, 200, "column align LEFT\nColumn text wraps at the width of the column and has no maximum height."); 246 | FC_DrawColumnAlign(font, target, 100, 250, 200, FC_ALIGN_CENTER, "column align CENTER\nColumn text wraps at the width of the column and has no maximum height."); 247 | FC_DrawColumnAlign(font, target, 200, 450, 200, FC_ALIGN_RIGHT, "column align RIGHT\nColumn text wraps at the width of the column and has no maximum height."); 248 | 249 | 250 | draw_rect(box1, black); 251 | draw_rect(box2, black); 252 | draw_rect(box3, black); 253 | 254 | set_clip(box1); 255 | FC_Rect box1a = {box1.x, box1.y - scroll, box1.w, box1.h + scroll}; 256 | FC_DrawBox(font, target, box1a, "box align LEFT\nBox text wraps at the width of the box and is clipped to the maximum height."); 257 | 258 | set_clip(box2); 259 | FC_Rect box2a = {box2.x, box2.y - scroll, box2.w, box2.h + scroll}; 260 | FC_DrawBoxAlign(font, target, box2a, FC_ALIGN_CENTER, "box align CENTER\nBox text wraps at the width of the box and is clipped to the maximum height."); 261 | 262 | set_clip(box3); 263 | FC_Rect box3a = {box3.x, box3.y - scroll, box3.w, box3.h + scroll}; 264 | FC_DrawBoxAlign(font, target, box3a, FC_ALIGN_RIGHT, "box align RIGHT\nBox text wraps at the width of the box and is clipped to the maximum height."); 265 | 266 | unset_clip(); 267 | } 268 | 269 | if(mode == 1) 270 | { 271 | fill_rect(leftHalf, blue); 272 | fill_rect(rightHalf, blue); 273 | 274 | #ifdef SDL_GPU_VERSION_MAJOR 275 | GPU_Image* image = FC_GetGlyphCacheLevel(font, 0); 276 | GPU_SetRGBA(image, 255, 255, 255, 255); 277 | GPU_Blit(image, NULL, screen, image->w/2, image->h/2); 278 | #else 279 | SDL_Texture* image = FC_GetGlyphCacheLevel(font, 0); 280 | SDL_Rect destrect = {0, 0, 0, 0}; 281 | SDL_QueryTexture(image, NULL, NULL, &destrect.w, &destrect.h); 282 | SDL_SetTextureColorMod(image, 255, 255, 255); 283 | SDL_SetTextureAlphaMod(image, 255); 284 | SDL_RenderCopy(renderer, image, NULL, &destrect); 285 | #endif 286 | } 287 | 288 | #ifdef SDL_GPU_VERSION_MAJOR 289 | GPU_Flip(screen); 290 | #else 291 | SDL_RenderPresent(renderer); 292 | #endif 293 | 294 | SDL_Delay(1); 295 | } 296 | 297 | SDL_StopTextInput(); 298 | 299 | free(utf8_string); 300 | FC_FreeFont(font); 301 | FC_FreeFont(font2); 302 | FC_FreeFont(font3); 303 | } 304 | 305 | int main(int argc, char* argv[]) 306 | { 307 | int w = 800; 308 | int h = 600; 309 | 310 | #ifdef SDL_GPU_VERSION_MAJOR 311 | screen = GPU_Init(w, h, GPU_DEFAULT_INIT_FLAGS); 312 | if(screen == NULL) 313 | { 314 | GPU_LogError("Failed to initialize SDL_gpu.\n"); 315 | return 1; 316 | } 317 | #else 318 | if(SDL_Init(SDL_INIT_VIDEO) < 0) 319 | { 320 | SDL_Log("Failed to initialize SDL.\n"); 321 | return 1; 322 | } 323 | 324 | window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN); 325 | if(window == NULL) 326 | { 327 | SDL_Log("Failed to create window.\n"); 328 | return 2; 329 | } 330 | 331 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); 332 | if(renderer == NULL) 333 | { 334 | SDL_Log("Failed to create renderer.\n"); 335 | return 3; 336 | } 337 | #endif 338 | 339 | loop_drawSomeText(); 340 | 341 | #ifdef SDL_GPU_VERSION_MAJOR 342 | GPU_Quit(); 343 | #else 344 | SDL_Quit(); 345 | #endif 346 | 347 | return 0; 348 | } 349 | -------------------------------------------------------------------------------- /SDL_FontCache.h: -------------------------------------------------------------------------------- 1 | /* 2 | SDL_FontCache v0.10.0: A font cache for SDL and SDL_ttf 3 | by Jonathan Dearborn 4 | Dedicated to the memory of Florian Hufsky 5 | 6 | License: 7 | The short: 8 | Use it however you'd like, but keep the copyright and license notice 9 | whenever these files or parts of them are distributed in uncompiled form. 10 | 11 | The long: 12 | Copyright (c) 2019 Jonathan Dearborn 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | */ 32 | 33 | #ifndef _SDL_FONTCACHE_H__ 34 | #define _SDL_FONTCACHE_H__ 35 | 36 | #include "SDL.h" 37 | #include "SDL_ttf.h" 38 | 39 | #ifdef FC_USE_SDL_GPU 40 | #include "SDL_gpu.h" 41 | #endif 42 | 43 | 44 | #include 45 | 46 | #ifdef __cplusplus 47 | extern "C" { 48 | #endif 49 | 50 | 51 | // Let's pretend this exists... 52 | #define TTF_STYLE_OUTLINE 16 53 | 54 | 55 | 56 | // Differences between SDL_Renderer and SDL_gpu 57 | #ifdef FC_USE_SDL_GPU 58 | #define FC_Rect GPU_Rect 59 | #define FC_Target GPU_Target 60 | #define FC_Image GPU_Image 61 | #define FC_Log GPU_LogError 62 | #else 63 | #define FC_Rect SDL_Rect 64 | #define FC_Target SDL_Renderer 65 | #define FC_Image SDL_Texture 66 | #define FC_Log SDL_Log 67 | #endif 68 | 69 | 70 | // SDL_FontCache types 71 | 72 | typedef enum 73 | { 74 | FC_ALIGN_LEFT, 75 | FC_ALIGN_CENTER, 76 | FC_ALIGN_RIGHT 77 | } FC_AlignEnum; 78 | 79 | typedef enum 80 | { 81 | FC_FILTER_NEAREST, 82 | FC_FILTER_LINEAR 83 | } FC_FilterEnum; 84 | 85 | typedef struct FC_Scale 86 | { 87 | float x; 88 | float y; 89 | 90 | } FC_Scale; 91 | 92 | typedef struct FC_Effect 93 | { 94 | FC_AlignEnum alignment; 95 | FC_Scale scale; 96 | SDL_Color color; 97 | 98 | } FC_Effect; 99 | 100 | // Opaque type 101 | typedef struct FC_Font FC_Font; 102 | 103 | 104 | typedef struct FC_GlyphData 105 | { 106 | SDL_Rect rect; 107 | int cache_level; 108 | 109 | } FC_GlyphData; 110 | 111 | 112 | 113 | 114 | // Object creation 115 | 116 | FC_Rect FC_MakeRect(float x, float y, float w, float h); 117 | 118 | FC_Scale FC_MakeScale(float x, float y); 119 | 120 | SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a); 121 | 122 | FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color); 123 | 124 | FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h); 125 | 126 | 127 | 128 | // Font object 129 | 130 | FC_Font* FC_CreateFont(void); 131 | 132 | #ifdef FC_USE_SDL_GPU 133 | Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); 134 | 135 | Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color); 136 | 137 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); 138 | #else 139 | Uint8 FC_LoadFont(FC_Font* font, SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style); 140 | 141 | Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color); 142 | 143 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style); 144 | #endif 145 | 146 | #ifndef FC_USE_SDL_GPU 147 | // note: handle SDL event types SDL_RENDER_TARGETS_RESET(>= SDL 2.0.2) and SDL_RENDER_DEVICE_RESET(>= SDL 2.0.4) 148 | void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType); 149 | #endif 150 | 151 | void FC_ClearFont(FC_Font* font); 152 | 153 | void FC_FreeFont(FC_Font* font); 154 | 155 | 156 | 157 | // Built-in loading strings 158 | 159 | char* FC_GetStringASCII(void); 160 | 161 | char* FC_GetStringLatin1(void); 162 | 163 | char* FC_GetStringASCII_Latin1(void); 164 | 165 | 166 | // UTF-8 to SDL_FontCache codepoint conversion 167 | 168 | /*! 169 | Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string. 170 | \param c A pointer to a string of proper UTF-8 character values. 171 | \param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints. 172 | */ 173 | Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer); 174 | 175 | /*! 176 | Parses the given codepoint and stores the UTF-8 bytes in 'result'. The result is NULL terminated. 177 | \param result A memory buffer for the UTF-8 values. Must be at least 5 bytes long. 178 | \param codepoint The Uint32 codepoint to parse (not UTF-32). 179 | */ 180 | void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint); 181 | 182 | 183 | // UTF-8 string operations 184 | 185 | /*! Allocates a new string of 'size' bytes that is already NULL-terminated. The NULL byte counts toward the size limit, as usual. Returns NULL if size is 0. */ 186 | char* U8_alloc(unsigned int size); 187 | 188 | /*! Deallocates the given string. */ 189 | void U8_free(char* string); 190 | 191 | /*! Allocates a copy of the given string. */ 192 | char* U8_strdup(const char* string); 193 | 194 | /*! Returns the number of UTF-8 characters in the given string. */ 195 | int U8_strlen(const char* string); 196 | 197 | /*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */ 198 | int U8_charsize(const char* character); 199 | 200 | /*! Copies the source multibyte character into the given buffer without overrunning it. Returns 0 on failure. */ 201 | int U8_charcpy(char* buffer, const char* source, int buffer_size); 202 | 203 | /*! Returns a pointer to the next UTF-8 character. */ 204 | const char* U8_next(const char* string); 205 | 206 | /*! Inserts a UTF-8 string into 'string' at the given position. Use a position of -1 to append. Returns 0 when unable to insert the string. */ 207 | int U8_strinsert(char* string, int position, const char* source, int max_bytes); 208 | 209 | /*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */ 210 | void U8_strdel(char* string, int position); 211 | 212 | 213 | // Internal settings 214 | 215 | /*! Sets the string from which to load the initial glyphs. Use this if you need upfront loading for any reason (such as lack of render-target support). */ 216 | void FC_SetLoadingString(FC_Font* font, const char* string); 217 | 218 | /*! Returns the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ 219 | unsigned int FC_GetBufferSize(void); 220 | 221 | /*! Changes the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */ 222 | void FC_SetBufferSize(unsigned int size); 223 | 224 | /*! Returns the width of a single horizontal tab in multiples of the width of a space (default: 4) */ 225 | unsigned int FC_GetTabWidth(void); 226 | 227 | /*! Changes the width of a horizontal tab in multiples of the width of a space (default: 4) */ 228 | void FC_SetTabWidth(unsigned int width_in_spaces); 229 | 230 | void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)); 231 | 232 | FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale); 233 | 234 | 235 | // Custom caching 236 | 237 | /*! Returns the number of cache levels that are active. */ 238 | int FC_GetNumCacheLevels(FC_Font* font); 239 | 240 | /*! Returns the cache source texture at the given cache level. */ 241 | FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level); 242 | 243 | // TODO: Specify ownership of the texture (should be shareable) 244 | /*! Sets a cache source texture for rendering. New cache levels must be sequential. */ 245 | Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture); 246 | 247 | /*! Copies the given surface to the given cache level as a texture. New cache levels must be sequential. */ 248 | Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface); 249 | 250 | 251 | /*! Returns the number of codepoints that are stored in the font's glyph data map. */ 252 | unsigned int FC_GetNumCodepoints(FC_Font* font); 253 | 254 | /*! Copies the stored codepoints into the given array. */ 255 | void FC_GetCodepoints(FC_Font* font, Uint32* result); 256 | 257 | /*! Stores the glyph data for the given codepoint in 'result'. Returns 0 if the codepoint was not found in the cache. */ 258 | Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint); 259 | 260 | /*! Sets the glyph data for the given codepoint. Duplicates are not checked. Returns a pointer to the stored data. */ 261 | FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data); 262 | 263 | 264 | // Rendering 265 | 266 | FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...); 267 | FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...); 268 | FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...); 269 | FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...); 270 | FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...); 271 | 272 | FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...); 273 | FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...); 274 | FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...); 275 | FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...); 276 | FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...); 277 | 278 | FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...); 279 | FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...); 280 | FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...); 281 | FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...); 282 | FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...); 283 | 284 | 285 | // Getters 286 | 287 | FC_FilterEnum FC_GetFilterMode(FC_Font* font); 288 | Uint16 FC_GetLineHeight(FC_Font* font); 289 | Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...); 290 | Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...); 291 | 292 | // Returns a 1-pixel wide box in front of the character in the given position (index) 293 | FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...); 294 | Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...); 295 | 296 | int FC_GetAscent(FC_Font* font, const char* formatted_text, ...); 297 | int FC_GetDescent(FC_Font* font, const char* formatted_text, ...); 298 | int FC_GetBaseline(FC_Font* font); 299 | int FC_GetSpacing(FC_Font* font); 300 | int FC_GetLineSpacing(FC_Font* font); 301 | Uint16 FC_GetMaxWidth(FC_Font* font); 302 | SDL_Color FC_GetDefaultColor(FC_Font* font); 303 | 304 | FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...); 305 | 306 | Uint8 FC_InRect(float x, float y, FC_Rect input_rect); 307 | // Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index) 308 | Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...); 309 | 310 | // Returns the number of characters in the new wrapped text written into `result`. 311 | int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...); 312 | 313 | // Setters 314 | 315 | void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter); 316 | void FC_SetSpacing(FC_Font* font, int LetterSpacing); 317 | void FC_SetLineSpacing(FC_Font* font, int LineSpacing); 318 | void FC_SetDefaultColor(FC_Font* font, SDL_Color color); 319 | 320 | 321 | #ifdef __cplusplus 322 | } 323 | #endif 324 | 325 | 326 | 327 | #endif 328 | -------------------------------------------------------------------------------- /SDL_FontCache.c: -------------------------------------------------------------------------------- 1 | /* 2 | SDL_FontCache: A font cache for SDL and SDL_ttf 3 | by Jonathan Dearborn 4 | 5 | See SDL_FontCache.h for license info. 6 | */ 7 | 8 | #include "SDL_FontCache.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // Visual C does not support static inline 15 | #ifndef static_inline 16 | #ifdef _MSC_VER 17 | #define static_inline static 18 | #else 19 | #define static_inline static inline 20 | #endif 21 | #endif 22 | 23 | #if SDL_VERSION_ATLEAST(2,0,0) 24 | #define FC_GET_ALPHA(sdl_color) ((sdl_color).a) 25 | #else 26 | #define FC_GET_ALPHA(sdl_color) ((sdl_color).unused) 27 | #endif 28 | 29 | // Need SDL_RenderIsClipEnabled() for proper clipping support 30 | #if SDL_VERSION_ATLEAST(2,0,4) 31 | #define ENABLE_SDL_CLIPPING 32 | #endif 33 | 34 | #define FC_MIN(a,b) ((a) < (b)? (a) : (b)) 35 | #define FC_MAX(a,b) ((a) > (b)? (a) : (b)) 36 | 37 | 38 | // vsnprintf replacement from Valentin Milea: 39 | // http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 40 | #if defined(_MSC_VER) && _MSC_VER < 1900 41 | 42 | #define snprintf c99_snprintf 43 | #define vsnprintf c99_vsnprintf 44 | 45 | __inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) 46 | { 47 | int count = -1; 48 | 49 | if (size != 0) 50 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); 51 | if (count == -1) 52 | count = _vscprintf(format, ap); 53 | 54 | return count; 55 | } 56 | 57 | __inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) 58 | { 59 | int count; 60 | va_list ap; 61 | 62 | va_start(ap, format); 63 | count = c99_vsnprintf(outBuf, size, format, ap); 64 | va_end(ap); 65 | 66 | return count; 67 | } 68 | 69 | #endif 70 | 71 | 72 | #define FC_EXTRACT_VARARGS(buffer, start_args) \ 73 | { \ 74 | va_list lst; \ 75 | va_start(lst, start_args); \ 76 | vsnprintf(buffer, fc_buffer_size, start_args, lst); \ 77 | va_end(lst); \ 78 | } 79 | 80 | // Extra pixels of padding around each glyph to avoid linear filtering artifacts 81 | #define FC_CACHE_PADDING 1 82 | 83 | 84 | 85 | static Uint8 has_clip(FC_Target* dest) 86 | { 87 | #ifdef FC_USE_SDL_GPU 88 | return dest->use_clip_rect; 89 | #elif defined(ENABLE_SDL_CLIPPING) 90 | return SDL_RenderIsClipEnabled(dest); 91 | #else 92 | return 0; 93 | #endif 94 | } 95 | 96 | static FC_Rect get_clip(FC_Target* dest) 97 | { 98 | #ifdef FC_USE_SDL_GPU 99 | return dest->clip_rect; 100 | #elif defined(ENABLE_SDL_CLIPPING) 101 | SDL_Rect r; 102 | SDL_RenderGetClipRect(dest, &r); 103 | return r; 104 | #else 105 | SDL_Rect r = {0, 0, 0, 0}; 106 | return r; 107 | #endif 108 | } 109 | 110 | static void set_clip(FC_Target* dest, FC_Rect* rect) 111 | { 112 | #ifdef FC_USE_SDL_GPU 113 | if(rect != NULL) 114 | GPU_SetClipRect(dest, *rect); 115 | else 116 | GPU_UnsetClip(dest); 117 | #elif defined(ENABLE_SDL_CLIPPING) 118 | SDL_RenderSetClipRect(dest, rect); 119 | #endif 120 | } 121 | 122 | static void set_color(FC_Image* src, Uint8 r, Uint8 g, Uint8 b, Uint8 a) 123 | { 124 | #ifdef FC_USE_SDL_GPU 125 | GPU_SetRGBA(src, r, g, b, a); 126 | #else 127 | SDL_SetTextureColorMod(src, r, g, b); 128 | SDL_SetTextureAlphaMod(src, a); 129 | #endif 130 | } 131 | 132 | 133 | 134 | static char* new_concat(const char* a, const char* b) 135 | { 136 | // Create new buffer 137 | unsigned int size = strlen(a) + strlen(b); 138 | char* new_string = (char*)malloc(size+1); 139 | 140 | // Concatenate strings in the new buffer 141 | strcpy(new_string, a); 142 | strcat(new_string, b); 143 | 144 | return new_string; 145 | } 146 | 147 | static char* replace_concat(char** a, const char* b) 148 | { 149 | char* new_string = new_concat(*a, b); 150 | free(*a); 151 | *a = new_string; 152 | return *a; 153 | } 154 | 155 | 156 | // Width of a tab in units of the space width (sorry, no tab alignment!) 157 | static unsigned int fc_tab_width = 4; 158 | 159 | // Shared buffer for variadic text 160 | static char* fc_buffer = NULL; 161 | static unsigned int fc_buffer_size = 1024; 162 | 163 | static Uint8 fc_has_render_target_support = 0; 164 | 165 | // The number of fonts that has been created but not freed 166 | static int NUM_EXISTING_FONTS = 0; 167 | 168 | // Globals for GetString functions 169 | static char* ASCII_STRING = NULL; 170 | static char* LATIN_1_STRING = NULL; 171 | static char* ASCII_LATIN_1_STRING = NULL; 172 | 173 | char* FC_GetStringASCII(void) 174 | { 175 | if(ASCII_STRING == NULL) 176 | { 177 | int i; 178 | char c; 179 | ASCII_STRING = (char*)malloc(512); 180 | memset(ASCII_STRING, 0, 512); 181 | i = 0; 182 | c = 32; 183 | while(1) 184 | { 185 | ASCII_STRING[i] = c; 186 | if(c == 126) 187 | break; 188 | ++i; 189 | ++c; 190 | } 191 | } 192 | return U8_strdup(ASCII_STRING); 193 | } 194 | 195 | char* FC_GetStringLatin1(void) 196 | { 197 | if(LATIN_1_STRING == NULL) 198 | { 199 | int i; 200 | unsigned char c; 201 | LATIN_1_STRING = (char*)malloc(512); 202 | memset(LATIN_1_STRING, 0, 512); 203 | i = 0; 204 | c = 0xA0; 205 | while(1) 206 | { 207 | LATIN_1_STRING[i] = 0xC2; 208 | LATIN_1_STRING[i+1] = c; 209 | if(c == 0xBF) 210 | break; 211 | i += 2; 212 | ++c; 213 | } 214 | i += 2; 215 | c = 0x80; 216 | while(1) 217 | { 218 | LATIN_1_STRING[i] = 0xC3; 219 | LATIN_1_STRING[i+1] = c; 220 | if(c == 0xBF) 221 | break; 222 | i += 2; 223 | ++c; 224 | } 225 | } 226 | return U8_strdup(LATIN_1_STRING); 227 | } 228 | 229 | char* FC_GetStringASCII_Latin1(void) 230 | { 231 | if(ASCII_LATIN_1_STRING == NULL) 232 | ASCII_LATIN_1_STRING = new_concat(FC_GetStringASCII(), FC_GetStringLatin1()); 233 | 234 | return U8_strdup(ASCII_LATIN_1_STRING); 235 | } 236 | 237 | FC_Rect FC_MakeRect(float x, float y, float w, float h) 238 | { 239 | FC_Rect r = {x, y, w, h}; 240 | return r; 241 | } 242 | 243 | FC_Scale FC_MakeScale(float x, float y) 244 | { 245 | FC_Scale s = {x, y}; 246 | 247 | return s; 248 | } 249 | 250 | SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) 251 | { 252 | SDL_Color c = {r, g, b, a}; 253 | 254 | return c; 255 | } 256 | 257 | FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color) 258 | { 259 | FC_Effect e; 260 | 261 | e.alignment = alignment; 262 | e.scale = scale; 263 | e.color = color; 264 | 265 | return e; 266 | } 267 | 268 | FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h) 269 | { 270 | FC_GlyphData gd; 271 | 272 | gd.rect.x = x; 273 | gd.rect.y = y; 274 | gd.rect.w = w; 275 | gd.rect.h = h; 276 | gd.cache_level = cache_level; 277 | 278 | return gd; 279 | } 280 | 281 | // Enough to hold all of the ascii characters and some. 282 | #define FC_DEFAULT_NUM_BUCKETS 300 283 | 284 | typedef struct FC_MapNode 285 | { 286 | Uint32 key; 287 | FC_GlyphData value; 288 | struct FC_MapNode* next; 289 | 290 | } FC_MapNode; 291 | 292 | typedef struct FC_Map 293 | { 294 | int num_buckets; 295 | FC_MapNode** buckets; 296 | } FC_Map; 297 | 298 | 299 | 300 | static FC_Map* FC_MapCreate(int num_buckets) 301 | { 302 | int i; 303 | FC_Map* map = (FC_Map*)malloc(sizeof(FC_Map)); 304 | 305 | map->num_buckets = num_buckets; 306 | map->buckets = (FC_MapNode**)malloc(num_buckets * sizeof(FC_MapNode*)); 307 | 308 | for(i = 0; i < num_buckets; ++i) 309 | { 310 | map->buckets[i] = NULL; 311 | } 312 | 313 | return map; 314 | } 315 | 316 | /*static void FC_MapClear(FC_Map* map) 317 | { 318 | int i; 319 | if(map == NULL) 320 | return; 321 | 322 | // Go through each bucket 323 | for(i = 0; i < map->num_buckets; ++i) 324 | { 325 | // Delete the nodes in order 326 | FC_MapNode* node = map->buckets[i]; 327 | while(node != NULL) 328 | { 329 | FC_MapNode* last = node; 330 | node = node->next; 331 | free(last); 332 | } 333 | // Set the bucket to empty 334 | map->buckets[i] = NULL; 335 | } 336 | }*/ 337 | 338 | static void FC_MapFree(FC_Map* map) 339 | { 340 | int i; 341 | if(map == NULL) 342 | return; 343 | 344 | // Go through each bucket 345 | for(i = 0; i < map->num_buckets; ++i) 346 | { 347 | // Delete the nodes in order 348 | FC_MapNode* node = map->buckets[i]; 349 | while(node != NULL) 350 | { 351 | FC_MapNode* last = node; 352 | node = node->next; 353 | free(last); 354 | } 355 | } 356 | 357 | free(map->buckets); 358 | free(map); 359 | } 360 | 361 | // Note: Does not handle duplicates in any special way. 362 | static FC_GlyphData* FC_MapInsert(FC_Map* map, Uint32 codepoint, FC_GlyphData glyph) 363 | { 364 | Uint32 index; 365 | FC_MapNode* node; 366 | if(map == NULL) 367 | return NULL; 368 | 369 | // Get index for bucket 370 | index = codepoint % map->num_buckets; 371 | 372 | // If this bucket is empty, create a node and return its value 373 | if(map->buckets[index] == NULL) 374 | { 375 | node = map->buckets[index] = (FC_MapNode*)malloc(sizeof(FC_MapNode)); 376 | node->key = codepoint; 377 | node->value = glyph; 378 | node->next = NULL; 379 | return &node->value; 380 | } 381 | 382 | for(node = map->buckets[index]; node != NULL; node = node->next) 383 | { 384 | // Find empty node and add a new one on. 385 | if(node->next == NULL) 386 | { 387 | node->next = (FC_MapNode*)malloc(sizeof(FC_MapNode)); 388 | node = node->next; 389 | 390 | node->key = codepoint; 391 | node->value = glyph; 392 | node->next = NULL; 393 | return &node->value; 394 | } 395 | } 396 | 397 | return NULL; 398 | } 399 | 400 | static FC_GlyphData* FC_MapFind(FC_Map* map, Uint32 codepoint) 401 | { 402 | Uint32 index; 403 | FC_MapNode* node; 404 | if(map == NULL) 405 | return NULL; 406 | 407 | // Get index for bucket 408 | index = codepoint % map->num_buckets; 409 | 410 | // Go through list until we find a match 411 | for(node = map->buckets[index]; node != NULL; node = node->next) 412 | { 413 | if(node->key == codepoint) 414 | return &node->value; 415 | } 416 | 417 | return NULL; 418 | } 419 | 420 | 421 | 422 | struct FC_Font 423 | { 424 | #ifndef FC_USE_SDL_GPU 425 | SDL_Renderer* renderer; 426 | #endif 427 | 428 | TTF_Font* ttf_source; // TTF_Font source of characters 429 | Uint8 owns_ttf_source; // Can we delete the TTF_Font ourselves? 430 | 431 | FC_FilterEnum filter; 432 | 433 | SDL_Color default_color; 434 | Uint16 height; 435 | 436 | Uint16 maxWidth; 437 | Uint16 baseline; 438 | int ascent; 439 | int descent; 440 | 441 | int lineSpacing; 442 | int letterSpacing; 443 | 444 | // Uses 32-bit (4-byte) Unicode codepoints to refer to each glyph 445 | // Codepoints are little endian (reversed from UTF-8) so that something like 0x00000005 is ASCII 5 and the map can be indexed by ASCII values 446 | FC_Map* glyphs; 447 | 448 | FC_GlyphData last_glyph; // Texture packing cursor 449 | int glyph_cache_size; 450 | int glyph_cache_count; 451 | FC_Image** glyph_cache; 452 | 453 | char* loading_string; 454 | 455 | }; 456 | 457 | // Private 458 | static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight); 459 | 460 | 461 | static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); 462 | static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); 463 | static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text); 464 | 465 | 466 | static_inline SDL_Surface* FC_CreateSurface32(Uint32 width, Uint32 height) 467 | { 468 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 469 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); 470 | #else 471 | return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); 472 | #endif 473 | } 474 | 475 | 476 | char* U8_alloc(unsigned int size) 477 | { 478 | char* result; 479 | if(size == 0) 480 | return NULL; 481 | 482 | result = (char*)malloc(size); 483 | result[0] = '\0'; 484 | 485 | return result; 486 | } 487 | 488 | void U8_free(char* string) 489 | { 490 | free(string); 491 | } 492 | 493 | char* U8_strdup(const char* string) 494 | { 495 | char* result; 496 | if(string == NULL) 497 | return NULL; 498 | 499 | result = (char*)malloc(strlen(string)+1); 500 | strcpy(result, string); 501 | 502 | return result; 503 | } 504 | 505 | int U8_strlen(const char* string) 506 | { 507 | int length = 0; 508 | if(string == NULL) 509 | return 0; 510 | 511 | while(*string != '\0') 512 | { 513 | string = U8_next(string); 514 | ++length; 515 | } 516 | 517 | return length; 518 | } 519 | 520 | int U8_charsize(const char* character) 521 | { 522 | if(character == NULL) 523 | return 0; 524 | 525 | if((unsigned char)*character <= 0x7F) 526 | return 1; 527 | else if((unsigned char)*character < 0xE0) 528 | return 2; 529 | else if((unsigned char)*character < 0xF0) 530 | return 3; 531 | else 532 | return 4; 533 | return 1; 534 | } 535 | 536 | int U8_charcpy(char* buffer, const char* source, int buffer_size) 537 | { 538 | int charsize; 539 | if(buffer == NULL || source == NULL || buffer_size < 1) 540 | return 0; 541 | 542 | charsize = U8_charsize(source); 543 | if(charsize > buffer_size) 544 | return 0; 545 | 546 | memcpy(buffer, source, charsize); 547 | return charsize; 548 | } 549 | 550 | const char* U8_next(const char* string) 551 | { 552 | return string + U8_charsize(string); 553 | } 554 | 555 | int U8_strinsert(char* string, int position, const char* source, int max_bytes) 556 | { 557 | int pos_u8char; 558 | int len; 559 | int add_len; 560 | int ulen; 561 | const char* string_start = string; 562 | 563 | if(string == NULL || source == NULL) 564 | return 0; 565 | 566 | len = strlen(string); 567 | add_len = strlen(source); 568 | ulen = U8_strlen(string); 569 | 570 | if(position == -1) 571 | position = ulen; 572 | 573 | if(position < 0 || position > ulen || len + add_len + 1 > max_bytes) 574 | return 0; 575 | 576 | // Move string pointer to the proper position 577 | pos_u8char = 0; 578 | while(*string != '\0' && pos_u8char < position) 579 | { 580 | string = (char*)U8_next(string); 581 | ++pos_u8char; 582 | } 583 | 584 | // Move the rest of the string out of the way 585 | memmove(string + add_len, string, len - (string - string_start) + 1); 586 | 587 | // Copy in the new characters 588 | memcpy(string, source, add_len); 589 | 590 | return 1; 591 | } 592 | 593 | void U8_strdel(char* string, int position) 594 | { 595 | if(string == NULL || position < 0) 596 | return; 597 | 598 | while(*string != '\0') 599 | { 600 | if(position == 0) 601 | { 602 | int chars_to_erase = U8_charsize(string); 603 | int remaining_bytes = strlen(string) + 1; 604 | memmove(string, string + chars_to_erase, remaining_bytes); 605 | break; 606 | } 607 | 608 | string = (char*)U8_next(string); 609 | --position; 610 | } 611 | } 612 | 613 | 614 | 615 | 616 | 617 | static_inline FC_Rect FC_RectUnion(FC_Rect A, FC_Rect B) 618 | { 619 | float x,x2,y,y2; 620 | x = FC_MIN(A.x, B.x); 621 | y = FC_MIN(A.y, B.y); 622 | x2 = FC_MAX(A.x+A.w, B.x+B.w); 623 | y2 = FC_MAX(A.y+A.h, B.y+B.h); 624 | { 625 | FC_Rect result = {x, y, FC_MAX(0, x2 - x), FC_MAX(0, y2 - y)}; 626 | return result; 627 | } 628 | } 629 | 630 | // Adapted from SDL_IntersectRect 631 | static_inline FC_Rect FC_RectIntersect(FC_Rect A, FC_Rect B) 632 | { 633 | FC_Rect result; 634 | float Amin, Amax, Bmin, Bmax; 635 | 636 | // Horizontal intersection 637 | Amin = A.x; 638 | Amax = Amin + A.w; 639 | Bmin = B.x; 640 | Bmax = Bmin + B.w; 641 | if(Bmin > Amin) 642 | Amin = Bmin; 643 | result.x = Amin; 644 | if(Bmax < Amax) 645 | Amax = Bmax; 646 | result.w = Amax - Amin > 0 ? Amax - Amin : 0; 647 | 648 | // Vertical intersection 649 | Amin = A.y; 650 | Amax = Amin + A.h; 651 | Bmin = B.y; 652 | Bmax = Bmin + B.h; 653 | if(Bmin > Amin) 654 | Amin = Bmin; 655 | result.y = Amin; 656 | if(Bmax < Amax) 657 | Amax = Bmax; 658 | result.h = Amax - Amin > 0 ? Amax - Amin : 0; 659 | 660 | return result; 661 | } 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) 677 | { 678 | float w = srcrect->w * xscale; 679 | float h = srcrect->h * yscale; 680 | FC_Rect result; 681 | 682 | // FIXME: Why does the scaled offset look so wrong? 683 | #ifdef FC_USE_SDL_GPU 684 | { 685 | GPU_Rect r = *srcrect; 686 | GPU_BlitScale(src, &r, dest, x + xscale*r.w/2.0f, y + r.h/2.0f, xscale, yscale); 687 | } 688 | #else 689 | { 690 | SDL_RendererFlip flip = SDL_FLIP_NONE; 691 | if(xscale < 0) 692 | { 693 | xscale = -xscale; 694 | flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_HORIZONTAL); 695 | } 696 | if(yscale < 0) 697 | { 698 | yscale = -yscale; 699 | flip = (SDL_RendererFlip) ((int)flip | (int)SDL_FLIP_VERTICAL); 700 | } 701 | 702 | SDL_Rect r = *srcrect; 703 | SDL_Rect dr = {(int)x, (int)y, (int)(xscale*r.w), (int)(yscale*r.h)}; 704 | SDL_RenderCopyEx(dest, src, &r, &dr, 0, NULL, flip); 705 | } 706 | #endif 707 | 708 | result.x = x; 709 | result.y = y; 710 | result.w = w; 711 | result.h = h; 712 | return result; 713 | } 714 | 715 | static FC_Rect (*fc_render_callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale) = &FC_DefaultRenderCallback; 716 | 717 | void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale)) 718 | { 719 | if(callback == NULL) 720 | fc_render_callback = &FC_DefaultRenderCallback; 721 | else 722 | fc_render_callback = callback; 723 | } 724 | 725 | void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint) 726 | { 727 | char a, b, c, d; 728 | 729 | if(result == NULL) 730 | return; 731 | 732 | a = (codepoint >> 24) & 0xFF; 733 | b = (codepoint >> 16) & 0xFF; 734 | c = (codepoint >> 8) & 0xFF; 735 | d = codepoint & 0xFF; 736 | 737 | if(a == 0) 738 | { 739 | if(b == 0) 740 | { 741 | if(c == 0) 742 | { 743 | result[0] = d; 744 | result[1] = '\0'; 745 | } 746 | else 747 | { 748 | result[0] = c; 749 | result[1] = d; 750 | result[2] = '\0'; 751 | } 752 | } 753 | else 754 | { 755 | result[0] = b; 756 | result[1] = c; 757 | result[2] = d; 758 | result[3] = '\0'; 759 | } 760 | } 761 | else 762 | { 763 | result[0] = a; 764 | result[1] = b; 765 | result[2] = c; 766 | result[3] = d; 767 | result[4] = '\0'; 768 | } 769 | } 770 | 771 | Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer) 772 | { 773 | Uint32 result = 0; 774 | const char* str; 775 | if(c == NULL || *c == NULL) 776 | return 0; 777 | 778 | str = *c; 779 | if((unsigned char)*str <= 0x7F) 780 | result = *str; 781 | else if((unsigned char)*str < 0xE0) 782 | { 783 | result |= (unsigned char)(*str) << 8; 784 | result |= (unsigned char)(*(str+1)); 785 | if(advance_pointer) 786 | *c += 1; 787 | } 788 | else if((unsigned char)*str < 0xF0) 789 | { 790 | result |= (unsigned char)(*str) << 16; 791 | result |= (unsigned char)(*(str+1)) << 8; 792 | result |= (unsigned char)(*(str+2)); 793 | if(advance_pointer) 794 | *c += 2; 795 | } 796 | else 797 | { 798 | result |= (unsigned char)(*str) << 24; 799 | result |= (unsigned char)(*(str+1)) << 16; 800 | result |= (unsigned char)(*(str+2)) << 8; 801 | result |= (unsigned char)(*(str+3)); 802 | if(advance_pointer) 803 | *c += 3; 804 | } 805 | return result; 806 | } 807 | 808 | 809 | void FC_SetLoadingString(FC_Font* font, const char* string) 810 | { 811 | if(font == NULL) 812 | return; 813 | 814 | free(font->loading_string); 815 | font->loading_string = U8_strdup(string); 816 | } 817 | 818 | 819 | unsigned int FC_GetBufferSize(void) 820 | { 821 | return fc_buffer_size; 822 | } 823 | 824 | void FC_SetBufferSize(unsigned int size) 825 | { 826 | free(fc_buffer); 827 | if(size > 0) 828 | { 829 | fc_buffer_size = size; 830 | fc_buffer = (char*)malloc(fc_buffer_size); 831 | } 832 | else 833 | fc_buffer = (char*)malloc(fc_buffer_size); 834 | } 835 | 836 | 837 | unsigned int FC_GetTabWidth(void) 838 | { 839 | return fc_tab_width; 840 | } 841 | 842 | void FC_SetTabWidth(unsigned int width_in_spaces) 843 | { 844 | fc_tab_width = width_in_spaces; 845 | } 846 | 847 | 848 | 849 | 850 | 851 | // Constructors 852 | 853 | static void FC_Init(FC_Font* font) 854 | { 855 | if(font == NULL) 856 | return; 857 | 858 | #ifndef FC_USE_SDL_GPU 859 | font->renderer = NULL; 860 | #endif 861 | 862 | font->ttf_source = NULL; 863 | font->owns_ttf_source = 0; 864 | 865 | font->filter = FC_FILTER_NEAREST; 866 | 867 | font->default_color.r = 0; 868 | font->default_color.g = 0; 869 | font->default_color.b = 0; 870 | FC_GET_ALPHA(font->default_color) = 255; 871 | 872 | font->height = 0; // ascent+descent 873 | 874 | font->maxWidth = 0; 875 | font->baseline = 0; 876 | font->ascent = 0; 877 | font->descent = 0; 878 | 879 | font->lineSpacing = 0; 880 | font->letterSpacing = 0; 881 | 882 | // Give a little offset for when filtering/mipmaps are used. Depending on mipmap level, this will still not be enough. 883 | font->last_glyph.rect.x = FC_CACHE_PADDING; 884 | font->last_glyph.rect.y = FC_CACHE_PADDING; 885 | font->last_glyph.rect.w = 0; 886 | font->last_glyph.rect.h = 0; 887 | font->last_glyph.cache_level = 0; 888 | 889 | if(font->glyphs != NULL) 890 | FC_MapFree(font->glyphs); 891 | 892 | font->glyphs = FC_MapCreate(FC_DEFAULT_NUM_BUCKETS); 893 | 894 | font->glyph_cache_size = 3; 895 | font->glyph_cache_count = 0; 896 | 897 | 898 | font->glyph_cache = (FC_Image**)malloc(font->glyph_cache_size * sizeof(FC_Image*)); 899 | 900 | if (font->loading_string == NULL) 901 | font->loading_string = FC_GetStringASCII(); 902 | 903 | if(fc_buffer == NULL) 904 | fc_buffer = (char*)malloc(fc_buffer_size); 905 | } 906 | 907 | static Uint8 FC_GrowGlyphCache(FC_Font* font) 908 | { 909 | if(font == NULL) 910 | return 0; 911 | #ifdef FC_USE_SDL_GPU 912 | GPU_Image* new_level = GPU_CreateImage(font->height * 12, font->height * 12, GPU_FORMAT_RGBA); 913 | GPU_SetAnchor(new_level, 0.5f, 0.5f); // Just in case the default is different 914 | #else 915 | SDL_Texture* new_level = SDL_CreateTexture(font->renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, font->height * 12, font->height * 12); 916 | #endif 917 | if(new_level == NULL || !FC_SetGlyphCacheLevel(font, font->glyph_cache_count, new_level)) 918 | { 919 | FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); 920 | #ifdef FC_USE_SDL_GPU 921 | GPU_FreeImage(new_level); 922 | #else 923 | SDL_DestroyTexture(new_level); 924 | #endif 925 | return 0; 926 | } 927 | // bug: we do not have the correct color here, this might be the wrong color! 928 | // , most functions use set_color_for_all_caches() 929 | // - for evading this bug, you must use FC_SetDefaultColor(), before using any draw functions 930 | set_color(new_level, font->default_color.r, font->default_color.g, font->default_color.b, FC_GET_ALPHA(font->default_color)); 931 | #ifndef FC_USE_SDL_GPU 932 | { 933 | Uint8 r, g, b, a; 934 | SDL_Texture* prev_target = SDL_GetRenderTarget(font->renderer); 935 | SDL_Rect prev_clip, prev_viewport; 936 | int prev_logicalw, prev_logicalh; 937 | Uint8 prev_clip_enabled; 938 | float prev_scalex, prev_scaley; 939 | // only backup if previous target existed (SDL will preserve them for the default target) 940 | if (prev_target) { 941 | prev_clip_enabled = has_clip(font->renderer); 942 | if (prev_clip_enabled) 943 | prev_clip = get_clip(font->renderer); 944 | SDL_RenderGetViewport(font->renderer, &prev_viewport); 945 | SDL_RenderGetScale(font->renderer, &prev_scalex, &prev_scaley); 946 | SDL_RenderGetLogicalSize(font->renderer, &prev_logicalw, &prev_logicalh); 947 | } 948 | SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); 949 | SDL_SetRenderTarget(font->renderer, new_level); 950 | SDL_GetRenderDrawColor(font->renderer, &r, &g, &b, &a); 951 | SDL_SetRenderDrawColor(font->renderer, 0, 0, 0, 0); 952 | SDL_RenderClear(font->renderer); 953 | SDL_SetRenderDrawColor(font->renderer, r, g, b, a); 954 | SDL_SetRenderTarget(font->renderer, prev_target); 955 | if (prev_target) { 956 | if (prev_clip_enabled) 957 | set_clip(font->renderer, &prev_clip); 958 | if (prev_logicalw && prev_logicalh) 959 | SDL_RenderSetLogicalSize(font->renderer, prev_logicalw, prev_logicalh); 960 | else { 961 | SDL_RenderSetViewport(font->renderer, &prev_viewport); 962 | SDL_RenderSetScale(font->renderer, prev_scalex, prev_scaley); 963 | } 964 | } 965 | } 966 | #endif 967 | return 1; 968 | } 969 | 970 | Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface) 971 | { 972 | if(font == NULL || data_surface == NULL) 973 | return 0; 974 | #ifdef FC_USE_SDL_GPU 975 | GPU_Image* new_level = GPU_CopyImageFromSurface(data_surface); 976 | GPU_SetAnchor(new_level, 0.5f, 0.5f); // Just in case the default is different 977 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) 978 | GPU_SetImageFilter(new_level, GPU_FILTER_LINEAR); 979 | else 980 | GPU_SetImageFilter(new_level, GPU_FILTER_NEAREST); 981 | #else 982 | SDL_Texture* new_level; 983 | if(!fc_has_render_target_support) 984 | new_level = SDL_CreateTextureFromSurface(font->renderer, data_surface); 985 | else 986 | { 987 | // Must upload with render target enabled so we can put more glyphs on later 988 | SDL_Renderer* renderer = font->renderer; 989 | 990 | // Set filter mode for new texture 991 | char old_filter_mode[16]; // Save it so we can change the hint value in the meantime 992 | const char* old_filter_hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY); 993 | if(!old_filter_hint) 994 | old_filter_hint = "nearest"; 995 | snprintf(old_filter_mode, 16, "%s", old_filter_hint); 996 | 997 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) 998 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); 999 | else 1000 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 1001 | 1002 | new_level = SDL_CreateTexture(renderer, data_surface->format->format, SDL_TEXTUREACCESS_TARGET, data_surface->w, data_surface->h); 1003 | SDL_SetTextureBlendMode(new_level, SDL_BLENDMODE_BLEND); 1004 | 1005 | // Reset filter mode for the temp texture 1006 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 1007 | 1008 | { 1009 | Uint8 r, g, b, a; 1010 | SDL_Texture* temp = SDL_CreateTextureFromSurface(renderer, data_surface); 1011 | SDL_Texture* prev_target = SDL_GetRenderTarget(renderer); 1012 | SDL_Rect prev_clip, prev_viewport; 1013 | int prev_logicalw, prev_logicalh; 1014 | Uint8 prev_clip_enabled; 1015 | float prev_scalex, prev_scaley; 1016 | // only backup if previous target existed (SDL will preserve them for the default target) 1017 | if (prev_target) { 1018 | prev_clip_enabled = has_clip(renderer); 1019 | if (prev_clip_enabled) 1020 | prev_clip = get_clip(renderer); 1021 | SDL_RenderGetViewport(renderer, &prev_viewport); 1022 | SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley); 1023 | SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh); 1024 | } 1025 | SDL_SetTextureBlendMode(temp, SDL_BLENDMODE_NONE); 1026 | SDL_SetRenderTarget(renderer, new_level); 1027 | 1028 | SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a); 1029 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); 1030 | SDL_RenderClear(renderer); 1031 | SDL_SetRenderDrawColor(renderer, r, g, b, a); 1032 | 1033 | SDL_RenderCopy(renderer, temp, NULL, NULL); 1034 | SDL_SetRenderTarget(renderer, prev_target); 1035 | if (prev_target) { 1036 | if (prev_clip_enabled) 1037 | set_clip(renderer, &prev_clip); 1038 | if (prev_logicalw && prev_logicalh) 1039 | SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh); 1040 | else { 1041 | SDL_RenderSetViewport(renderer, &prev_viewport); 1042 | SDL_RenderSetScale(renderer, prev_scalex, prev_scaley); 1043 | } 1044 | } 1045 | 1046 | SDL_DestroyTexture(temp); 1047 | } 1048 | 1049 | // Reset to the old filter value 1050 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, old_filter_mode); 1051 | 1052 | } 1053 | #endif 1054 | if(new_level == NULL || !FC_SetGlyphCacheLevel(font, cache_level, new_level)) 1055 | { 1056 | FC_Log("Error: SDL_FontCache ran out of packing space and could not add another cache level.\n"); 1057 | #ifdef FC_USE_SDL_GPU 1058 | GPU_FreeImage(new_level); 1059 | #else 1060 | SDL_DestroyTexture(new_level); 1061 | #endif 1062 | return 0; 1063 | } 1064 | return 1; 1065 | } 1066 | 1067 | static FC_GlyphData* FC_PackGlyphData(FC_Font* font, Uint32 codepoint, Uint16 width, Uint16 maxWidth, Uint16 maxHeight) 1068 | { 1069 | FC_Map* glyphs = font->glyphs; 1070 | FC_GlyphData* last_glyph = &font->last_glyph; 1071 | Uint16 height = font->height + FC_CACHE_PADDING; 1072 | 1073 | // TAB is special! 1074 | if(codepoint == '\t') 1075 | { 1076 | FC_GlyphData spaceGlyph; 1077 | FC_GetGlyphData(font, &spaceGlyph, ' '); 1078 | width = fc_tab_width * spaceGlyph.rect.w; 1079 | } 1080 | 1081 | if(last_glyph->rect.x + last_glyph->rect.w + width >= maxWidth - FC_CACHE_PADDING) 1082 | { 1083 | if(last_glyph->rect.y + height + height >= maxHeight - FC_CACHE_PADDING) 1084 | { 1085 | // Get ready to pack on the next cache level when it is ready 1086 | last_glyph->cache_level = font->glyph_cache_count; 1087 | last_glyph->rect.x = FC_CACHE_PADDING; 1088 | last_glyph->rect.y = FC_CACHE_PADDING; 1089 | last_glyph->rect.w = 0; 1090 | return NULL; 1091 | } 1092 | else 1093 | { 1094 | // Go to next row 1095 | last_glyph->rect.x = FC_CACHE_PADDING; 1096 | last_glyph->rect.y += height; 1097 | last_glyph->rect.w = 0; 1098 | } 1099 | } 1100 | 1101 | // Move to next space 1102 | last_glyph->rect.x += last_glyph->rect.w + 1 + FC_CACHE_PADDING; 1103 | last_glyph->rect.w = width; 1104 | 1105 | return FC_MapInsert(glyphs, codepoint, FC_MakeGlyphData(last_glyph->cache_level, last_glyph->rect.x, last_glyph->rect.y, last_glyph->rect.w, last_glyph->rect.h)); 1106 | } 1107 | 1108 | 1109 | FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level) 1110 | { 1111 | if(font == NULL || cache_level < 0 || cache_level > font->glyph_cache_count) 1112 | return NULL; 1113 | 1114 | return font->glyph_cache[cache_level]; 1115 | } 1116 | 1117 | Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture) 1118 | { 1119 | if(font == NULL || cache_level < 0) 1120 | return 0; 1121 | 1122 | // Must be sequentially added 1123 | if(cache_level > font->glyph_cache_count + 1) 1124 | return 0; 1125 | 1126 | if(cache_level == font->glyph_cache_count) 1127 | { 1128 | font->glyph_cache_count++; 1129 | 1130 | // Grow cache? 1131 | if(font->glyph_cache_count > font->glyph_cache_size) 1132 | { 1133 | // Copy old cache to new one 1134 | int i; 1135 | FC_Image** new_cache; 1136 | new_cache = (FC_Image**)malloc(font->glyph_cache_count * sizeof(FC_Image*)); 1137 | for(i = 0; i < font->glyph_cache_size; ++i) 1138 | new_cache[i] = font->glyph_cache[i]; 1139 | 1140 | // Save new cache 1141 | free(font->glyph_cache); 1142 | font->glyph_cache_size = font->glyph_cache_count; 1143 | font->glyph_cache = new_cache; 1144 | } 1145 | } 1146 | 1147 | font->glyph_cache[cache_level] = cache_texture; 1148 | return 1; 1149 | } 1150 | 1151 | 1152 | FC_Font* FC_CreateFont(void) 1153 | { 1154 | FC_Font* font; 1155 | 1156 | font = (FC_Font*)malloc(sizeof(FC_Font)); 1157 | memset(font, 0, sizeof(FC_Font)); 1158 | 1159 | FC_Init(font); 1160 | ++NUM_EXISTING_FONTS; 1161 | 1162 | return font; 1163 | } 1164 | 1165 | 1166 | // Assume this many will be enough... 1167 | #define FC_LOAD_MAX_SURFACES 10 1168 | 1169 | #ifdef FC_USE_SDL_GPU 1170 | Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color) 1171 | #else 1172 | Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color) 1173 | #endif 1174 | { 1175 | if(font == NULL || ttf == NULL) 1176 | return 0; 1177 | #ifndef FC_USE_SDL_GPU 1178 | if(renderer == NULL) 1179 | return 0; 1180 | #endif 1181 | 1182 | FC_ClearFont(font); 1183 | 1184 | 1185 | // Might as well check render target support here 1186 | #ifdef FC_USE_SDL_GPU 1187 | fc_has_render_target_support = GPU_IsFeatureEnabled(GPU_FEATURE_RENDER_TARGETS); 1188 | #else 1189 | SDL_RendererInfo info; 1190 | SDL_GetRendererInfo(renderer, &info); 1191 | fc_has_render_target_support = (info.flags & SDL_RENDERER_TARGETTEXTURE); 1192 | 1193 | font->renderer = renderer; 1194 | #endif 1195 | 1196 | font->ttf_source = ttf; 1197 | 1198 | //font->line_height = TTF_FontLineSkip(ttf); 1199 | font->height = TTF_FontHeight(ttf); 1200 | font->ascent = TTF_FontAscent(ttf); 1201 | font->descent = -TTF_FontDescent(ttf); 1202 | 1203 | // Some bug for certain fonts can result in an incorrect height. 1204 | if(font->height < font->ascent - font->descent) 1205 | font->height = font->ascent - font->descent; 1206 | 1207 | font->baseline = font->height - font->descent; 1208 | 1209 | font->default_color = color; 1210 | 1211 | { 1212 | SDL_Color white = {255, 255, 255, 255}; 1213 | SDL_Surface* glyph_surf; 1214 | char buff[5]; 1215 | const char* buff_ptr = buff; 1216 | const char* source_string; 1217 | Uint8 packed = 0; 1218 | 1219 | // Copy glyphs from the surface to the font texture and store the position data 1220 | // Pack row by row into a square texture 1221 | // Try figuring out dimensions that make sense for the font size. 1222 | unsigned int w = font->height*12; 1223 | unsigned int h = font->height*12; 1224 | SDL_Surface* surfaces[FC_LOAD_MAX_SURFACES]; 1225 | int num_surfaces = 1; 1226 | surfaces[0] = FC_CreateSurface32(w, h); 1227 | font->last_glyph.rect.x = FC_CACHE_PADDING; 1228 | font->last_glyph.rect.y = FC_CACHE_PADDING; 1229 | font->last_glyph.rect.w = 0; 1230 | font->last_glyph.rect.h = font->height; 1231 | 1232 | source_string = font->loading_string; 1233 | for(; *source_string != '\0'; source_string = U8_next(source_string)) 1234 | { 1235 | memset(buff, 0, 5); 1236 | if(!U8_charcpy(buff, source_string, 5)) 1237 | continue; 1238 | glyph_surf = TTF_RenderUTF8_Blended(ttf, buff, white); 1239 | if(glyph_surf == NULL) 1240 | continue; 1241 | 1242 | // Try packing. If it fails, create a new surface for the next cache level. 1243 | packed = (FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL); 1244 | if(!packed) 1245 | { 1246 | int i = num_surfaces-1; 1247 | if(num_surfaces >= FC_LOAD_MAX_SURFACES) 1248 | { 1249 | // Can't do any more! 1250 | FC_Log("SDL_FontCache error: Could not create enough cache surfaces to fit all of the loading string!\n"); 1251 | SDL_FreeSurface(glyph_surf); 1252 | break; 1253 | } 1254 | 1255 | // Upload the current surface to the glyph cache now so we can keep the cache level packing cursor up to date as we go. 1256 | FC_UploadGlyphCache(font, i, surfaces[i]); 1257 | SDL_FreeSurface(surfaces[i]); 1258 | #ifndef FC_USE_SDL_GPU 1259 | SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); 1260 | #endif 1261 | // Update the glyph cursor to the new cache level. We need to do this here because the actual cache lags behind our use of the packing above. 1262 | font->last_glyph.cache_level = num_surfaces; 1263 | 1264 | 1265 | surfaces[num_surfaces] = FC_CreateSurface32(w, h); 1266 | num_surfaces++; 1267 | } 1268 | 1269 | // Try packing for the new surface, then blit onto it. 1270 | if(packed || FC_PackGlyphData(font, FC_GetCodepointFromUTF8(&buff_ptr, 0), glyph_surf->w, surfaces[num_surfaces-1]->w, surfaces[num_surfaces-1]->h) != NULL) 1271 | { 1272 | SDL_SetSurfaceBlendMode(glyph_surf, SDL_BLENDMODE_NONE); 1273 | SDL_Rect srcRect = {0, 0, glyph_surf->w, glyph_surf->h}; 1274 | SDL_Rect destrect = font->last_glyph.rect; 1275 | SDL_BlitSurface(glyph_surf, &srcRect, surfaces[num_surfaces-1], &destrect); 1276 | } 1277 | 1278 | SDL_FreeSurface(glyph_surf); 1279 | } 1280 | 1281 | { 1282 | int i = num_surfaces-1; 1283 | FC_UploadGlyphCache(font, i, surfaces[i]); 1284 | SDL_FreeSurface(surfaces[i]); 1285 | #ifndef FC_USE_SDL_GPU 1286 | SDL_SetTextureBlendMode(font->glyph_cache[i], SDL_BLENDMODE_BLEND); 1287 | #endif 1288 | } 1289 | } 1290 | 1291 | return 1; 1292 | } 1293 | 1294 | 1295 | #ifdef FC_USE_SDL_GPU 1296 | Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) 1297 | #else 1298 | Uint8 FC_LoadFont(FC_Font* font, FC_Target* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style) 1299 | #endif 1300 | { 1301 | SDL_RWops* rwops; 1302 | 1303 | if(font == NULL) 1304 | return 0; 1305 | 1306 | rwops = SDL_RWFromFile(filename_ttf, "rb"); 1307 | 1308 | if(rwops == NULL) 1309 | { 1310 | FC_Log("Unable to open file for reading: %s \n", SDL_GetError()); 1311 | return 0; 1312 | } 1313 | 1314 | #ifdef FC_USE_SDL_GPU 1315 | return FC_LoadFont_RW(font, rwops, 1, pointSize, color, style); 1316 | #else 1317 | return FC_LoadFont_RW(font, renderer, rwops, 1, pointSize, color, style); 1318 | #endif 1319 | } 1320 | 1321 | #ifdef FC_USE_SDL_GPU 1322 | Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) 1323 | #else 1324 | Uint8 FC_LoadFont_RW(FC_Font* font, FC_Target* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style) 1325 | #endif 1326 | { 1327 | Uint8 result; 1328 | TTF_Font* ttf; 1329 | Uint8 outline; 1330 | 1331 | if(font == NULL) 1332 | return 0; 1333 | 1334 | if(!TTF_WasInit() && TTF_Init() < 0) 1335 | { 1336 | FC_Log("Unable to initialize SDL_ttf: %s \n", TTF_GetError()); 1337 | if(own_rwops) 1338 | SDL_RWclose(file_rwops_ttf); 1339 | return 0; 1340 | } 1341 | 1342 | ttf = TTF_OpenFontRW(file_rwops_ttf, own_rwops, pointSize); 1343 | 1344 | if(ttf == NULL) 1345 | { 1346 | FC_Log("Unable to load TrueType font: %s \n", TTF_GetError()); 1347 | if(own_rwops) 1348 | SDL_RWclose(file_rwops_ttf); 1349 | return 0; 1350 | } 1351 | 1352 | outline = (style & TTF_STYLE_OUTLINE); 1353 | if(outline) 1354 | { 1355 | style &= ~TTF_STYLE_OUTLINE; 1356 | TTF_SetFontOutline(ttf, 1); 1357 | } 1358 | TTF_SetFontStyle(ttf, style); 1359 | 1360 | #ifdef FC_USE_SDL_GPU 1361 | result = FC_LoadFontFromTTF(font, ttf, color); 1362 | #else 1363 | result = FC_LoadFontFromTTF(font, renderer, ttf, color); 1364 | #endif 1365 | 1366 | // Can only load new (uncached) glyphs if we can keep the SDL_RWops open. 1367 | font->owns_ttf_source = own_rwops; 1368 | if(!own_rwops) 1369 | { 1370 | TTF_CloseFont(font->ttf_source); 1371 | font->ttf_source = NULL; 1372 | } 1373 | 1374 | return result; 1375 | } 1376 | 1377 | 1378 | #ifndef FC_USE_SDL_GPU 1379 | void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType) 1380 | { 1381 | TTF_Font* ttf; 1382 | SDL_Color col; 1383 | Uint8 owns_ttf; 1384 | if (font == NULL) 1385 | return; 1386 | 1387 | // Destroy glyph cache 1388 | if (evType == SDL_RENDER_TARGETS_RESET) { 1389 | int i; 1390 | for (i = 0; i < font->glyph_cache_count; ++i) 1391 | SDL_DestroyTexture(font->glyph_cache[i]); 1392 | } 1393 | free(font->glyph_cache); 1394 | 1395 | ttf = font->ttf_source; 1396 | col = font->default_color; 1397 | owns_ttf = font->owns_ttf_source; 1398 | FC_Init(font); 1399 | 1400 | // Can only reload glyphs if we own the SDL_RWops. 1401 | if (owns_ttf) 1402 | FC_LoadFontFromTTF(font, renderer, ttf, col); 1403 | font->owns_ttf_source = owns_ttf; 1404 | } 1405 | #endif 1406 | 1407 | void FC_ClearFont(FC_Font* font) 1408 | { 1409 | int i; 1410 | if(font == NULL) 1411 | return; 1412 | 1413 | // Release resources 1414 | if(font->owns_ttf_source) 1415 | TTF_CloseFont(font->ttf_source); 1416 | 1417 | font->owns_ttf_source = 0; 1418 | font->ttf_source = NULL; 1419 | 1420 | // Delete glyph map 1421 | FC_MapFree(font->glyphs); 1422 | font->glyphs = NULL; 1423 | 1424 | // Delete glyph cache 1425 | for(i = 0; i < font->glyph_cache_count; ++i) 1426 | { 1427 | #ifdef FC_USE_SDL_GPU 1428 | GPU_FreeImage(font->glyph_cache[i]); 1429 | #else 1430 | SDL_DestroyTexture(font->glyph_cache[i]); 1431 | #endif 1432 | } 1433 | free(font->glyph_cache); 1434 | font->glyph_cache = NULL; 1435 | 1436 | // Reset font 1437 | FC_Init(font); 1438 | } 1439 | 1440 | 1441 | void FC_FreeFont(FC_Font* font) 1442 | { 1443 | int i; 1444 | if(font == NULL) 1445 | return; 1446 | 1447 | // Release resources 1448 | if(font->owns_ttf_source) 1449 | TTF_CloseFont(font->ttf_source); 1450 | 1451 | // Delete glyph map 1452 | FC_MapFree(font->glyphs); 1453 | 1454 | // Delete glyph cache 1455 | for(i = 0; i < font->glyph_cache_count; ++i) 1456 | { 1457 | #ifdef FC_USE_SDL_GPU 1458 | GPU_FreeImage(font->glyph_cache[i]); 1459 | #else 1460 | SDL_DestroyTexture(font->glyph_cache[i]); 1461 | #endif 1462 | } 1463 | free(font->glyph_cache); 1464 | 1465 | free(font->loading_string); 1466 | 1467 | free(font); 1468 | 1469 | // If the last font has been freed; assume shutdown and free the global variables 1470 | if (--NUM_EXISTING_FONTS <= 0) 1471 | { 1472 | free(ASCII_STRING); 1473 | ASCII_STRING = NULL; 1474 | 1475 | free(LATIN_1_STRING); 1476 | LATIN_1_STRING = NULL; 1477 | 1478 | free(ASCII_LATIN_1_STRING); 1479 | ASCII_LATIN_1_STRING = NULL; 1480 | 1481 | free(fc_buffer); 1482 | fc_buffer = NULL; 1483 | } 1484 | } 1485 | 1486 | int FC_GetNumCacheLevels(FC_Font* font) 1487 | { 1488 | return font->glyph_cache_count; 1489 | } 1490 | 1491 | Uint8 FC_AddGlyphToCache(FC_Font* font, SDL_Surface* glyph_surface) 1492 | { 1493 | if(font == NULL || glyph_surface == NULL) 1494 | return 0; 1495 | 1496 | SDL_SetSurfaceBlendMode(glyph_surface, SDL_BLENDMODE_NONE); 1497 | FC_Image* dest = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); 1498 | if(dest == NULL) 1499 | return 0; 1500 | 1501 | #ifdef FC_USE_SDL_GPU 1502 | { 1503 | GPU_Target* target = GPU_LoadTarget(dest); 1504 | if(target == NULL) 1505 | return 0; 1506 | GPU_Image* img = GPU_CopyImageFromSurface(glyph_surface); 1507 | GPU_SetAnchor(img, 0.5f, 0.5f); // Just in case the default is different 1508 | GPU_SetImageFilter(img, GPU_FILTER_NEAREST); 1509 | GPU_SetBlendMode(img, GPU_BLEND_SET); 1510 | 1511 | SDL_Rect destrect = font->last_glyph.rect; 1512 | GPU_Blit(img, NULL, target, destrect.x + destrect.w/2, destrect.y + destrect.h/2); 1513 | 1514 | GPU_FreeImage(img); 1515 | GPU_FreeTarget(target); 1516 | } 1517 | #else 1518 | { 1519 | SDL_Renderer* renderer = font->renderer; 1520 | SDL_Texture* img; 1521 | SDL_Rect destrect; 1522 | SDL_Texture* prev_target = SDL_GetRenderTarget(renderer); 1523 | SDL_Rect prev_clip, prev_viewport; 1524 | int prev_logicalw, prev_logicalh; 1525 | Uint8 prev_clip_enabled; 1526 | float prev_scalex, prev_scaley; 1527 | // only backup if previous target existed (SDL will preserve them for the default target) 1528 | if (prev_target) { 1529 | prev_clip_enabled = has_clip(renderer); 1530 | if (prev_clip_enabled) 1531 | prev_clip = get_clip(renderer); 1532 | SDL_RenderGetViewport(renderer, &prev_viewport); 1533 | SDL_RenderGetScale(renderer, &prev_scalex, &prev_scaley); 1534 | SDL_RenderGetLogicalSize(renderer, &prev_logicalw, &prev_logicalh); 1535 | } 1536 | 1537 | img = SDL_CreateTextureFromSurface(renderer, glyph_surface); 1538 | 1539 | destrect = font->last_glyph.rect; 1540 | SDL_SetRenderTarget(renderer, dest); 1541 | SDL_RenderCopy(renderer, img, NULL, &destrect); 1542 | SDL_SetRenderTarget(renderer, prev_target); 1543 | if (prev_target) { 1544 | if (prev_clip_enabled) 1545 | set_clip(renderer, &prev_clip); 1546 | if (prev_logicalw && prev_logicalh) 1547 | SDL_RenderSetLogicalSize(renderer, prev_logicalw, prev_logicalh); 1548 | else { 1549 | SDL_RenderSetViewport(renderer, &prev_viewport); 1550 | SDL_RenderSetScale(renderer, prev_scalex, prev_scaley); 1551 | } 1552 | } 1553 | 1554 | SDL_DestroyTexture(img); 1555 | } 1556 | #endif 1557 | 1558 | return 1; 1559 | } 1560 | 1561 | 1562 | unsigned int FC_GetNumCodepoints(FC_Font* font) 1563 | { 1564 | FC_Map* glyphs; 1565 | int i; 1566 | unsigned int result = 0; 1567 | if(font == NULL || font->glyphs == NULL) 1568 | return 0; 1569 | 1570 | glyphs = font->glyphs; 1571 | 1572 | for(i = 0; i < glyphs->num_buckets; ++i) 1573 | { 1574 | FC_MapNode* node; 1575 | for(node = glyphs->buckets[i]; node != NULL; node = node->next) 1576 | { 1577 | result++; 1578 | } 1579 | } 1580 | 1581 | return result; 1582 | } 1583 | 1584 | void FC_GetCodepoints(FC_Font* font, Uint32* result) 1585 | { 1586 | FC_Map* glyphs; 1587 | int i; 1588 | unsigned int count = 0; 1589 | if(font == NULL || font->glyphs == NULL) 1590 | return; 1591 | 1592 | glyphs = font->glyphs; 1593 | 1594 | for(i = 0; i < glyphs->num_buckets; ++i) 1595 | { 1596 | FC_MapNode* node; 1597 | for(node = glyphs->buckets[i]; node != NULL; node = node->next) 1598 | { 1599 | result[count] = node->key; 1600 | count++; 1601 | } 1602 | } 1603 | } 1604 | 1605 | Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint) 1606 | { 1607 | FC_GlyphData* e = FC_MapFind(font->glyphs, codepoint); 1608 | if(e == NULL) 1609 | { 1610 | char buff[5]; 1611 | int w, h; 1612 | SDL_Color white = {255, 255, 255, 255}; 1613 | SDL_Surface* surf; 1614 | FC_Image* cache_image; 1615 | 1616 | if(font->ttf_source == NULL) 1617 | return 0; 1618 | 1619 | FC_GetUTF8FromCodepoint(buff, codepoint); 1620 | 1621 | cache_image = FC_GetGlyphCacheLevel(font, font->last_glyph.cache_level); 1622 | if(cache_image == NULL) 1623 | { 1624 | FC_Log("SDL_FontCache: Failed to load cache image, so cannot add new glyphs!\n"); 1625 | return 0; 1626 | } 1627 | 1628 | #ifdef FC_USE_SDL_GPU 1629 | w = cache_image->w; 1630 | h = cache_image->h; 1631 | #else 1632 | SDL_QueryTexture(cache_image, NULL, NULL, &w, &h); 1633 | #endif 1634 | 1635 | surf = TTF_RenderUTF8_Blended(font->ttf_source, buff, white); 1636 | if(surf == NULL) 1637 | { 1638 | return 0; 1639 | } 1640 | 1641 | e = FC_PackGlyphData(font, codepoint, surf->w, w, h); 1642 | if(e == NULL) 1643 | { 1644 | // Grow the cache 1645 | FC_GrowGlyphCache(font); 1646 | 1647 | // Try packing again 1648 | e = FC_PackGlyphData(font, codepoint, surf->w, w, h); 1649 | if(e == NULL) 1650 | { 1651 | SDL_FreeSurface(surf); 1652 | return 0; 1653 | } 1654 | } 1655 | 1656 | // Render onto the cache texture 1657 | FC_AddGlyphToCache(font, surf); 1658 | 1659 | SDL_FreeSurface(surf); 1660 | } 1661 | 1662 | if(result != NULL && e != NULL) 1663 | *result = *e; 1664 | 1665 | return 1; 1666 | } 1667 | 1668 | 1669 | FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data) 1670 | { 1671 | return FC_MapInsert(font->glyphs, codepoint, glyph_data); 1672 | } 1673 | 1674 | 1675 | 1676 | // Drawing 1677 | static FC_Rect FC_RenderLeft(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) 1678 | { 1679 | const char* c = text; 1680 | FC_Rect srcRect; 1681 | FC_Rect dstRect; 1682 | FC_Rect dirtyRect = FC_MakeRect(x, y, 0, 0); 1683 | 1684 | FC_GlyphData glyph; 1685 | Uint32 codepoint; 1686 | 1687 | float destX = x; 1688 | float destY = y; 1689 | float destH; 1690 | float destLineSpacing; 1691 | float destLetterSpacing; 1692 | 1693 | if(font == NULL) 1694 | return dirtyRect; 1695 | 1696 | destH = font->height * scale.y; 1697 | destLineSpacing = font->lineSpacing*scale.y; 1698 | destLetterSpacing = font->letterSpacing*scale.x; 1699 | 1700 | if(c == NULL || font->glyph_cache_count == 0 || dest == NULL) 1701 | return dirtyRect; 1702 | 1703 | int newlineX = x; 1704 | 1705 | for(; *c != '\0'; c++) 1706 | { 1707 | if(*c == '\n') 1708 | { 1709 | destX = newlineX; 1710 | destY += destH + destLineSpacing; 1711 | continue; 1712 | } 1713 | 1714 | codepoint = FC_GetCodepointFromUTF8(&c, 1); // Increments 'c' to skip the extra UTF-8 bytes 1715 | if(!FC_GetGlyphData(font, &glyph, codepoint)) 1716 | { 1717 | codepoint = ' '; 1718 | if(!FC_GetGlyphData(font, &glyph, codepoint)) 1719 | continue; // Skip bad characters 1720 | } 1721 | 1722 | if (codepoint == ' ') 1723 | { 1724 | destX += glyph.rect.w*scale.x + destLetterSpacing; 1725 | continue; 1726 | } 1727 | /*if(destX >= dest->w) 1728 | continue; 1729 | if(destY >= dest->h) 1730 | continue;*/ 1731 | 1732 | #ifdef FC_USE_SDL_GPU 1733 | srcRect.x = glyph.rect.x; 1734 | srcRect.y = glyph.rect.y; 1735 | srcRect.w = glyph.rect.w; 1736 | srcRect.h = glyph.rect.h; 1737 | #else 1738 | srcRect = glyph.rect; 1739 | #endif 1740 | dstRect = fc_render_callback(FC_GetGlyphCacheLevel(font, glyph.cache_level), &srcRect, dest, destX, destY, scale.x, scale.y); 1741 | if(dirtyRect.w == 0 || dirtyRect.h == 0) 1742 | dirtyRect = dstRect; 1743 | else 1744 | dirtyRect = FC_RectUnion(dirtyRect, dstRect); 1745 | 1746 | destX += glyph.rect.w*scale.x + destLetterSpacing; 1747 | } 1748 | 1749 | return dirtyRect; 1750 | } 1751 | 1752 | static void set_color_for_all_caches(FC_Font* font, SDL_Color color) 1753 | { 1754 | // TODO: How can I predict which glyph caches are to be used? 1755 | FC_Image* img; 1756 | int i; 1757 | int num_levels = FC_GetNumCacheLevels(font); 1758 | for(i = 0; i < num_levels; ++i) 1759 | { 1760 | img = FC_GetGlyphCacheLevel(font, i); 1761 | set_color(img, color.r, color.g, color.b, FC_GET_ALPHA(color)); 1762 | } 1763 | } 1764 | 1765 | FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...) 1766 | { 1767 | if(formatted_text == NULL || font == NULL) 1768 | return FC_MakeRect(x, y, 0, 0); 1769 | 1770 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 1771 | 1772 | set_color_for_all_caches(font, font->default_color); 1773 | 1774 | return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); 1775 | } 1776 | 1777 | 1778 | 1779 | typedef struct FC_StringList 1780 | { 1781 | char* value; 1782 | struct FC_StringList* next; 1783 | } FC_StringList; 1784 | 1785 | void FC_StringListFree(FC_StringList* node) 1786 | { 1787 | // Delete the nodes in order 1788 | while(node != NULL) 1789 | { 1790 | FC_StringList* last = node; 1791 | node = node->next; 1792 | 1793 | free(last->value); 1794 | free(last); 1795 | } 1796 | } 1797 | 1798 | FC_StringList** FC_StringListPushBack(FC_StringList** node, char* value, Uint8 copy) 1799 | { 1800 | if(node == NULL) 1801 | { 1802 | return NULL; 1803 | } 1804 | 1805 | // Get to the last node 1806 | while(*node != NULL) 1807 | { 1808 | node = &(*node)->next; 1809 | } 1810 | 1811 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); 1812 | 1813 | (*node)->value = (copy? U8_strdup(value) : value); 1814 | (*node)->next = NULL; 1815 | 1816 | return node; 1817 | } 1818 | 1819 | FC_StringList** FC_StringListPushBackBytes(FC_StringList** node, const char* data, int num_bytes) 1820 | { 1821 | if(node == NULL) 1822 | { 1823 | return node; 1824 | } 1825 | 1826 | // Get to the last node 1827 | while(*node != NULL) 1828 | { 1829 | node = &(*node)->next; 1830 | } 1831 | 1832 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); 1833 | 1834 | (*node)->value = (char*)malloc(num_bytes + 1); 1835 | memcpy((*node)->value, data, num_bytes); 1836 | (*node)->value[num_bytes] = '\0'; 1837 | (*node)->next = NULL; 1838 | 1839 | return node; 1840 | } 1841 | 1842 | static FC_StringList* FC_Explode(const char* text, char delimiter) 1843 | { 1844 | FC_StringList* head; 1845 | FC_StringList* new_node; 1846 | FC_StringList** node; 1847 | const char* start; 1848 | const char* end; 1849 | unsigned int size; 1850 | if(text == NULL) 1851 | return NULL; 1852 | 1853 | head = NULL; 1854 | node = &head; 1855 | 1856 | // Doesn't technically support UTF-8, but it's probably fine, right? 1857 | size = 0; 1858 | start = end = text; 1859 | while(1) 1860 | { 1861 | if(*end == delimiter || *end == '\0') 1862 | { 1863 | *node = (FC_StringList*)malloc(sizeof(FC_StringList)); 1864 | new_node = *node; 1865 | 1866 | new_node->value = (char*)malloc(size + 1); 1867 | memcpy(new_node->value, start, size); 1868 | new_node->value[size] = '\0'; 1869 | 1870 | new_node->next = NULL; 1871 | 1872 | if(*end == '\0') 1873 | break; 1874 | 1875 | node = &((*node)->next); 1876 | start = end+1; 1877 | size = 0; 1878 | } 1879 | else 1880 | ++size; 1881 | 1882 | ++end; 1883 | } 1884 | 1885 | return head; 1886 | } 1887 | 1888 | static FC_StringList* FC_ExplodeBreakingSpace(const char* text, FC_StringList** spaces) 1889 | { 1890 | FC_StringList* head; 1891 | FC_StringList** node; 1892 | const char* start; 1893 | const char* end; 1894 | unsigned int size; 1895 | if(text == NULL) 1896 | return NULL; 1897 | 1898 | head = NULL; 1899 | node = &head; 1900 | 1901 | // Warning: spaces must not be initialized before this function 1902 | *spaces = NULL; 1903 | 1904 | // Doesn't technically support UTF-8, but it's probably fine, right? 1905 | size = 0; 1906 | start = end = text; 1907 | while(1) 1908 | { 1909 | // Add any characters here that should make separate words (except for \n?) 1910 | if(*end == ' ' || *end == '\t' || *end == '\0') 1911 | { 1912 | FC_StringListPushBackBytes(node, start, size); 1913 | FC_StringListPushBackBytes(spaces, end, 1); 1914 | 1915 | if(*end == '\0') 1916 | break; 1917 | 1918 | node = &((*node)->next); 1919 | start = end+1; 1920 | size = 0; 1921 | } 1922 | else 1923 | ++size; 1924 | 1925 | ++end; 1926 | } 1927 | 1928 | return head; 1929 | } 1930 | 1931 | static FC_StringList* FC_ExplodeAndKeep(const char* text, char delimiter) 1932 | { 1933 | FC_StringList* head; 1934 | FC_StringList** node; 1935 | const char* start; 1936 | const char* end; 1937 | unsigned int size; 1938 | if(text == NULL) 1939 | return NULL; 1940 | 1941 | head = NULL; 1942 | node = &head; 1943 | 1944 | // Doesn't technically support UTF-8, but it's probably fine, right? 1945 | size = 0; 1946 | start = end = text; 1947 | while(1) 1948 | { 1949 | if(*end == delimiter || *end == '\0') 1950 | { 1951 | FC_StringListPushBackBytes(node, start, size); 1952 | 1953 | if(*end == '\0') 1954 | break; 1955 | 1956 | node = &((*node)->next); 1957 | start = end; 1958 | size = 1; 1959 | } 1960 | else 1961 | ++size; 1962 | 1963 | ++end; 1964 | } 1965 | 1966 | return head; 1967 | } 1968 | 1969 | static void FC_RenderAlign(FC_Font* font, FC_Target* dest, float x, float y, int width, FC_Scale scale, FC_AlignEnum align, const char* text) 1970 | { 1971 | switch(align) 1972 | { 1973 | case FC_ALIGN_LEFT: 1974 | FC_RenderLeft(font, dest, x, y, scale, text); 1975 | break; 1976 | case FC_ALIGN_CENTER: 1977 | FC_RenderCenter(font, dest, x + width/2, y, scale, text); 1978 | break; 1979 | case FC_ALIGN_RIGHT: 1980 | FC_RenderRight(font, dest, x + width, y, scale, text); 1981 | break; 1982 | } 1983 | } 1984 | 1985 | static FC_StringList* FC_GetBufferFitToColumn(FC_Font* font, int width, FC_Scale scale, Uint8 keep_newlines) 1986 | { 1987 | FC_StringList* result = NULL; 1988 | FC_StringList** current = &result; 1989 | 1990 | FC_StringList *ls, *iter; 1991 | 1992 | ls = (keep_newlines? FC_ExplodeAndKeep(fc_buffer, '\n') : FC_Explode(fc_buffer, '\n')); 1993 | for(iter = ls; iter != NULL; iter = iter->next) 1994 | { 1995 | char* line = iter->value; 1996 | 1997 | // If line is too long, then add words one at a time until we go over. 1998 | if(width > 0 && FC_GetWidth(font, "%s", line) > width) 1999 | { 2000 | FC_StringList *words, *word_iter, *spaces, *spaces_iter; 2001 | 2002 | words = FC_ExplodeBreakingSpace(line, &spaces); 2003 | // Skip the first word for the iterator, so there will always be at least one word per line 2004 | line = new_concat(words->value, spaces->value); 2005 | for(word_iter = words->next, spaces_iter = spaces->next; word_iter != NULL && spaces_iter != NULL; word_iter = word_iter->next, spaces_iter = spaces_iter->next) 2006 | { 2007 | char* line_plus_word = new_concat(line, word_iter->value); 2008 | char* word_plus_space = new_concat(word_iter->value, spaces_iter->value); 2009 | if(FC_GetWidth(font, "%s", line_plus_word) > width) 2010 | { 2011 | current = FC_StringListPushBack(current, line, 0); 2012 | 2013 | line = word_plus_space; 2014 | } 2015 | else 2016 | { 2017 | replace_concat(&line, word_plus_space); 2018 | free(word_plus_space); 2019 | } 2020 | free(line_plus_word); 2021 | } 2022 | current = FC_StringListPushBack(current, line, 0); 2023 | FC_StringListFree(words); 2024 | FC_StringListFree(spaces); 2025 | } 2026 | else 2027 | { 2028 | current = FC_StringListPushBack(current, line, 0); 2029 | iter->value = NULL; 2030 | } 2031 | } 2032 | FC_StringListFree(ls); 2033 | 2034 | return result; 2035 | } 2036 | 2037 | static void FC_DrawColumnFromBuffer(FC_Font* font, FC_Target* dest, FC_Rect box, int* total_height, FC_Scale scale, FC_AlignEnum align) 2038 | { 2039 | int y = box.y; 2040 | FC_StringList *ls, *iter; 2041 | 2042 | ls = FC_GetBufferFitToColumn(font, box.w, scale, 0); 2043 | for(iter = ls; iter != NULL; iter = iter->next) 2044 | { 2045 | FC_RenderAlign(font, dest, box.x, y, box.w, scale, align, iter->value); 2046 | y += FC_GetLineHeight(font); 2047 | } 2048 | FC_StringListFree(ls); 2049 | 2050 | if(total_height != NULL) 2051 | *total_height = y - box.y; 2052 | } 2053 | 2054 | FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...) 2055 | { 2056 | Uint8 useClip; 2057 | if(formatted_text == NULL || font == NULL) 2058 | return FC_MakeRect(box.x, box.y, 0, 0); 2059 | 2060 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2061 | 2062 | useClip = has_clip(dest); 2063 | FC_Rect oldclip, newclip; 2064 | if(useClip) 2065 | { 2066 | oldclip = get_clip(dest); 2067 | newclip = FC_RectIntersect(oldclip, box); 2068 | } 2069 | else 2070 | newclip = box; 2071 | 2072 | set_clip(dest, &newclip); 2073 | 2074 | set_color_for_all_caches(font, font->default_color); 2075 | 2076 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); 2077 | 2078 | if(useClip) 2079 | set_clip(dest, &oldclip); 2080 | else 2081 | set_clip(dest, NULL); 2082 | 2083 | return box; 2084 | } 2085 | 2086 | FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...) 2087 | { 2088 | Uint8 useClip; 2089 | if(formatted_text == NULL || font == NULL) 2090 | return FC_MakeRect(box.x, box.y, 0, 0); 2091 | 2092 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2093 | 2094 | useClip = has_clip(dest); 2095 | FC_Rect oldclip, newclip; 2096 | if(useClip) 2097 | { 2098 | oldclip = get_clip(dest); 2099 | newclip = FC_RectIntersect(oldclip, box); 2100 | } 2101 | else 2102 | newclip = box; 2103 | set_clip(dest, &newclip); 2104 | 2105 | set_color_for_all_caches(font, font->default_color); 2106 | 2107 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), align); 2108 | 2109 | if(useClip) 2110 | set_clip(dest, &oldclip); 2111 | else 2112 | set_clip(dest, NULL); 2113 | 2114 | return box; 2115 | } 2116 | 2117 | FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...) 2118 | { 2119 | Uint8 useClip; 2120 | if(formatted_text == NULL || font == NULL) 2121 | return FC_MakeRect(box.x, box.y, 0, 0); 2122 | 2123 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2124 | 2125 | useClip = has_clip(dest); 2126 | FC_Rect oldclip, newclip; 2127 | if(useClip) 2128 | { 2129 | oldclip = get_clip(dest); 2130 | newclip = FC_RectIntersect(oldclip, box); 2131 | } 2132 | else 2133 | newclip = box; 2134 | set_clip(dest, &newclip); 2135 | 2136 | set_color_for_all_caches(font, font->default_color); 2137 | 2138 | FC_DrawColumnFromBuffer(font, dest, box, NULL, scale, FC_ALIGN_LEFT); 2139 | 2140 | if(useClip) 2141 | set_clip(dest, &oldclip); 2142 | else 2143 | set_clip(dest, NULL); 2144 | 2145 | return box; 2146 | } 2147 | 2148 | FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...) 2149 | { 2150 | Uint8 useClip; 2151 | if(formatted_text == NULL || font == NULL) 2152 | return FC_MakeRect(box.x, box.y, 0, 0); 2153 | 2154 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2155 | 2156 | useClip = has_clip(dest); 2157 | FC_Rect oldclip, newclip; 2158 | if(useClip) 2159 | { 2160 | oldclip = get_clip(dest); 2161 | newclip = FC_RectIntersect(oldclip, box); 2162 | } 2163 | else 2164 | newclip = box; 2165 | set_clip(dest, &newclip); 2166 | 2167 | set_color_for_all_caches(font, color); 2168 | 2169 | FC_DrawColumnFromBuffer(font, dest, box, NULL, FC_MakeScale(1,1), FC_ALIGN_LEFT); 2170 | 2171 | if(useClip) 2172 | set_clip(dest, &oldclip); 2173 | else 2174 | set_clip(dest, NULL); 2175 | 2176 | return box; 2177 | } 2178 | 2179 | FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...) 2180 | { 2181 | Uint8 useClip; 2182 | if(formatted_text == NULL || font == NULL) 2183 | return FC_MakeRect(box.x, box.y, 0, 0); 2184 | 2185 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2186 | 2187 | useClip = has_clip(dest); 2188 | FC_Rect oldclip, newclip; 2189 | if(useClip) 2190 | { 2191 | oldclip = get_clip(dest); 2192 | newclip = FC_RectIntersect(oldclip, box); 2193 | } 2194 | else 2195 | newclip = box; 2196 | set_clip(dest, &newclip); 2197 | 2198 | set_color_for_all_caches(font, effect.color); 2199 | 2200 | FC_DrawColumnFromBuffer(font, dest, box, NULL, effect.scale, effect.alignment); 2201 | 2202 | if(useClip) 2203 | set_clip(dest, &oldclip); 2204 | else 2205 | set_clip(dest, NULL); 2206 | 2207 | return box; 2208 | } 2209 | 2210 | FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...) 2211 | { 2212 | FC_Rect box = {x, y, width, 0}; 2213 | int total_height; 2214 | 2215 | if(formatted_text == NULL || font == NULL) 2216 | return FC_MakeRect(x, y, 0, 0); 2217 | 2218 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2219 | 2220 | set_color_for_all_caches(font, font->default_color); 2221 | 2222 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); 2223 | 2224 | return FC_MakeRect(box.x, box.y, width, total_height); 2225 | } 2226 | 2227 | FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...) 2228 | { 2229 | FC_Rect box = {x, y, width, 0}; 2230 | int total_height; 2231 | 2232 | if(formatted_text == NULL || font == NULL) 2233 | return FC_MakeRect(x, y, 0, 0); 2234 | 2235 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2236 | 2237 | set_color_for_all_caches(font, font->default_color); 2238 | 2239 | switch(align) 2240 | { 2241 | case FC_ALIGN_CENTER: 2242 | box.x -= width/2; 2243 | break; 2244 | case FC_ALIGN_RIGHT: 2245 | box.x -= width; 2246 | break; 2247 | default: 2248 | break; 2249 | } 2250 | 2251 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), align); 2252 | 2253 | return FC_MakeRect(box.x, box.y, width, total_height); 2254 | } 2255 | 2256 | FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...) 2257 | { 2258 | FC_Rect box = {x, y, width, 0}; 2259 | int total_height; 2260 | 2261 | if(formatted_text == NULL || font == NULL) 2262 | return FC_MakeRect(x, y, 0, 0); 2263 | 2264 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2265 | 2266 | set_color_for_all_caches(font, font->default_color); 2267 | 2268 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, scale, FC_ALIGN_LEFT); 2269 | 2270 | return FC_MakeRect(box.x, box.y, width, total_height); 2271 | } 2272 | 2273 | FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...) 2274 | { 2275 | FC_Rect box = {x, y, width, 0}; 2276 | int total_height; 2277 | 2278 | if(formatted_text == NULL || font == NULL) 2279 | return FC_MakeRect(x, y, 0, 0); 2280 | 2281 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2282 | 2283 | set_color_for_all_caches(font, color); 2284 | 2285 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, FC_MakeScale(1,1), FC_ALIGN_LEFT); 2286 | 2287 | return FC_MakeRect(box.x, box.y, width, total_height); 2288 | } 2289 | 2290 | FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...) 2291 | { 2292 | FC_Rect box = {x, y, width, 0}; 2293 | int total_height; 2294 | 2295 | if(formatted_text == NULL || font == NULL) 2296 | return FC_MakeRect(x, y, 0, 0); 2297 | 2298 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2299 | 2300 | set_color_for_all_caches(font, effect.color); 2301 | 2302 | switch(effect.alignment) 2303 | { 2304 | case FC_ALIGN_CENTER: 2305 | box.x -= width/2; 2306 | break; 2307 | case FC_ALIGN_RIGHT: 2308 | box.x -= width; 2309 | break; 2310 | default: 2311 | break; 2312 | } 2313 | 2314 | FC_DrawColumnFromBuffer(font, dest, box, &total_height, effect.scale, effect.alignment); 2315 | 2316 | return FC_MakeRect(box.x, box.y, width, total_height); 2317 | } 2318 | 2319 | static FC_Rect FC_RenderCenter(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) 2320 | { 2321 | FC_Rect result = {x, y, 0, 0}; 2322 | if(text == NULL || font == NULL) 2323 | return result; 2324 | 2325 | char* str = U8_strdup(text); 2326 | char* del = str; 2327 | char* c; 2328 | 2329 | // Go through str, when you find a \n, replace it with \0 and print it 2330 | // then move down, back, and continue. 2331 | for(c = str; *c != '\0';) 2332 | { 2333 | if(*c == '\n') 2334 | { 2335 | *c = '\0'; 2336 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); 2337 | *c = '\n'; 2338 | c++; 2339 | str = c; 2340 | y += scale.y*font->height; 2341 | } 2342 | else 2343 | c++; 2344 | } 2345 | 2346 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str)/2.0f, y, scale, str), result); 2347 | 2348 | free(del); 2349 | return result; 2350 | } 2351 | 2352 | static FC_Rect FC_RenderRight(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* text) 2353 | { 2354 | FC_Rect result = {x, y, 0, 0}; 2355 | if(text == NULL || font == NULL) 2356 | return result; 2357 | 2358 | char* str = U8_strdup(text); 2359 | char* del = str; 2360 | char* c; 2361 | 2362 | for(c = str; *c != '\0';) 2363 | { 2364 | if(*c == '\n') 2365 | { 2366 | *c = '\0'; 2367 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); 2368 | *c = '\n'; 2369 | c++; 2370 | str = c; 2371 | y += scale.y*font->height; 2372 | } 2373 | else 2374 | c++; 2375 | } 2376 | 2377 | result = FC_RectUnion(FC_RenderLeft(font, dest, x - scale.x*FC_GetWidth(font, "%s", str), y, scale, str), result); 2378 | 2379 | free(del); 2380 | return result; 2381 | } 2382 | 2383 | 2384 | 2385 | FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...) 2386 | { 2387 | if(formatted_text == NULL || font == NULL) 2388 | return FC_MakeRect(x, y, 0, 0); 2389 | 2390 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2391 | 2392 | set_color_for_all_caches(font, font->default_color); 2393 | 2394 | return FC_RenderLeft(font, dest, x, y, scale, fc_buffer); 2395 | } 2396 | 2397 | FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...) 2398 | { 2399 | if(formatted_text == NULL || font == NULL) 2400 | return FC_MakeRect(x, y, 0, 0); 2401 | 2402 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2403 | 2404 | set_color_for_all_caches(font, font->default_color); 2405 | 2406 | FC_Rect result; 2407 | switch(align) 2408 | { 2409 | case FC_ALIGN_LEFT: 2410 | result = FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); 2411 | break; 2412 | case FC_ALIGN_CENTER: 2413 | result = FC_RenderCenter(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); 2414 | break; 2415 | case FC_ALIGN_RIGHT: 2416 | result = FC_RenderRight(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); 2417 | break; 2418 | default: 2419 | result = FC_MakeRect(x, y, 0, 0); 2420 | break; 2421 | } 2422 | 2423 | return result; 2424 | } 2425 | 2426 | FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...) 2427 | { 2428 | if(formatted_text == NULL || font == NULL) 2429 | return FC_MakeRect(x, y, 0, 0); 2430 | 2431 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2432 | 2433 | set_color_for_all_caches(font, color); 2434 | 2435 | return FC_RenderLeft(font, dest, x, y, FC_MakeScale(1,1), fc_buffer); 2436 | } 2437 | 2438 | 2439 | FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...) 2440 | { 2441 | if(formatted_text == NULL || font == NULL) 2442 | return FC_MakeRect(x, y, 0, 0); 2443 | 2444 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2445 | 2446 | set_color_for_all_caches(font, effect.color); 2447 | 2448 | FC_Rect result; 2449 | switch(effect.alignment) 2450 | { 2451 | case FC_ALIGN_LEFT: 2452 | result = FC_RenderLeft(font, dest, x, y, effect.scale, fc_buffer); 2453 | break; 2454 | case FC_ALIGN_CENTER: 2455 | result = FC_RenderCenter(font, dest, x, y, effect.scale, fc_buffer); 2456 | break; 2457 | case FC_ALIGN_RIGHT: 2458 | result = FC_RenderRight(font, dest, x, y, effect.scale, fc_buffer); 2459 | break; 2460 | default: 2461 | result = FC_MakeRect(x, y, 0, 0); 2462 | break; 2463 | } 2464 | 2465 | return result; 2466 | } 2467 | 2468 | 2469 | 2470 | 2471 | // Getters 2472 | 2473 | 2474 | FC_FilterEnum FC_GetFilterMode(FC_Font* font) 2475 | { 2476 | if(font == NULL) 2477 | return FC_FILTER_NEAREST; 2478 | 2479 | return font->filter; 2480 | } 2481 | 2482 | Uint16 FC_GetLineHeight(FC_Font* font) 2483 | { 2484 | if(font == NULL) 2485 | return 0; 2486 | 2487 | return font->height; 2488 | } 2489 | 2490 | Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...) 2491 | { 2492 | if(formatted_text == NULL || font == NULL) 2493 | return 0; 2494 | 2495 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2496 | 2497 | Uint16 numLines = 1; 2498 | const char* c; 2499 | 2500 | for (c = fc_buffer; *c != '\0'; c++) 2501 | { 2502 | if(*c == '\n') 2503 | numLines++; 2504 | } 2505 | 2506 | // Actual height of letter region + line spacing 2507 | return font->height*numLines + font->lineSpacing*(numLines - 1); //height*numLines; 2508 | } 2509 | 2510 | Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...) 2511 | { 2512 | if(formatted_text == NULL || font == NULL) 2513 | return 0; 2514 | 2515 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2516 | 2517 | const char* c; 2518 | Uint16 width = 0; 2519 | Uint16 bigWidth = 0; // Allows for multi-line strings 2520 | 2521 | for (c = fc_buffer; *c != '\0'; c++) 2522 | { 2523 | if(*c == '\n') 2524 | { 2525 | bigWidth = bigWidth >= width? bigWidth : width; 2526 | width = 0; 2527 | continue; 2528 | } 2529 | 2530 | FC_GlyphData glyph; 2531 | Uint32 codepoint = FC_GetCodepointFromUTF8(&c, 1); 2532 | if(FC_GetGlyphData(font, &glyph, codepoint) || FC_GetGlyphData(font, &glyph, ' ')) 2533 | width += glyph.rect.w; 2534 | } 2535 | bigWidth = bigWidth >= width? bigWidth : width; 2536 | 2537 | return bigWidth; 2538 | } 2539 | 2540 | // If width == -1, use no width limit 2541 | FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...) 2542 | { 2543 | FC_Rect result = {0, 0, 1, FC_GetLineHeight(font)}; 2544 | FC_StringList *ls, *iter; 2545 | int num_lines = 0; 2546 | Uint8 done = 0; 2547 | 2548 | if(formatted_text == NULL || column_width == 0 || position_index == 0 || font == NULL) 2549 | return result; 2550 | 2551 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2552 | 2553 | ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); 2554 | for(iter = ls; iter != NULL;) 2555 | { 2556 | char* line; 2557 | int i = 0; 2558 | FC_StringList* next_iter = iter->next; 2559 | 2560 | ++num_lines; 2561 | for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) 2562 | { 2563 | ++i; 2564 | --position_index; 2565 | if(position_index == 0) 2566 | { 2567 | // FIXME: Doesn't handle box-wrapped newlines correctly 2568 | line = (char*)U8_next(line); 2569 | line[0] = '\0'; 2570 | result.x = FC_GetWidth(font, "%s", iter->value); 2571 | done = 1; 2572 | break; 2573 | } 2574 | } 2575 | if(done) 2576 | break; 2577 | 2578 | // Prevent line wrapping if there are no more lines 2579 | if(next_iter == NULL && !done) 2580 | result.x = FC_GetWidth(font, "%s", iter->value); 2581 | iter = next_iter; 2582 | } 2583 | FC_StringListFree(ls); 2584 | 2585 | if(num_lines > 1) 2586 | { 2587 | result.y = (num_lines - 1) * FC_GetLineHeight(font); 2588 | } 2589 | 2590 | return result; 2591 | } 2592 | 2593 | 2594 | Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...) 2595 | { 2596 | int y = 0; 2597 | 2598 | FC_StringList *ls, *iter; 2599 | 2600 | if(font == NULL) 2601 | return 0; 2602 | 2603 | if(formatted_text == NULL || width == 0) 2604 | return font->height; 2605 | 2606 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2607 | 2608 | ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); 2609 | for(iter = ls; iter != NULL; iter = iter->next) 2610 | { 2611 | y += FC_GetLineHeight(font); 2612 | } 2613 | FC_StringListFree(ls); 2614 | 2615 | return y; 2616 | } 2617 | 2618 | static int FC_GetAscentFromCodepoint(FC_Font* font, Uint32 codepoint) 2619 | { 2620 | FC_GlyphData glyph; 2621 | 2622 | if(font == NULL) 2623 | return 0; 2624 | 2625 | // FIXME: Store ascent so we can return it here 2626 | FC_GetGlyphData(font, &glyph, codepoint); 2627 | return glyph.rect.h; 2628 | } 2629 | 2630 | static int FC_GetDescentFromCodepoint(FC_Font* font, Uint32 codepoint) 2631 | { 2632 | FC_GlyphData glyph; 2633 | 2634 | if(font == NULL) 2635 | return 0; 2636 | 2637 | // FIXME: Store descent so we can return it here 2638 | FC_GetGlyphData(font, &glyph, codepoint); 2639 | return glyph.rect.h; 2640 | } 2641 | 2642 | int FC_GetAscent(FC_Font* font, const char* formatted_text, ...) 2643 | { 2644 | Uint32 codepoint; 2645 | int max, ascent; 2646 | const char* c; 2647 | 2648 | if(font == NULL) 2649 | return 0; 2650 | 2651 | if(formatted_text == NULL) 2652 | return font->ascent; 2653 | 2654 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2655 | 2656 | max = 0; 2657 | c = fc_buffer; 2658 | 2659 | while(*c != '\0') 2660 | { 2661 | codepoint = FC_GetCodepointFromUTF8(&c, 1); 2662 | if(codepoint != 0) 2663 | { 2664 | ascent = FC_GetAscentFromCodepoint(font, codepoint); 2665 | if(ascent > max) 2666 | max = ascent; 2667 | } 2668 | ++c; 2669 | } 2670 | return max; 2671 | } 2672 | 2673 | int FC_GetDescent(FC_Font* font, const char* formatted_text, ...) 2674 | { 2675 | Uint32 codepoint; 2676 | int max, descent; 2677 | const char* c; 2678 | 2679 | if(font == NULL) 2680 | return 0; 2681 | 2682 | if(formatted_text == NULL) 2683 | return font->descent; 2684 | 2685 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2686 | 2687 | max = 0; 2688 | c = fc_buffer; 2689 | 2690 | while(*c != '\0') 2691 | { 2692 | codepoint = FC_GetCodepointFromUTF8(&c, 1); 2693 | if(codepoint != 0) 2694 | { 2695 | descent = FC_GetDescentFromCodepoint(font, codepoint); 2696 | if(descent > max) 2697 | max = descent; 2698 | } 2699 | ++c; 2700 | } 2701 | return max; 2702 | } 2703 | 2704 | int FC_GetBaseline(FC_Font* font) 2705 | { 2706 | if(font == NULL) 2707 | return 0; 2708 | 2709 | return font->baseline; 2710 | } 2711 | 2712 | int FC_GetSpacing(FC_Font* font) 2713 | { 2714 | if(font == NULL) 2715 | return 0; 2716 | 2717 | return font->letterSpacing; 2718 | } 2719 | 2720 | int FC_GetLineSpacing(FC_Font* font) 2721 | { 2722 | if(font == NULL) 2723 | return 0; 2724 | 2725 | return font->lineSpacing; 2726 | } 2727 | 2728 | Uint16 FC_GetMaxWidth(FC_Font* font) 2729 | { 2730 | if(font == NULL) 2731 | return 0; 2732 | 2733 | return font->maxWidth; 2734 | } 2735 | 2736 | SDL_Color FC_GetDefaultColor(FC_Font* font) 2737 | { 2738 | if(font == NULL) 2739 | { 2740 | SDL_Color c = {0,0,0,255}; 2741 | return c; 2742 | } 2743 | 2744 | return font->default_color; 2745 | } 2746 | 2747 | FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...) 2748 | { 2749 | FC_Rect result = {x, y, 0, 0}; 2750 | 2751 | if(formatted_text == NULL) 2752 | return result; 2753 | 2754 | // Create a temp buffer while GetWidth and GetHeight use fc_buffer. 2755 | char* temp = (char*)malloc(fc_buffer_size); 2756 | FC_EXTRACT_VARARGS(temp, formatted_text); 2757 | 2758 | result.w = FC_GetWidth(font, "%s", temp) * scale.x; 2759 | result.h = FC_GetHeight(font, "%s", temp) * scale.y; 2760 | 2761 | switch(align) 2762 | { 2763 | case FC_ALIGN_LEFT: 2764 | break; 2765 | case FC_ALIGN_CENTER: 2766 | result.x -= result.w/2; 2767 | break; 2768 | case FC_ALIGN_RIGHT: 2769 | result.x -= result.w; 2770 | break; 2771 | default: 2772 | break; 2773 | } 2774 | 2775 | free(temp); 2776 | 2777 | return result; 2778 | } 2779 | 2780 | Uint8 FC_InRect(float x, float y, FC_Rect input_rect) 2781 | { 2782 | return (input_rect.x <= x && x <= input_rect.x + input_rect.w && input_rect.y <= y && y <= input_rect.y + input_rect.h); 2783 | } 2784 | 2785 | // TODO: Make it work with alignment 2786 | Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...) 2787 | { 2788 | FC_StringList *ls, *iter; 2789 | Uint8 done = 0; 2790 | int height = FC_GetLineHeight(font); 2791 | Uint16 position = 0; 2792 | int current_x = 0; 2793 | int current_y = 0; 2794 | FC_GlyphData glyph_data; 2795 | 2796 | if(formatted_text == NULL || column_width == 0 || font == NULL) 2797 | return 0; 2798 | 2799 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2800 | 2801 | ls = FC_GetBufferFitToColumn(font, column_width, FC_MakeScale(1,1), 1); 2802 | for(iter = ls; iter != NULL; iter = iter->next) 2803 | { 2804 | char* line; 2805 | 2806 | for(line = iter->value; line != NULL && *line != '\0'; line = (char*)U8_next(line)) 2807 | { 2808 | if(FC_GetGlyphData(font, &glyph_data, FC_GetCodepointFromUTF8((const char**)&line, 0))) 2809 | { 2810 | if(FC_InRect(x, y, FC_MakeRect(current_x, current_y, glyph_data.rect.w, glyph_data.rect.h))) 2811 | { 2812 | done = 1; 2813 | break; 2814 | } 2815 | 2816 | current_x += glyph_data.rect.w; 2817 | } 2818 | position++; 2819 | } 2820 | if(done) 2821 | break; 2822 | 2823 | current_x = 0; 2824 | current_y += height; 2825 | if(y < current_y) 2826 | break; 2827 | } 2828 | FC_StringListFree(ls); 2829 | 2830 | return position; 2831 | } 2832 | 2833 | int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...) 2834 | { 2835 | FC_StringList *ls, *iter; 2836 | 2837 | if(font == NULL) 2838 | return 0; 2839 | 2840 | if(formatted_text == NULL || width == 0) 2841 | return 0; 2842 | 2843 | FC_EXTRACT_VARARGS(fc_buffer, formatted_text); 2844 | 2845 | ls = FC_GetBufferFitToColumn(font, width, FC_MakeScale(1,1), 0); 2846 | int size_so_far = 0; 2847 | int size_remaining = max_result_size-1; // reserve for \0 2848 | for(iter = ls; iter != NULL && size_remaining > 0; iter = iter->next) 2849 | { 2850 | // Copy as much of this line as we can 2851 | int len = strlen(iter->value); 2852 | int num_bytes = FC_MIN(len, size_remaining); 2853 | memcpy(&result[size_so_far], iter->value, num_bytes); 2854 | size_so_far += num_bytes; 2855 | 2856 | // If there's another line, add newline character 2857 | if(size_remaining > 0 && iter->next != NULL) 2858 | { 2859 | --size_remaining; 2860 | result[size_so_far] = '\n'; 2861 | ++size_so_far; 2862 | } 2863 | } 2864 | FC_StringListFree(ls); 2865 | 2866 | result[size_so_far] = '\0'; 2867 | 2868 | return size_so_far; 2869 | } 2870 | 2871 | 2872 | 2873 | // Setters 2874 | 2875 | 2876 | void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter) 2877 | { 2878 | if(font == NULL) 2879 | return; 2880 | 2881 | if(font->filter != filter) 2882 | { 2883 | font->filter = filter; 2884 | 2885 | #ifdef FC_USE_SDL_GPU 2886 | // Update each texture to use this filter mode 2887 | { 2888 | int i; 2889 | GPU_FilterEnum gpu_filter = GPU_FILTER_NEAREST; 2890 | if(FC_GetFilterMode(font) == FC_FILTER_LINEAR) 2891 | gpu_filter = GPU_FILTER_LINEAR; 2892 | 2893 | for(i = 0; i < font->glyph_cache_count; ++i) 2894 | { 2895 | GPU_SetImageFilter(font->glyph_cache[i], gpu_filter); 2896 | } 2897 | } 2898 | #endif 2899 | } 2900 | } 2901 | 2902 | 2903 | void FC_SetSpacing(FC_Font* font, int LetterSpacing) 2904 | { 2905 | if(font == NULL) 2906 | return; 2907 | 2908 | font->letterSpacing = LetterSpacing; 2909 | } 2910 | 2911 | void FC_SetLineSpacing(FC_Font* font, int LineSpacing) 2912 | { 2913 | if(font == NULL) 2914 | return; 2915 | 2916 | font->lineSpacing = LineSpacing; 2917 | } 2918 | 2919 | void FC_SetDefaultColor(FC_Font* font, SDL_Color color) 2920 | { 2921 | if(font == NULL) 2922 | return; 2923 | 2924 | font->default_color = color; 2925 | } 2926 | --------------------------------------------------------------------------------