├── 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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
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 |
--------------------------------------------------------------------------------