|
1 /* |
|
2 SDL_FontCache: A font cache for SDL and SDL_ttf |
|
3 by Jonathan Dearborn |
|
4 |
|
5 See fontcche.h for license info. |
|
6 */ |
|
7 |
|
8 #include "fontcche.h" |
|
9 |
|
10 #include <stdio.h> |
|
11 #include <stdlib.h> |
|
12 #include <string.h> |
|
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 } |