src/fontcche.c
changeset 0 b2e3aa63e96c
equal deleted inserted replaced
-1:000000000000 0:b2e3aa63e96c
       
     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 }