... forgot to add the files, I'm too used to git now :P
authorblub <blub@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 23 Dec 2009 10:48:04 +0000 (10:48 +0000)
committerblub <blub@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 23 Dec 2009 10:48:04 +0000 (10:48 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9642 d7cf8633-e32d-0410-b094-e92efae38249

ft2.c [new file with mode: 0644]
ft2.h [new file with mode: 0644]
ft2_defs.h [new file with mode: 0644]
ft2_fontdefs.h [new file with mode: 0644]
utf8lib.c [new file with mode: 0644]
utf8lib.h [new file with mode: 0644]

diff --git a/ft2.c b/ft2.c
new file mode 100644 (file)
index 0000000..5e0462e
--- /dev/null
+++ b/ft2.c
@@ -0,0 +1,1114 @@
+/* FreeType 2 and UTF-8 encoding support for
+ * DarkPlaces
+ */
+#include "quakedef.h"
+
+#include "ft2.h"
+#include "ft2_defs.h"
+#include "ft2_fontdefs.h"
+
+static int img_fontmap[256] = {
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+================================================================================
+CVars introduced with the freetype extension
+================================================================================
+*/
+
+cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"};
+cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"};
+cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
+
+/*
+================================================================================
+Function definitions. Taken from the freetype2 headers.
+================================================================================
+*/
+
+
+FT_EXPORT( FT_Error )
+(*qFT_Init_FreeType)( FT_Library  *alibrary );
+FT_EXPORT( FT_Error )
+(*qFT_Done_FreeType)( FT_Library  library );
+/*
+FT_EXPORT( FT_Error )
+(*qFT_New_Face)( FT_Library   library,
+                const char*  filepathname,
+                FT_Long      face_index,
+                FT_Face     *aface );
+*/
+FT_EXPORT( FT_Error )
+(*qFT_New_Memory_Face)( FT_Library      library,
+                       const FT_Byte*  file_base,
+                       FT_Long         file_size,
+                       FT_Long         face_index,
+                       FT_Face        *aface );
+FT_EXPORT( FT_Error )
+(*qFT_Done_Face)( FT_Face  face );
+FT_EXPORT( FT_Error )
+(*qFT_Select_Size)( FT_Face  face,
+                   FT_Int   strike_index );
+FT_EXPORT( FT_Error )
+(*qFT_Request_Size)( FT_Face          face,
+                    FT_Size_Request  req );
+FT_EXPORT( FT_Error )
+(*qFT_Set_Char_Size)( FT_Face     face,
+                     FT_F26Dot6  char_width,
+                     FT_F26Dot6  char_height,
+                     FT_UInt     horz_resolution,
+                     FT_UInt     vert_resolution );
+FT_EXPORT( FT_Error )
+(*qFT_Set_Pixel_Sizes)( FT_Face  face,
+                       FT_UInt  pixel_width,
+                       FT_UInt  pixel_height );
+FT_EXPORT( FT_Error )
+(*qFT_Load_Glyph)( FT_Face   face,
+                  FT_UInt   glyph_index,
+                  FT_Int32  load_flags );
+FT_EXPORT( FT_Error )
+(*qFT_Load_Char)( FT_Face   face,
+                 FT_ULong  char_code,
+                 FT_Int32  load_flags );
+FT_EXPORT( FT_UInt )
+(*qFT_Get_Char_Index)( FT_Face   face,
+                      FT_ULong  charcode );
+FT_EXPORT( FT_Error )
+(*qFT_Render_Glyph)( FT_GlyphSlot    slot,
+                    FT_Render_Mode  render_mode );
+FT_EXPORT( FT_Error )
+(*qFT_Get_Kerning)( FT_Face     face,
+                   FT_UInt     left_glyph,
+                   FT_UInt     right_glyph,
+                   FT_UInt     kern_mode,
+                   FT_Vector  *akerning );
+FT_EXPORT( FT_Error )
+(*qFT_Attach_Stream)( FT_Face        face,
+                     FT_Open_Args*  parameters );
+/*
+================================================================================
+Support for dynamically loading the FreeType2 library
+================================================================================
+*/
+
+static dllfunction_t ft2funcs[] =
+{
+       {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
+       {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
+       //{"FT_New_Face",                       (void **) &qFT_New_Face},
+       {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
+       {"FT_Done_Face",                (void **) &qFT_Done_Face},
+       {"FT_Select_Size",              (void **) &qFT_Select_Size},
+       {"FT_Request_Size",             (void **) &qFT_Request_Size},
+       {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
+       {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
+       {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
+       {"FT_Load_Char",                (void **) &qFT_Load_Char},
+       {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
+       {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
+       {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
+       {"FT_Attach_Stream",            (void **) &qFT_Attach_Stream},
+       {NULL, NULL}
+};
+
+/// Handle for FreeType2 DLL
+static dllhandle_t ft2_dll = NULL;
+
+/// Memory pool for fonts
+static mempool_t *font_mempool= NULL;
+static rtexturepool_t *font_texturepool = NULL;
+
+/// FreeType library handle
+static FT_Library font_ft2lib = NULL;
+
+/*
+====================
+Font_CloseLibrary
+
+Unload the FreeType2 DLL
+====================
+*/
+void Font_CloseLibrary (void)
+{
+       if (font_mempool)
+               Mem_FreePool(&font_mempool);
+       if (font_texturepool)
+               R_FreeTexturePool(&font_texturepool);
+       if (font_ft2lib && qFT_Done_FreeType)
+       {
+               qFT_Done_FreeType(font_ft2lib);
+               font_ft2lib = NULL;
+       }
+       Sys_UnloadLibrary (&ft2_dll);
+}
+
+/*
+====================
+Font_OpenLibrary
+
+Try to load the FreeType2 DLL
+====================
+*/
+qboolean Font_OpenLibrary (void)
+{
+       const char* dllnames [] =
+       {
+#if defined(WIN64)
+               #error path for freetype 2 dll
+#elif defined(WIN32)
+               #error path for freetype 2 dll
+#elif defined(MACOSX)
+               "libfreetype.dylib",
+#else
+               "libfreetype.so.6",
+               "libfreetype.so",
+#endif
+               NULL
+       };
+
+       if (r_font_disable_freetype.integer)
+               return false;
+
+       // Already loaded?
+       if (ft2_dll)
+               return true;
+
+       // Load the DLL
+       if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
+               return false;
+       return true;
+}
+
+/*
+====================
+Font_Init
+
+Initialize the freetype2 font subsystem
+====================
+*/
+
+void font_start(void)
+{
+       if (!Font_OpenLibrary())
+               return;
+
+       if (qFT_Init_FreeType(&font_ft2lib))
+       {
+               Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
+               Font_CloseLibrary();
+               return;
+       }
+
+       font_mempool = Mem_AllocPool("FONT", 0, NULL);
+       if (!font_mempool)
+       {
+               Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
+               Font_CloseLibrary();
+               return;
+       }
+
+       font_texturepool = R_AllocTexturePool();
+       if (!font_texturepool)
+       {
+               Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
+               Font_CloseLibrary();
+               return;
+       }
+}
+
+void font_shutdown(void)
+{
+       int i;
+       for (i = 0; i < MAX_FONTS; ++i)
+       {
+               if (dp_fonts[i].ft2)
+               {
+                       Font_UnloadFont(dp_fonts[i].ft2);
+                       dp_fonts[i].ft2 = NULL;
+               }
+       }
+       Font_CloseLibrary();
+}
+
+void font_newmap(void)
+{
+}
+
+void Font_Init(void)
+{
+       Cvar_RegisterVariable(&r_font_disable_freetype);
+       Cvar_RegisterVariable(&r_font_use_alpha_textures);
+       Cvar_RegisterVariable(&r_font_size_snapping);
+}
+
+/*
+================================================================================
+Implementation of a more or less lazy font loading and rendering code.
+================================================================================
+*/
+
+#include "ft2_fontdefs.h"
+
+ft2_font_t *Font_Alloc(void)
+{
+       if (!ft2_dll)
+               return NULL;
+       return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
+}
+
+qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment)
+{
+       ft2_attachment_t *na;
+
+       font->attachmentcount++;
+       na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount);
+       if (na == NULL)
+               return false;
+       if (font->attachments && font->attachmentcount > 1)
+       {
+               memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1));
+               Mem_Free(font->attachments);
+       }
+       memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment));
+       font->attachments = na;
+       return true;
+}
+
+static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font);
+static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning);
+qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
+{
+       int s, count, i;
+       ft2_font_t *ft2, *fbfont, *fb;
+
+       ft2 = Font_Alloc();
+       if (!ft2)
+       {
+               dpfnt->ft2 = NULL;
+               return false;
+       }
+
+       // check if a fallback font has been specified, if it has been, and the
+       // font fails to load, use the image font as main font
+       for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
+       {
+               if (dpfnt->fallbacks[i][0])
+                       break;
+       }
+
+       if (!Font_LoadFile(name, dpfnt->req_face, ft2))
+       {
+               if (i >= MAX_FONT_FALLBACKS)
+               {
+                       dpfnt->ft2 = NULL;
+                       Mem_Free(ft2);
+                       return false;
+               }
+               strlcpy(ft2->name, name, sizeof(ft2->name));
+               ft2->image_font = true;
+               ft2->has_kerning = false;
+       }
+       else
+       {
+               ft2->image_font = false;
+       }
+
+       // attempt to load fallback fonts:
+       fbfont = ft2;
+       for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
+       {
+               if (!dpfnt->fallbacks[i][0])
+                       break;
+               if (! (fb = Font_Alloc()) )
+               {
+                       Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
+                       break;
+               }
+               if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], fb))
+               {
+                       Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
+                       Mem_Free(fb);
+                       break;
+               }
+               count = 0;
+               for (s = 0; s < MAX_FONT_SIZES; ++s)
+               {
+                       if (Font_LoadSize(fb, dpfnt->req_sizes[s], true, false))
+                               ++count;
+               }
+               if (!count)
+               {
+                       Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name);
+                       Font_UnloadFont(fb);
+                       Mem_Free(fb);
+                       break;
+               }
+               // at least one size of the fallback font loaded successfully
+               // link it:
+               fbfont->next = fb;
+               fbfont = fb;
+       }
+
+       if (fbfont == ft2 && ft2->image_font)
+       {
+               // no fallbacks were loaded successfully:
+               dpfnt->ft2 = NULL;
+               Mem_Free(ft2);
+               return false;
+       }
+
+       count = 0;
+       for (s = 0; s < MAX_FONT_SIZES; ++s)
+       {
+               if (Font_LoadSize(ft2, dpfnt->req_sizes[s], false, false))
+                       ++count;
+       }
+       if (!count)
+       {
+               // loading failed for every requested size
+               Font_UnloadFont(ft2);
+               Mem_Free(ft2);
+               dpfnt->ft2 = NULL;
+               return false;
+       }
+
+       //Con_Printf("%i sizes loaded\n", count);
+       dpfnt->ft2 = ft2;
+       return true;
+}
+
+static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font)
+{
+       size_t namelen;
+       char filename[PATH_MAX];
+       int status;
+       size_t i;
+       unsigned char *data;
+       fs_offset_t datasize;
+
+       memset(font, 0, sizeof(*font));
+
+       if (!Font_OpenLibrary())
+       {
+               if (!r_font_disable_freetype.integer)
+               {
+                       Con_Printf("WARNING: can't open load font %s\n"
+                                  "You need the FreeType2 DLL to load font files\n",
+                                  name);
+               }
+               return false;
+       }
+
+       namelen = strlen(name);
+
+       memcpy(filename, name, namelen);
+       memcpy(filename + namelen, ".ttf", 5);
+       data = FS_LoadFile(filename, font_mempool, false, &datasize);
+       if (!data)
+       {
+               memcpy(filename + namelen, ".otf", 5);
+               data = FS_LoadFile(filename, font_mempool, false, &datasize);
+       }
+       if (!data)
+       {
+               ft2_attachment_t afm;
+
+               memcpy(filename + namelen, ".pfb", 5);
+               data = FS_LoadFile(filename, font_mempool, false, &datasize);
+
+               if (data)
+               {
+                       memcpy(filename + namelen, ".afm", 5);
+                       afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size);
+
+                       if (afm.data)
+                               Font_Attach(font, &afm);
+               }
+       }
+
+       if (!data)
+       {
+               // FS_LoadFile being not-quiet should print an error :)
+               return false;
+       }
+       Con_Printf("Loading font %s face %i...\n", filename, _face);
+
+       status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face);
+       if (status && _face != 0)
+       {
+               Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name);
+               _face = 0;
+               status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face);
+       }
+       if (status)
+       {
+               Con_Printf("ERROR: can't create face for %s\n"
+                          "Error %i\n", // TODO: error strings
+                          name, status);
+               Font_UnloadFont(font);
+               return false;
+       }
+
+       // add the attachments
+       for (i = 0; i < font->attachmentcount; ++i)
+       {
+               FT_Open_Args args;
+               memset(&args, 0, sizeof(args));
+               args.flags = FT_OPEN_MEMORY;
+               args.memory_base = (const FT_Byte*)font->attachments[i].data;
+               args.memory_size = font->attachments[i].size;
+               if (qFT_Attach_Stream(font->face, &args))
+                       Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name);
+       }
+
+       memcpy(font->name, name, namelen+1);
+       font->image_font = false;
+       font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
+       return true;
+}
+
+static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap);
+static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning)
+{
+       int map_index;
+       ft2_font_map_t *fmap, temp;
+
+       if (IS_NAN(size))
+               size = 0;
+
+       if (!size)
+               size = 16;
+       if (size < 2) // bogus sizes are not allowed - and they screw up our allocations
+               return false;
+
+       if (!no_texture)
+       {
+               for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index)
+               {
+                       if (!font->font_maps[map_index])
+                               break;
+                       // if a similar size has already been loaded, ignore this one
+                       //abs(font->font_maps[map_index]->size - size) < 4
+                       if (font->font_maps[map_index]->size == size)
+                               return true;
+               }
+
+               if (map_index >= MAX_FONT_SIZES)
+                       return false;
+
+               memset(&temp, 0, sizeof(temp));
+               temp.size = size;
+               temp.glyphSize = CeilPowerOf2(size*2);
+               temp.sfx = (1.0/64.0)/(double)size;
+               temp.sfy = (1.0/64.0)/(double)size;
+               temp.intSize = -1; // negative value: LoadMap must search now :)
+               if (!Font_LoadMap(font, &temp, 0, &fmap))
+               {
+                       Con_Printf("ERROR: can't load the first character map for %s\n"
+                                  "This is fatal\n",
+                                  font->name);
+                       Font_UnloadFont(font);
+                       return false;
+               }
+               font->font_maps[map_index] = temp.next;
+
+               fmap->sfx = temp.sfx;
+               fmap->sfy = temp.sfy;
+       }
+       if (!no_kerning)
+       {
+               // load the default kerning vector:
+               if (font->has_kerning)
+               {
+                       Uchar l, r;
+                       FT_Vector kernvec;
+                       for (l = 0; l < 256; ++l)
+                       {
+                               for (r = 0; r < 256; ++r)
+                               {
+                                       FT_ULong ul, ur;
+                                       ul = qFT_Get_Char_Index(font->face, l);
+                                       ur = qFT_Get_Char_Index(font->face, r);
+                                       if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
+                                       {
+                                               fmap->kerning.kerning[l][r][0] = 0;
+                                               fmap->kerning.kerning[l][r][1] = 0;
+                                       }
+                                       else
+                                       {
+                                               fmap->kerning.kerning[l][r][0] = (kernvec.x >> 6) / fmap->size;
+                                               fmap->kerning.kerning[l][r][1] = (kernvec.y >> 6) / fmap->size;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh)
+{
+       int match = -1;
+       int value = 1000000;
+       int nval;
+       int matchsize = -10000;
+       int m;
+       int size;
+       float fsize;
+       ft2_font_map_t **maps = font->font_maps;
+
+       fsize = _fsize * vid.height / vid_conheight.value;
+
+       if (fsize < 0)
+               size = 16;
+       else
+       {
+               // round up
+               size = (int)fsize;
+               if (fsize - (float)size >= 0.49)
+                       ++size;
+       }
+
+       for (m = 0; m < MAX_FONT_SIZES; ++m)
+       {
+               if (!maps[m])
+                       continue;
+               // "round up" to the bigger size if two equally-valued matches exist
+               nval = abs(maps[m]->size - size);
+               if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
+               {
+                       value = nval;
+                       match = m;
+                       matchsize = maps[m]->size;
+                       if (value == 0) // there is no better match
+                               break;
+               }
+       }
+       if (r_font_size_snapping.integer && value <= 1)
+       {
+               if (outw && outh)
+               {
+                       if (!*outh) *outh = *outw;
+                       if (!*outw) *outw = *outh;
+               }
+               // keep the aspect
+               if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
+               if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width * *outw / _fsize;
+       }
+       return match;
+}
+
+ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index)
+{
+       if (index < 0 || index >= MAX_FONT_SIZES)
+               return NULL;
+       return font->font_maps[index];
+}
+
+static qboolean Font_SetSize(ft2_font_t *font, float w, float h)
+{
+       if (font->currenth == h &&
+           ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set
+            font->currentw == w)) // same size has been requested
+       {
+               return true;
+       }
+       // sorry, but freetype doesn't seem to care about other sizes
+       w = (int)w;
+       h = (int)h;
+       if (font->image_font)
+       {
+               if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
+                       return false;
+       }
+       else
+       {
+               if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72))
+                       return false;
+       }
+       font->currentw = w;
+       font->currenth = h;
+       return true;
+}
+
+qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
+{
+       ft2_font_map_t *fmap;
+       if (!font->has_kerning)
+               return false;
+       if (map_index < 0 || map_index >= MAX_FONT_SIZES)
+               return false;
+       fmap = font->font_maps[map_index];
+       if (!fmap)
+               return false;
+       if (left < 256 && right < 256)
+       {
+               // quick-kerning, be aware of the size: scale it
+               if (outx) *outx = fmap->kerning.kerning[left][right][0] * w / (float)fmap->size;
+               if (outy) *outy = fmap->kerning.kerning[left][right][1] * h / (float)fmap->size;
+               return true;
+       }
+       else
+       {
+               FT_Vector kernvec;
+               FT_ULong ul, ur;
+
+               //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
+               if (!Font_SetSize(font, w, h))
+               {
+                       // this deserves an error message
+                       Con_Printf("Failed to get kerning for %s\n", font->name);
+                       return false;
+               }
+               ul = qFT_Get_Char_Index(font->face, left);
+               ur = qFT_Get_Char_Index(font->face, right);
+               if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
+               {
+                       if (outx) *outx = kernvec.x * fmap->sfx;
+                       if (outy) *outy = kernvec.y * fmap->sfy;
+                       return true;
+               }
+               return false;
+       }
+}
+
+qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
+{
+       return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy);
+}
+
+static void UnloadMapRec(ft2_font_map_t *map)
+{
+       if (map->texture)
+       {
+               R_FreeTexture(map->texture);
+               map->texture = NULL;
+       }
+       if (map->next)
+               UnloadMapRec(map->next);
+       Mem_Free(map);
+}
+
+void Font_UnloadFont(ft2_font_t *font)
+{
+       int i;
+       if (font->attachments && font->attachmentcount)
+       {
+               Mem_Free(font->attachments);
+               font->attachmentcount = 0;
+               font->attachments = NULL;
+       }
+       for (i = 0; i < MAX_FONT_SIZES; ++i)
+       {
+               if (font->font_maps[i])
+               {
+                       UnloadMapRec(font->font_maps[i]);
+                       font->font_maps[i] = NULL;
+               }
+       }
+       if (ft2_dll)
+       {
+               if (font->face)
+               {
+                       qFT_Done_Face((FT_Face)font->face);
+                       font->face = NULL;
+               }
+       }
+}
+
+static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap)
+{
+       char map_identifier[PATH_MAX];
+       unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
+       unsigned char *data;
+       FT_ULong ch, mapch;
+       int status;
+       int tp;
+
+       int pitch;
+       int gR, gC; // glyph position: row and column
+
+       ft2_font_map_t *map, *next;
+       ft2_font_t *usefont;
+
+       FT_Face fontface;
+
+       int bytesPerPixel = 4; // change the conversion loop too if you change this!
+
+       if (outmap)
+               *outmap = NULL;
+
+       if (r_font_use_alpha_textures.integer)
+               bytesPerPixel = 1;
+
+       if (font->image_font)
+               fontface = (FT_Face)font->next->face;
+       else
+               fontface = (FT_Face)font->face;
+
+       //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size);
+       //if (status)
+       if (font->image_font && mapstart->intSize < 0)
+               mapstart->intSize = mapstart->size;
+       if (mapstart->intSize < 0)
+       {
+               mapstart->intSize = mapstart->size;
+               while (1)
+               {
+                       if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize))
+                       {
+                               Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize);
+                               return false;
+                       }
+                       if ((fontface->size->metrics.height>>6) <= mapstart->size)
+                               break;
+                       if (mapstart->intSize < 2)
+                       {
+                               Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size);
+                               return false;
+                       }
+                       --mapstart->intSize;
+               }
+               if (developer.integer)
+                       Con_Printf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size);
+       }
+
+       if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize))
+       {
+               Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size);
+               return false;
+       }
+
+       map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
+       if (!map)
+       {
+               Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
+               return false;
+       }
+
+       // copy over the information
+       map->size = mapstart->size;
+       map->intSize = mapstart->intSize;
+       map->glyphSize = mapstart->glyphSize;
+       map->sfx = mapstart->sfx;
+       map->sfy = mapstart->sfy;
+
+       pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
+       data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch);
+       if (!data)
+       {
+               Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size);
+               Mem_Free(map);
+               return false;
+       }
+
+       // initialize as white texture with zero alpha
+       tp = 0;
+       while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch)
+       {
+               if (bytesPerPixel == 4)
+               {
+                       data[tp++] = 0xFF;
+                       data[tp++] = 0xFF;
+                       data[tp++] = 0xFF;
+               }
+               data[tp++] = 0x00;
+       }
+
+       // insert the map
+       map->start = mapidx * FONT_CHARS_PER_MAP;
+       next = mapstart;
+       while(next->next && next->next->start < map->start)
+               next = next->next;
+       map->next = next->next;
+       next->next = map;
+
+       gR = 0;
+       gC = -1;
+       for (ch = map->start;
+            ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
+            ++ch)
+       {
+               FT_ULong glyphIndex;
+               int w, h, x, y;
+               FT_GlyphSlot glyph;
+               FT_Bitmap *bmp;
+               unsigned char *imagedata, *dst, *src;
+               glyph_slot_t *mapglyph;
+               FT_Face face;
+
+               mapch = ch - map->start;
+
+               if (developer.integer)
+                       Con_Print("glyphinfo: ------------- GLYPH INFO -----------------\n");
+
+               ++gC;
+               if (gC >= FONT_CHARS_PER_LINE)
+               {
+                       gC -= FONT_CHARS_PER_LINE;
+                       ++gR;
+               }
+
+               imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel;
+               //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER);
+               // we need the glyphIndex
+               face = font->face;
+               usefont = NULL;
+               if (font->image_font && mapch == ch && img_fontmap[mapch])
+               {
+                       map->glyphs[mapch].image = true;
+                       continue;
+               }
+               glyphIndex = qFT_Get_Char_Index(face, ch);
+               if (glyphIndex == 0)
+               {
+                       // by convention, 0 is the "missing-glyph"-glyph
+                       // try to load from a fallback font
+                       for(usefont = font->next; usefont != NULL; usefont = usefont->next)
+                       {
+                               if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize))
+                                       continue;
+                               // try that glyph
+                               face = usefont->face;
+                               glyphIndex = qFT_Get_Char_Index(face, ch);
+                               if (glyphIndex == 0)
+                                       continue;
+                               status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
+                               if (status)
+                                       continue;
+                               break;
+                       }
+                       if (!usefont)
+                       {
+                               //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
+                               // now we let it use the "missing-glyph"-glyph
+                               face = font->face;
+                               glyphIndex = 0;
+                       }
+               }
+
+               if (!usefont)
+               {
+                       usefont = font;
+                       face = font->face;
+                       status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
+                       if (status)
+                       {
+                               //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
+                               Con_Printf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name);
+                               continue;
+                       }
+               }
+
+               glyph = face->glyph;
+               bmp = &glyph->bitmap;
+
+               w = bmp->width;
+               h = bmp->rows;
+
+               if (w > map->glyphSize || h > map->glyphSize) {
+                       Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h);
+                       if (w > map->glyphSize)
+                               w = map->glyphSize;
+                       if (h > map->glyphSize)
+                               h = map->glyphSize;
+               }
+
+               switch (bmp->pixel_mode)
+               {
+               case FT_PIXEL_MODE_MONO:
+                       if (developer.integer)
+                               Con_Print("glyphinfo:   Pixel Mode: MONO\n");
+                       break;
+               case FT_PIXEL_MODE_GRAY2:
+                       if (developer.integer)
+                               Con_Print("glyphinfo:   Pixel Mode: GRAY2\n");
+                       break;
+               case FT_PIXEL_MODE_GRAY4:
+                       if (developer.integer)
+                               Con_Print("glyphinfo:   Pixel Mode: GRAY4\n");
+                       break;
+               case FT_PIXEL_MODE_GRAY:
+                       if (developer.integer)
+                               Con_Print("glyphinfo:   Pixel Mode: GRAY\n");
+                       break;
+               default:
+                       if (developer.integer)
+                               Con_Printf("glyphinfo:   Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
+                       Mem_Free(data);
+                       Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode);
+                       return false;
+               }
+               for (y = 0; y < h; ++y)
+               {
+                       dst = imagedata + y * pitch;
+                       src = bmp->buffer + y * bmp->pitch;
+
+                       switch (bmp->pixel_mode)
+                       {
+                       case FT_PIXEL_MODE_MONO:
+                               dst += bytesPerPixel - 1; // shift to alpha byte
+                               for (x = 0; x < bmp->width; x += 8)
+                               {
+                                       unsigned char ch = *src++;
+                                       *dst = 255 * ((ch & 0x80) >> 7); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x40) >> 6); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x20) >> 5); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x10) >> 4); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x08) >> 3); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x04) >> 2); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x02) >> 1); dst += bytesPerPixel;
+                                       *dst = 255 * ((ch & 0x01) >> 0); dst += bytesPerPixel;
+                               }
+                               break;
+                       case FT_PIXEL_MODE_GRAY2:
+                               dst += bytesPerPixel - 1; // shift to alpha byte
+                               for (x = 0; x < bmp->width; x += 4)
+                               {
+                                       unsigned char ch = *src++;
+                                       *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
+                                       *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
+                                       *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
+                                       *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel;
+                               }
+                               break;
+                       case FT_PIXEL_MODE_GRAY4:
+                               dst += bytesPerPixel - 1; // shift to alpha byte
+                               for (x = 0; x < bmp->width; x += 2)
+                               {
+                                       unsigned char ch = *src++;
+                                       *dst = ( ((ch & 0xF0) >> 4) * 0x24); dst += bytesPerPixel;
+                                       *dst = ( ((ch & 0x0F) ) * 0x24); dst += bytesPerPixel;
+                               }
+                               break;
+                       case FT_PIXEL_MODE_GRAY:
+                               // in this case pitch should equal width
+                               for (tp = 0; tp < bmp->pitch; ++tp)
+                                       dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes
+
+                               //memcpy((void*)dst, (void*)src, bmp->pitch);
+                               //dst += bmp->pitch;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               // now fill map->glyphs[ch - map->start]
+               mapglyph = &map->glyphs[mapch];
+
+               {
+                       // old way
+                       // double advance = (double)glyph->metrics.horiAdvance * map->sfx;
+
+                       double bearingX = (glyph->metrics.horiBearingX >> 6) / map->size;
+                       double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
+                       double advance = (glyph->advance.x >> 6) / map->size;
+                       double mWidth = (glyph->metrics.width >> 6) / map->size;
+                       double mHeight = (glyph->metrics.height >> 6) / map->size;
+
+                       mapglyph->vxmin = bearingX;
+                       mapglyph->vxmax = bearingX + mWidth;
+                       mapglyph->vymin = -bearingY;
+                       mapglyph->vymax = mHeight - bearingY;
+                       mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
+                       mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) );
+                       mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
+                       mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(map->glyphSize * FONT_CHAR_LINES) );
+                       //mapglyph->advance_x = advance * usefont->size;
+                       mapglyph->advance_x = advance;
+                       mapglyph->advance_y = 0;
+
+                       if (developer.integer)
+                       {
+                               Con_Printf("glyphinfo:   Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
+                               Con_Printf("glyphinfo:   %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX);
+                               if (ch >= 32 && ch <= 128)
+                                       Con_Printf("glyphinfo:   Character: %c\n", (int)ch);
+                               Con_Printf("glyphinfo:   Vertex info:\n");
+                               Con_Printf("glyphinfo:     X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
+                               Con_Printf("glyphinfo:     Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
+                               Con_Printf("glyphinfo:   Texture info:\n");
+                               Con_Printf("glyphinfo:     S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
+                               Con_Printf("glyphinfo:     T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
+                               Con_Printf("glyphinfo:   Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
+                       }
+               }
+               map->glyphs[mapch].image = false;
+       }
+
+       // create a texture from the data now
+
+       if (developer.integer)
+       {
+               // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
+               dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
+               FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize);
+       }
+       dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
+
+       // probably use bytesPerPixel here instead?
+       if (r_font_use_alpha_textures.integer)
+       {
+               map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
+                                              map->glyphSize * FONT_CHARS_PER_LINE,
+                                              map->glyphSize * FONT_CHAR_LINES,
+                                              data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
+       } else {
+               map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
+                                              map->glyphSize * FONT_CHARS_PER_LINE,
+                                              map->glyphSize * FONT_CHAR_LINES,
+                                              data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL);
+       }
+
+       Mem_Free(data);
+       if (!map->texture)
+       {
+               // if the first try isn't successful, keep it with a broken texture
+               // otherwise we retry to load it every single frame where ft2 rendering is used
+               // this would be bad...
+               // only `data' must be freed
+               Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n",
+                          font->name, mapstart->size, mapidx);
+               return false;
+       }
+       if (outmap)
+               *outmap = map;
+       return true;
+}
+
+qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap)
+{
+       if (map_index < 0 || map_index >= MAX_FONT_SIZES)
+               return false;
+       // the first map must have been loaded already
+       if (!font->font_maps[map_index])
+               return false;
+       return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap);
+}
+
+ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch)
+{
+       while (start && start->start + FONT_CHARS_PER_MAP < ch)
+               start = start->next;
+       if (start && start->start > ch)
+               return NULL;
+       return start;
+}
diff --git a/ft2.h b/ft2.h
new file mode 100644 (file)
index 0000000..99de79a
--- /dev/null
+++ b/ft2.h
@@ -0,0 +1,78 @@
+/* Header for FreeType 2 and UTF-8 encoding support for
+ * DarkPlaces
+ */
+
+#ifndef DP_FREETYPE2_H__
+#define DP_FREETYPE2_H__
+
+//#include <sys/types.h>
+
+#include "utf8lib.h"
+
+/* 
+ * From http://www.unicode.org/Public/UNIDATA/Blocks.txt
+ *
+ *   E000..F8FF; Private Use Area
+ *   F0000..FFFFF; Supplementary Private Use Area-A
+ *
+ * We use:
+ *   Range E000 - E0FF
+ *     Contains the non-FreeType2 version of characters.
+ */
+
+typedef struct ft2_font_map_s ft2_font_map_t;
+typedef struct ft2_attachment_s ft2_attachment_t;
+#define ft2_oldstyle_map ((ft2_font_map_t*)-1)
+
+typedef float ft2_kernvec[2];
+typedef struct ft2_kerning_s
+{
+       ft2_kernvec kerning[256][256]; /* kerning[left char][right char] */
+} ft2_kerning_t;
+
+typedef struct ft2_font_s
+{
+       char            name[64];
+       qboolean        has_kerning;
+       // last requested size loaded using Font_SetSize
+       float           currentw;
+       float           currenth;
+       float           ascend;
+       float           descend;
+       qboolean        image_font; // only fallbacks are freetype fonts
+
+       // TODO: clean this up and do not expose everything.
+       
+       //unsigned char  *data;
+       //fs_offset_t     datasize;
+       void           *face;
+
+       // an unordered array of ordered linked lists of glyph maps for a specific size
+       ft2_font_map_t *font_maps[MAX_FONT_SIZES];
+       int             num_sizes;
+
+       // attachments
+       size_t            attachmentcount;
+       ft2_attachment_t *attachments;
+
+       // fallback mechanism
+       struct ft2_font_s *next;
+} ft2_font_t;
+
+void            Font_CloseLibrary(void);
+void            Font_Init(void);
+qboolean        Font_OpenLibrary(void);
+ft2_font_t*     Font_Alloc(void);
+void            Font_UnloadFont(ft2_font_t *font);
+// IndexForSize suggests to change the width and height if a font size is in a reasonable range
+// for example, you render at a size of 12.4, and a font of size 12 has been loaded
+// in such a case, *outw and *outh are set to 12, which is often a good alternative size
+int             Font_IndexForSize(ft2_font_t *font, float size, float *outw, float *outh);
+ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index);
+qboolean        Font_LoadFont(const char *name, dp_font_t *dpfnt);
+qboolean        Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
+qboolean        Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy);
+
+// since this is used on a font_map_t, let's name it FontMap_*
+ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch);
+#endif // DP_FREETYPE2_H__
diff --git a/ft2_defs.h b/ft2_defs.h
new file mode 100644 (file)
index 0000000..fd15998
--- /dev/null
@@ -0,0 +1,499 @@
+/* FreeType 2 definitions from the freetype header mostly.
+ */
+
+#ifndef FT2_DEFS_H_H__
+#define FT2_DEFS_H_H__
+
+#ifdef _MSC_VER
+typedef __int32 FT_Int32;
+typedef __uint32 FT_UInt32;
+#else
+typedef int32_t FT_Int32;
+typedef u_int32_t FT_UInt32;
+#endif
+
+typedef int FT_Error;
+
+typedef signed char FT_Char;
+typedef unsigned char FT_Byte;
+typedef const FT_Byte *FT_Bytes;
+typedef char FT_String;
+typedef signed short FT_Short;
+typedef unsigned short FT_UShort;
+typedef signed int FT_Int;
+typedef unsigned int FT_UInt;
+typedef signed long FT_Long;
+typedef signed long FT_Fixed;
+typedef unsigned long FT_ULong;
+typedef void *FT_Pointer;
+typedef size_t FT_Offset;
+typedef signed long FT_F26Dot6;
+
+typedef void *FT_Stream;
+typedef void *FT_Module;
+typedef void *FT_Library;
+typedef struct FT_FaceRec_ *FT_Face;
+typedef struct FT_CharMapRec_*  FT_CharMap;
+typedef struct FT_SizeRec_*  FT_Size;
+typedef struct FT_Size_InternalRec_*  FT_Size_Internal;
+typedef struct FT_GlyphSlotRec_*  FT_GlyphSlot;
+typedef struct FT_SubGlyphRec_*  FT_SubGlyph;
+typedef struct FT_Slot_InternalRec_* FT_Slot_Internal;
+
+// Taken from the freetype headers:
+typedef signed long FT_Pos;
+typedef struct FT_Vector_
+{
+       FT_Pos x;
+       FT_Pos y;
+} FT_Vector;
+
+typedef struct  FT_BBox_
+{
+       FT_Pos  xMin, yMin;
+       FT_Pos  xMax, yMax;
+} FT_BBox;
+
+typedef enum  FT_Pixel_Mode_
+{
+       FT_PIXEL_MODE_NONE = 0,
+       FT_PIXEL_MODE_MONO,
+       FT_PIXEL_MODE_GRAY,
+       FT_PIXEL_MODE_GRAY2,
+       FT_PIXEL_MODE_GRAY4,
+       FT_PIXEL_MODE_LCD,
+       FT_PIXEL_MODE_LCD_V,
+       FT_PIXEL_MODE_MAX      /* do not remove */
+} FT_Pixel_Mode;
+typedef enum  FT_Render_Mode_
+{
+       FT_RENDER_MODE_NORMAL = 0,
+       FT_RENDER_MODE_LIGHT,
+       FT_RENDER_MODE_MONO,
+       FT_RENDER_MODE_LCD,
+       FT_RENDER_MODE_LCD_V,
+
+       FT_RENDER_MODE_MAX
+} FT_Render_Mode;
+
+#define ft_pixel_mode_none   FT_PIXEL_MODE_NONE
+#define ft_pixel_mode_mono   FT_PIXEL_MODE_MONO
+#define ft_pixel_mode_grays  FT_PIXEL_MODE_GRAY
+#define ft_pixel_mode_pal2   FT_PIXEL_MODE_GRAY2
+#define ft_pixel_mode_pal4   FT_PIXEL_MODE_GRAY4
+
+typedef struct  FT_Bitmap_
+{
+       int             rows;
+       int             width;
+       int             pitch;
+       unsigned char*  buffer;
+       short           num_grays;
+       char            pixel_mode;
+       char            palette_mode;
+       void*           palette;
+} FT_Bitmap;
+
+typedef struct  FT_Outline_
+{
+       short       n_contours;      /* number of contours in glyph        */
+       short       n_points;        /* number of points in the glyph      */
+
+       FT_Vector*  points;          /* the outline's points               */
+       char*       tags;            /* the points flags                   */
+       short*      contours;        /* the contour end points             */
+
+       int         flags;           /* outline masks                      */
+} FT_Outline;
+
+#define FT_OUTLINE_NONE             0x0
+#define FT_OUTLINE_OWNER            0x1
+#define FT_OUTLINE_EVEN_ODD_FILL    0x2
+#define FT_OUTLINE_REVERSE_FILL     0x4
+#define FT_OUTLINE_IGNORE_DROPOUTS  0x8
+#define FT_OUTLINE_SMART_DROPOUTS   0x10
+#define FT_OUTLINE_INCLUDE_STUBS    0x20
+
+#define FT_OUTLINE_HIGH_PRECISION   0x100
+#define FT_OUTLINE_SINGLE_PASS      0x200
+
+#define ft_outline_none             FT_OUTLINE_NONE
+#define ft_outline_owner            FT_OUTLINE_OWNER
+#define ft_outline_even_odd_fill    FT_OUTLINE_EVEN_ODD_FILL
+#define ft_outline_reverse_fill     FT_OUTLINE_REVERSE_FILL
+#define ft_outline_ignore_dropouts  FT_OUTLINE_IGNORE_DROPOUTS
+#define ft_outline_high_precision   FT_OUTLINE_HIGH_PRECISION
+#define ft_outline_single_pass      FT_OUTLINE_SINGLE_PASS
+
+#define FT_CURVE_TAG( flag )  ( flag & 3 )
+
+#define FT_CURVE_TAG_ON           1
+#define FT_CURVE_TAG_CONIC        0
+#define FT_CURVE_TAG_CUBIC        2
+
+#define FT_CURVE_TAG_TOUCH_X      8  /* reserved for the TrueType hinter */
+#define FT_CURVE_TAG_TOUCH_Y     16  /* reserved for the TrueType hinter */
+
+#define FT_CURVE_TAG_TOUCH_BOTH  ( FT_CURVE_TAG_TOUCH_X | \
+                                   FT_CURVE_TAG_TOUCH_Y )
+
+#define FT_Curve_Tag_On       FT_CURVE_TAG_ON
+#define FT_Curve_Tag_Conic    FT_CURVE_TAG_CONIC
+#define FT_Curve_Tag_Cubic    FT_CURVE_TAG_CUBIC
+#define FT_Curve_Tag_Touch_X  FT_CURVE_TAG_TOUCH_X
+#define FT_Curve_Tag_Touch_Y  FT_CURVE_TAG_TOUCH_Y
+
+typedef int
+(*FT_Outline_MoveToFunc)( const FT_Vector*  to,
+                         void*             user );
+#define FT_Outline_MoveTo_Func  FT_Outline_MoveToFunc
+
+typedef int
+(*FT_Outline_LineToFunc)( const FT_Vector*  to,
+                         void*             user );
+#define FT_Outline_LineTo_Func  FT_Outline_LineToFunc
+
+typedef int
+(*FT_Outline_ConicToFunc)( const FT_Vector*  control,
+                          const FT_Vector*  to,
+                          void*             user );
+#define FT_Outline_ConicTo_Func  FT_Outline_ConicToFunc
+
+typedef int
+(*FT_Outline_CubicToFunc)( const FT_Vector*  control1,
+                          const FT_Vector*  control2,
+                          const FT_Vector*  to,
+                          void*             user );
+#define FT_Outline_CubicTo_Func  FT_Outline_CubicToFunc
+
+typedef struct  FT_Outline_Funcs_
+{
+       FT_Outline_MoveToFunc   move_to;
+       FT_Outline_LineToFunc   line_to;
+       FT_Outline_ConicToFunc  conic_to;
+       FT_Outline_CubicToFunc  cubic_to;
+
+       int                     shift;
+       FT_Pos                  delta;
+} FT_Outline_Funcs;
+
+#ifndef FT_IMAGE_TAG
+#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 )  \
+          value = ( ( (unsigned long)_x1 << 24 ) | \
+                    ( (unsigned long)_x2 << 16 ) | \
+                    ( (unsigned long)_x3 << 8  ) | \
+                      (unsigned long)_x4         )
+#endif /* FT_IMAGE_TAG */
+
+typedef enum  FT_Glyph_Format_
+{
+       FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ),
+
+       FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ),
+       FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP,    'b', 'i', 't', 's' ),
+       FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE,   'o', 'u', 't', 'l' ),
+       FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER,   'p', 'l', 'o', 't' )
+} FT_Glyph_Format;
+#define ft_glyph_format_none       FT_GLYPH_FORMAT_NONE
+#define ft_glyph_format_composite  FT_GLYPH_FORMAT_COMPOSITE
+#define ft_glyph_format_bitmap     FT_GLYPH_FORMAT_BITMAP
+#define ft_glyph_format_outline    FT_GLYPH_FORMAT_OUTLINE
+#define ft_glyph_format_plotter    FT_GLYPH_FORMAT_PLOTTER
+
+typedef struct  FT_Glyph_Metrics_
+{
+       FT_Pos  width;
+       FT_Pos  height;
+
+       FT_Pos  horiBearingX;
+       FT_Pos  horiBearingY;
+       FT_Pos  horiAdvance;
+
+       FT_Pos  vertBearingX;
+       FT_Pos  vertBearingY;
+       FT_Pos  vertAdvance;
+} FT_Glyph_Metrics;
+
+#define FT_EXPORT( x )  x
+
+#define FT_OPEN_MEMORY    0x1
+#define FT_OPEN_STREAM    0x2
+#define FT_OPEN_PATHNAME  0x4
+#define FT_OPEN_DRIVER    0x8
+#define FT_OPEN_PARAMS    0x10
+
+typedef struct  FT_Parameter_
+{
+       FT_ULong    tag;
+       FT_Pointer  data;
+} FT_Parameter;
+
+typedef struct  FT_Open_Args_
+{
+       FT_UInt         flags;
+       const FT_Byte*  memory_base;
+       FT_Long         memory_size;
+       FT_String*      pathname;
+       FT_Stream       stream;
+       FT_Module       driver;
+       FT_Int          num_params;
+       FT_Parameter*   params;
+} FT_Open_Args;
+typedef enum  FT_Size_Request_Type_
+{
+       FT_SIZE_REQUEST_TYPE_NOMINAL,
+       FT_SIZE_REQUEST_TYPE_REAL_DIM,
+       FT_SIZE_REQUEST_TYPE_BBOX,
+       FT_SIZE_REQUEST_TYPE_CELL,
+       FT_SIZE_REQUEST_TYPE_SCALES,
+
+       FT_SIZE_REQUEST_TYPE_MAX
+
+} FT_Size_Request_Type;
+typedef struct  FT_Size_RequestRec_
+{
+       FT_Size_Request_Type  type;
+       FT_Long               width;
+       FT_Long               height;
+       FT_UInt               horiResolution;
+       FT_UInt               vertResolution;
+} FT_Size_RequestRec;
+typedef struct FT_Size_RequestRec_  *FT_Size_Request;
+
+#define FT_LOAD_DEFAULT                      0x0
+#define FT_LOAD_NO_SCALE                     0x1
+#define FT_LOAD_NO_HINTING                   0x2
+#define FT_LOAD_RENDER                       0x4
+#define FT_LOAD_NO_BITMAP                    0x8
+#define FT_LOAD_VERTICAL_LAYOUT              0x10
+#define FT_LOAD_FORCE_AUTOHINT               0x20
+#define FT_LOAD_CROP_BITMAP                  0x40
+#define FT_LOAD_PEDANTIC                     0x80
+#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH  0x200
+#define FT_LOAD_NO_RECURSE                   0x400
+#define FT_LOAD_IGNORE_TRANSFORM             0x800
+#define FT_LOAD_MONOCHROME                   0x1000
+#define FT_LOAD_LINEAR_DESIGN                0x2000
+#define FT_LOAD_NO_AUTOHINT                  0x8000U
+
+#define FT_LOAD_TARGET_( x )   ( (FT_Int32)( (x) & 15 ) << 16 )
+
+#define FT_LOAD_TARGET_NORMAL  FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL )
+#define FT_LOAD_TARGET_LIGHT   FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT  )
+#define FT_LOAD_TARGET_MONO    FT_LOAD_TARGET_( FT_RENDER_MODE_MONO   )
+#define FT_LOAD_TARGET_LCD     FT_LOAD_TARGET_( FT_RENDER_MODE_LCD    )
+#define FT_LOAD_TARGET_LCD_V   FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V  )
+
+#define FT_ENC_TAG( value, a, b, c, d )              \
+       value = ( ( (FT_UInt32)(a) << 24 ) |                      \
+                 ( (FT_UInt32)(b) << 16 ) |                            \
+                 ( (FT_UInt32)(c) <<  8 ) |                            \
+                 (FT_UInt32)(d)         )
+
+typedef enum  FT_Encoding_
+{
+       FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ),
+
+       FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ),
+       FT_ENC_TAG( FT_ENCODING_UNICODE,   'u', 'n', 'i', 'c' ),
+
+       FT_ENC_TAG( FT_ENCODING_SJIS,    's', 'j', 'i', 's' ),
+       FT_ENC_TAG( FT_ENCODING_GB2312,  'g', 'b', ' ', ' ' ),
+       FT_ENC_TAG( FT_ENCODING_BIG5,    'b', 'i', 'g', '5' ),
+       FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ),
+       FT_ENC_TAG( FT_ENCODING_JOHAB,   'j', 'o', 'h', 'a' ),
+
+       /* for backwards compatibility */
+       FT_ENCODING_MS_SJIS    = FT_ENCODING_SJIS,
+       FT_ENCODING_MS_GB2312  = FT_ENCODING_GB2312,
+       FT_ENCODING_MS_BIG5    = FT_ENCODING_BIG5,
+       FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG,
+       FT_ENCODING_MS_JOHAB   = FT_ENCODING_JOHAB,
+
+       FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ),
+       FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT,   'A', 'D', 'B', 'E' ),
+       FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM,   'A', 'D', 'B', 'C' ),
+       FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1,  'l', 'a', 't', '1' ),
+
+       FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ),
+
+       FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' )
+} FT_Encoding;
+
+#define ft_encoding_none            FT_ENCODING_NONE
+#define ft_encoding_unicode         FT_ENCODING_UNICODE
+#define ft_encoding_symbol          FT_ENCODING_MS_SYMBOL
+#define ft_encoding_latin_1         FT_ENCODING_ADOBE_LATIN_1
+#define ft_encoding_latin_2         FT_ENCODING_OLD_LATIN_2
+#define ft_encoding_sjis            FT_ENCODING_SJIS
+#define ft_encoding_gb2312          FT_ENCODING_GB2312
+#define ft_encoding_big5            FT_ENCODING_BIG5
+#define ft_encoding_wansung         FT_ENCODING_WANSUNG
+#define ft_encoding_johab           FT_ENCODING_JOHAB
+
+#define ft_encoding_adobe_standard  FT_ENCODING_ADOBE_STANDARD
+#define ft_encoding_adobe_expert    FT_ENCODING_ADOBE_EXPERT
+#define ft_encoding_adobe_custom    FT_ENCODING_ADOBE_CUSTOM
+#define ft_encoding_apple_roman     FT_ENCODING_APPLE_ROMAN
+
+typedef struct  FT_Bitmap_Size_
+{
+       FT_Short  height;
+       FT_Short  width;
+
+       FT_Pos    size;
+
+       FT_Pos    x_ppem;
+       FT_Pos    y_ppem;
+} FT_Bitmap_Size;
+
+typedef struct  FT_CharMapRec_
+{
+       FT_Face      face;
+       FT_Encoding  encoding;
+       FT_UShort    platform_id;
+       FT_UShort    encoding_id;
+} FT_CharMapRec;
+
+typedef void  (*FT_Generic_Finalizer)(void*  object);
+typedef struct  FT_Generic_
+{
+       void*                 data;
+       FT_Generic_Finalizer  finalizer;
+} FT_Generic;
+
+typedef struct  FT_Size_Metrics_
+{
+       FT_UShort  x_ppem;      /* horizontal pixels per EM               */
+       FT_UShort  y_ppem;      /* vertical pixels per EM                 */
+
+       FT_Fixed   x_scale;     /* scaling values used to convert font    */
+       FT_Fixed   y_scale;     /* units to 26.6 fractional pixels        */
+
+       FT_Pos     ascender;    /* ascender in 26.6 frac. pixels          */
+       FT_Pos     descender;   /* descender in 26.6 frac. pixels         */
+       FT_Pos     height;      /* text height in 26.6 frac. pixels       */
+       FT_Pos     max_advance; /* max horizontal advance, in 26.6 pixels */
+} FT_Size_Metrics;
+
+typedef struct  FT_SizeRec_
+{
+       FT_Face           face;      /* parent face object              */
+       FT_Generic        generic;   /* generic pointer for client uses */
+       FT_Size_Metrics   metrics;   /* size metrics                    */
+       FT_Size_Internal  internal;
+} FT_SizeRec;
+
+typedef struct  FT_FaceRec_
+{
+       FT_Long           num_faces;
+       FT_Long           face_index;
+
+       FT_Long           face_flags;
+       FT_Long           style_flags;
+
+       FT_Long           num_glyphs;
+
+       FT_String*        family_name;
+       FT_String*        style_name;
+
+       FT_Int            num_fixed_sizes;
+       FT_Bitmap_Size*   available_sizes;
+
+       FT_Int            num_charmaps;
+       FT_CharMap*       charmaps;
+
+       FT_Generic        generic;
+
+       /*# The following member variables (down to `underline_thickness') */
+       /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size    */
+       /*# for bitmap fonts.                                              */
+       FT_BBox           bbox;
+
+       FT_UShort         units_per_EM;
+       FT_Short          ascender;
+       FT_Short          descender;
+       FT_Short          height;
+
+       FT_Short          max_advance_width;
+       FT_Short          max_advance_height;
+
+       FT_Short          underline_position;
+       FT_Short          underline_thickness;
+
+       FT_GlyphSlot      glyph;
+       FT_Size           size;
+       FT_CharMap        charmap;
+
+       /* ft2 private
+       FT_Driver         driver;
+       FT_Memory         memory;
+       FT_Stream         stream;
+
+       FT_ListRec        sizes_list;
+
+       FT_Generic        autohint;
+       void*             extensions;
+
+       FT_Face_Internal  internal;
+       */
+} FT_FaceRec;
+
+typedef struct  FT_GlyphSlotRec_
+{
+       FT_Library        library;
+       FT_Face           face;
+       FT_GlyphSlot      next;
+       FT_UInt           reserved;       /* retained for binary compatibility */
+       FT_Generic        generic;
+
+       FT_Glyph_Metrics  metrics;
+       FT_Fixed          linearHoriAdvance;
+       FT_Fixed          linearVertAdvance;
+       FT_Vector         advance;
+
+       FT_Glyph_Format   format;
+
+       FT_Bitmap         bitmap;
+       FT_Int            bitmap_left;
+       FT_Int            bitmap_top;
+
+       FT_Outline        outline;
+
+       FT_UInt           num_subglyphs;
+       FT_SubGlyph       subglyphs;
+
+       void*             control_data;
+       long              control_len;
+
+       FT_Pos            lsb_delta;
+       FT_Pos            rsb_delta;
+
+       void*             other;
+
+       FT_Slot_Internal  internal;
+} FT_GlyphSlotRec;
+
+#define FT_FACE_FLAG_SCALABLE          ( 1L <<  0 )
+#define FT_FACE_FLAG_FIXED_SIZES       ( 1L <<  1 )
+#define FT_FACE_FLAG_FIXED_WIDTH       ( 1L <<  2 )
+#define FT_FACE_FLAG_SFNT              ( 1L <<  3 )
+#define FT_FACE_FLAG_HORIZONTAL        ( 1L <<  4 )
+#define FT_FACE_FLAG_VERTICAL          ( 1L <<  5 )
+#define FT_FACE_FLAG_KERNING           ( 1L <<  6 )
+#define FT_FACE_FLAG_FAST_GLYPHS       ( 1L <<  7 )
+#define FT_FACE_FLAG_MULTIPLE_MASTERS  ( 1L <<  8 )
+#define FT_FACE_FLAG_GLYPH_NAMES       ( 1L <<  9 )
+#define FT_FACE_FLAG_EXTERNAL_STREAM   ( 1L << 10 )
+#define FT_FACE_FLAG_HINTER            ( 1L << 11 )
+#define FT_FACE_FLAG_CID_KEYED         ( 1L << 12 )
+#define FT_FACE_FLAG_TRICKY            ( 1L << 13 )
+
+typedef enum  FT_Kerning_Mode_
+{
+       FT_KERNING_DEFAULT  = 0,
+       FT_KERNING_UNFITTED,
+       FT_KERNING_UNSCALED
+} FT_Kerning_Mode;
+
+#endif // FT2_DEFS_H_H__
diff --git a/ft2_fontdefs.h b/ft2_fontdefs.h
new file mode 100644 (file)
index 0000000..20726d0
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef FT2_PRIVATE_H__
+#define FT2_PRIVATE_H__
+
+// anything should work, but I recommend multiples of 8
+// since the texture size should be a power of 2
+#define FONT_CHARS_PER_LINE 16
+#define FONT_CHAR_LINES 16
+#define FONT_CHARS_PER_MAP (FONT_CHARS_PER_LINE * FONT_CHAR_LINES)
+
+typedef struct glyph_slot_s
+{
+       qboolean image;
+       // we keep the quad coords here only currently
+       // if you need other info, make Font_LoadMapForIndex fill it into this slot
+       float txmin; // texture coordinate in [0,1]
+       float txmax;
+       float tymin;
+       float tymax;
+       float vxmin;
+       float vxmax;
+       float vymin;
+       float vymax;
+       float advance_x;
+       float advance_y;
+} glyph_slot_t;
+
+struct ft2_font_map_s
+{
+       Uchar                  start;
+       struct ft2_font_map_s *next;
+       float                  size;
+       // the actual size used in the freetype code
+       // by convention, the requested size is the height of the font's bounding box.
+       float                  intSize;
+       int                    glyphSize;
+
+       rtexture_t            *texture;
+       qboolean               static_tex;
+       glyph_slot_t           glyphs[FONT_CHARS_PER_MAP];
+
+       // contains the kerning information for the first 256 characters
+       // for the other characters, we will lookup the kerning information
+       ft2_kerning_t          kerning;
+       // safes us the trouble of calculating these over and over again
+       double                 sfx, sfy;
+};
+
+struct ft2_attachment_s
+{
+       unsigned char *data;
+       fs_offset_t    size;
+};
+
+//qboolean Font_LoadMapForIndex(ft2_font_t *font, Uchar _ch, ft2_font_map_t **outmap);
+qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap);
+
+void font_start(void);
+void font_shutdown(void);
+void font_newmap(void);
+
+#endif // FT2_PRIVATE_H__
diff --git a/utf8lib.c b/utf8lib.c
new file mode 100644 (file)
index 0000000..8b50e75
--- /dev/null
+++ b/utf8lib.c
@@ -0,0 +1,660 @@
+#include "quakedef.h"
+#include "utf8lib.h"
+
+/*
+================================================================================
+Initialization of UTF-8 support and new cvars.
+================================================================================
+*/
+// for compatibility this defaults to 0
+cvar_t    utf8_enable = {CVAR_SAVE, "utf8_enable", "0", "Enable UTF-8 support. For compatibility, this is disabled by default in most games."};
+
+void   u8_Init(void)
+{
+       Cvar_RegisterVariable(&utf8_enable);
+}
+
+/*
+================================================================================
+UTF-8 encoding and decoding functions follow.
+================================================================================
+*/
+
+/** Analyze the next character and return various information if requested.
+ * @param _s      An utf-8 string.
+ * @param _start  Filled with the start byte-offset of the next valid character
+ * @param _len    Fileed with the length of the next valid character
+ * @param _ch     Filled with the unicode value of the next character
+ * @return        Whether or not another valid character is in the string
+ */
+static qboolean u8_analyze(const char *_s, size_t *_start, size_t *_len, Uchar *_ch)
+{
+       const unsigned char *s = (const unsigned char*)_s;
+       unsigned char bt, bc;
+       size_t i;
+       size_t bits, j;
+       Uchar ch;
+
+       i = 0;
+findchar:
+
+       // <0xC2 is always an overlong encoding, they're invalid, thus skipped
+       while (s[i] && s[i] >= 0x80 && s[i] <= 0xC2) {
+               //fprintf(stderr, "skipping\n");
+               ++i;
+       }
+       //fprintf(stderr, "checking\n");
+
+       // If we hit the end, well, we're out and invalid
+       if (!s[i])
+               return false;
+       //fprintf(stderr, "checking ascii\n");
+
+       // ascii characters
+       if (s[i] < 0x80)
+       {
+               if (_start) *_start = i;
+               if (_len) *_len = 1;
+               if (_ch) *_ch = (Uchar)s[i];
+               //fprintf(stderr, "valid ascii\n");
+               return true;
+       }
+       //fprintf(stderr, "checking length\n");
+
+       // Figure out the next char's length
+       bc = s[i];
+       bits = 1;
+       // count the 1 bits, they're the # of bytes
+       for (bt = 0x40; bt && (bc & bt); bt >>= 1, ++bits);
+       if (!bt)
+       {
+               //fprintf(stderr, "superlong\n");
+               ++i;
+               goto findchar;
+       }
+       // turn bt into a mask and give ch a starting value
+       --bt;
+       ch = (s[i] & bt);
+       // check the byte sequence for invalid bytes
+       for (j = 1; j < bits; ++j)
+       {
+               // valid bit value: 10xx xxxx
+               //if (s[i+j] < 0x80 || s[i+j] >= 0xC0)
+               if ( (s[i+j] & 0xC0) != 0x80 )
+               {
+                       //fprintf(stderr, "sequence of %i f'd at %i by %x\n", bits, j, (unsigned int)s[i+j]);
+                       // this byte sequence is invalid, skip it
+                       i += j;
+                       // find a character after it
+                       goto findchar;
+               }
+               // at the same time, decode the character
+               ch = (ch << 6) | (s[i+j] & 0x3F);
+       }
+
+       // Now check the decoded byte for an overlong encoding
+       if ( (bits >= 2 && ch < 0x80) ||
+            (bits >= 3 && ch < 0x800) ||
+            (bits >= 4 && ch < 0x10000) ||
+            ch >= 0x10FFFF // RFC 3629
+               )
+       {
+               i += bits;
+               //fprintf(stderr, "overlong: %i bytes for %x\n", bits, ch);
+               goto findchar;
+       }
+
+       if (_start)
+               *_start = i;
+       if (_len)
+               *_len = bits;
+       if (_ch)
+               *_ch = ch;
+       //fprintf(stderr, "valid utf8\n");
+       return true;
+}
+
+/** Get the number of characters in an UTF-8 string.
+ * @param _s    An utf-8 encoded null-terminated string.
+ * @return      The number of unicode characters in the string.
+ */
+size_t u8_strlen(const char *_s)
+{
+       size_t st, ln;
+       size_t len = 0;
+       const unsigned char *s = (const unsigned char*)_s;
+
+       if (!utf8_enable.integer)
+               return strlen(_s);
+
+       while (*s)
+       {
+               // ascii char, skip u8_analyze
+               if (*s < 0x80)
+               {
+                       ++len;
+                       ++s;
+                       continue;
+               }
+
+               // invalid, skip u8_analyze
+               if (*s <= 0xC2)
+               {
+                       ++s;
+                       continue;
+               }
+
+               if (!u8_analyze((const char*)s, &st, &ln, NULL))
+                       break;
+               // valid character, skip after it
+               s += st + ln;
+               ++len;
+       }
+       return len;
+}
+
+/** Get the number of characters in a part of an UTF-8 string.
+ * @param _s    An utf-8 encoded null-terminated string.
+ * @param n     The maximum number of bytes.
+ * @return      The number of unicode characters in the string.
+ */
+size_t u8_strnlen(const char *_s, size_t n)
+{
+       size_t st, ln;
+       size_t len = 0;
+       const unsigned char *s = (const unsigned char*)_s;
+
+       if (!utf8_enable.integer)
+       {
+               len = strlen(_s);
+               return (len < n) ? len : n;
+       }
+
+       while (*s && n)
+       {
+               // ascii char, skip u8_analyze
+               if (*s < 0x80)
+               {
+                       ++len;
+                       ++s;
+                       --n;
+                       continue;
+               }
+
+               // invalid, skip u8_analyze
+               if (*s <= 0xC2)
+               {
+                       ++s;
+                       --n;
+                       continue;
+               }
+
+               if (!u8_analyze((const char*)s, &st, &ln, NULL))
+                       break;
+               // valid character, see if it's still inside the range specified by n:
+               if (n < st + ln)
+                       return len;
+               ++len;
+               n -= st + ln;
+               s += st + ln;
+       }
+       return len;
+}
+
+/** Get the number of bytes used in a string to represent an amount of characters.
+ * @param _s    An utf-8 encoded null-terminated string.
+ * @param n     The number of characters we want to know the byte-size for.
+ * @return      The number of bytes used to represent n characters.
+ */
+size_t u8_bytelen(const char *_s, size_t n)
+{
+       size_t st, ln;
+       size_t len = 0;
+       const unsigned char *s = (const unsigned char*)_s;
+
+       if (!utf8_enable.integer)
+               return n;
+
+       while (*s && n)
+       {
+               // ascii char, skip u8_analyze
+               if (*s < 0x80)
+               {
+                       ++len;
+                       ++s;
+                       --n;
+                       continue;
+               }
+
+               // invalid, skip u8_analyze
+               if (*s <= 0xC2)
+               {
+                       ++s;
+                       ++len;
+                       continue;
+               }
+
+               if (!u8_analyze((const char*)s, &st, &ln, NULL))
+                       break;
+               --n;
+               s += st + ln;
+               len += st + ln;
+       }
+       return len;
+}
+
+/** Get the byte-index for a character-index.
+ * @param _s      An utf-8 encoded string.
+ * @param i       The character-index for which you want the byte offset.
+ * @param len     If not null, character's length will be stored in there.
+ * @return        The byte-index at which the character begins, or -1 if the string is too short.
+ */
+int u8_byteofs(const char *_s, size_t i, size_t *len)
+{
+       size_t st, ln;
+       size_t ofs = 0;
+       const unsigned char *s = (const unsigned char*)_s;
+
+       if (!utf8_enable.integer)
+       {
+               if (len) *len = 1;
+               return i;
+       }
+
+       st = ln = 0;
+       do
+       {
+               ofs += ln;
+               if (!u8_analyze((const char*)s + ofs, &st, &ln, NULL))
+                       return -1;
+               ofs += st;
+       } while(i-- > 0);
+       if (len)
+               *len = ln;
+       return ofs;
+}
+
+/** Get the char-index for a byte-index.
+ * @param _s      An utf-8 encoded string.
+ * @param i       The byte offset for which you want the character index.
+ * @param len     If not null, the offset within the character is stored here.
+ * @return        The character-index, or -1 if the string is too short.
+ */
+int u8_charidx(const char *_s, size_t i, size_t *len)
+{
+       size_t st, ln;
+       size_t ofs = 0;
+       size_t pofs = 0;
+       int idx = 0;
+       const unsigned char *s = (const unsigned char*)_s;
+
+       if (!utf8_enable.integer)
+       {
+               if (len) *len = 0;
+               return i;
+       }
+
+       while (ofs < i && s[ofs])
+       {
+               // ascii character, skip u8_analyze
+               if (s[ofs] < 0x80)
+               {
+                       pofs = ofs;
+                       ++idx;
+                       ++ofs;
+                       continue;
+               }
+
+               // invalid, skip u8_analyze
+               if (s[ofs] <= 0xC2)
+               {
+                       ++ofs;
+                       continue;
+               }
+
+               if (!u8_analyze((const char*)s+ofs, &st, &ln, NULL))
+                       return -1;
+               // see if next char is after the bytemark
+               if (ofs + st > i)
+               {
+                       if (len)
+                               *len = i - pofs;
+                       return idx;
+               }
+               ++idx;
+               pofs = ofs + st;
+               ofs += st + ln;
+               // see if bytemark is within the char
+               if (ofs > i)
+               {
+                       if (len)
+                               *len = i - pofs;
+                       return idx;
+               }
+       }
+       if (len) *len = 0;
+       return idx;
+}
+
+/** Get the byte offset of the previous byte.
+ * The result equals:
+ * prevchar_pos = u8_byteofs(text, u8_charidx(text, thischar_pos, NULL) - 1, NULL)
+ * @param _s      An utf-8 encoded string.
+ * @param i       The current byte offset.
+ * @return        The byte offset of the previous character
+ */
+size_t u8_prevbyte(const char *_s, size_t i)
+{
+       size_t st, ln;
+       const unsigned char *s = (const unsigned char*)_s;
+       size_t lastofs = 0;
+       size_t ofs = 0;
+
+       if (!utf8_enable.integer)
+       {
+               if (i > 0)
+                       return i-1;
+               return 0;
+       }
+
+       while (ofs < i && s[ofs])
+       {
+               // ascii character, skip u8_analyze
+               if (s[ofs] < 0x80)
+               {
+                       lastofs = ofs++;
+                       continue;
+               }
+
+               // invalid, skip u8_analyze
+               if (s[ofs] <= 0xC2)
+               {
+                       ++ofs;
+                       continue;
+               }
+
+               if (!u8_analyze((const char*)s+ofs, &st, &ln, NULL))
+                       return lastofs;
+               if (ofs + st > i)
+                       return lastofs;
+               if (ofs + st + ln >= i)
+                       return ofs + st;
+
+               lastofs = ofs;
+               ofs += st + ln;
+       }
+       return lastofs;
+}
+
+static int char_usefont[256] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+/** Fetch a character from an utf-8 encoded string.
+ * @param _s      The start of an utf-8 encoded multi-byte character.
+ * @param _end    Will point to after the first multi-byte character.
+ * @return        The 32-bit integer representation of the first multi-byte character or 0 for invalid characters.
+ */
+Uchar u8_getchar(const char *_s, const char **_end)
+{
+       size_t st, ln;
+       Uchar ch;
+
+       if (!utf8_enable.integer)
+       {
+               if (_end)
+                       *_end = _s + 1;
+               /* Careful: if we disable utf8 but not freetype, we wish to see freetype chars
+                * for normal letters. So use E000+x for special chars, but leave the freetype stuff for the
+                * rest:
+                */
+               if (!char_usefont[(unsigned int)*(const unsigned char*)_s])
+                       return 0xE000 + (Uchar)*(const unsigned char*)_s;
+               return (Uchar)*(const unsigned char*)_s;
+       }
+       
+       if (!u8_analyze(_s, &st, &ln, &ch))
+               return 0;
+       if (_end)
+               *_end = _s + st + ln;
+       return ch;
+}
+
+/** Encode a wide-character into utf-8.
+ * @param w        The wide character to encode.
+ * @param to       The target buffer the utf-8 encoded string is stored to.
+ * @param maxlen   The maximum number of bytes that fit into the target buffer.
+ * @return         Number of bytes written to the buffer not including the terminating null.
+ *                 Less or equal to 0 if the buffer is too small.
+ */
+int u8_fromchar(Uchar w, char *to, size_t maxlen)
+{
+       if (maxlen < 1)
+               return -2;
+
+       if (!w)
+               return -5;
+
+       if (w >= 0xE000 && !utf8_enable.integer)
+               w -= 0xE000;
+
+       if (w < 0x80 || !utf8_enable.integer)
+       {
+               to[0] = (char)w;
+               if (maxlen < 2)
+                       return -1;
+               to[1] = 0;
+               return 1;
+       }
+       // for a little speedup
+       if (w < 0x800)
+       {
+               if (maxlen < 3)
+               {
+                       to[0] = 0;
+                       return -1;
+               }
+               to[2] = 0;
+               to[1] = 0x80 | (w & 0x3F); w >>= 6;
+               to[0] = 0xC0 | w;
+               return 2;
+       }
+       if (w < 0x10000)
+       {
+               if (maxlen < 4)
+               {
+                       to[0] = 0;
+                       return -1;
+               }
+               to[3] = 0;
+               to[2] = 0x80 | (w & 0x3F); w >>= 6;
+               to[1] = 0x80 | (w & 0x3F); w >>= 6;
+               to[0] = 0xE0 | w;
+               return 3;
+       }
+
+       // RFC 3629
+       if (w <= 0x10FFFF)
+       {
+               if (maxlen < 5)
+               {
+                       to[0] = 0;
+                       return -1;
+               }
+               to[4] = 0;
+               to[3] = 0x80 | (w & 0x3F); w >>= 6;
+               to[2] = 0x80 | (w & 0x3F); w >>= 6;
+               to[1] = 0x80 | (w & 0x3F); w >>= 6;
+               to[0] = 0xE0 | w;
+               return 4;
+       }
+       return -1;
+}
+
+/** uses u8_fromchar on a static buffer
+ * @param ch        The unicode character to convert to encode
+ * @param l         The number of bytes without the terminating null.
+ * @return          A statically allocated buffer containing the character's utf8 representation, or NULL if it fails.
+ */
+char *u8_encodech(Uchar ch, size_t *l)
+{
+       static char buf[16];
+       size_t len;
+       len = u8_fromchar(ch, buf, sizeof(buf));
+       if (len > 0)
+       {
+               if (l) *l = len;
+               return buf;
+       }
+       return NULL;
+}
+
+/** Convert a utf-8 multibyte string to a wide character string.
+ * @param wcs       The target wide-character buffer.
+ * @param mb        The utf-8 encoded multibyte string to convert.
+ * @param maxlen    The maximum number of wide-characters that fit into the target buffer.
+ * @return          The number of characters written to the target buffer.
+ */
+size_t u8_mbstowcs(Uchar *wcs, const char *mb, size_t maxlen)
+{
+       size_t i;
+       Uchar ch;
+       if (maxlen < 1)
+               return 0;
+       for (i = 0; *mb && i < maxlen-1; ++i)
+       {
+               ch = u8_getchar(mb, &mb);
+               if (!ch)
+                       break;
+               wcs[i] = ch;
+       }
+       wcs[i] = 0;
+       return i;
+}
+
+/** Convert a wide-character string to a utf-8 multibyte string.
+ * @param mb      The target buffer the utf-8 string is written to.
+ * @param wcs     The wide-character string to convert.
+ * @param maxlen  The number bytes that fit into the multibyte target buffer.
+ * @return        The number of bytes written, not including the terminating \0
+ */
+size_t u8_wcstombs(char *mb, const Uchar *wcs, size_t maxlen)
+{
+       size_t i;
+       const char *start = mb;
+       if (maxlen < 2)
+               return 0;
+       for (i = 0; wcs[i] && i < maxlen-1; ++i)
+       {
+               int len;
+               if ( (len = u8_fromchar(wcs[i], mb, maxlen - i)) < 0)
+                       return (mb - start);
+               mb += len;
+       }
+       *mb = 0;
+       return (mb - start);
+}
+
+/*
+============
+UTF-8 aware COM_StringLengthNoColors
+
+calculates the visible width of a color coded string.
+
+*valid is filled with TRUE if the string is a valid colored string (that is, if
+it does not end with an unfinished color code). If it gets filled with FALSE, a
+fix would be adding a STRING_COLOR_TAG at the end of the string.
+
+valid can be set to NULL if the caller doesn't care.
+
+For size_s, specify the maximum number of characters from s to use, or 0 to use
+all characters until the zero terminator.
+============
+*/
+size_t
+COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid);
+size_t
+u8_COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
+{
+       const char *end;
+       size_t len = 0;
+
+       if (!utf8_enable.integer)
+               return COM_StringLengthNoColors(s, size_s, valid);
+
+       end = size_s ? (s + size_s) : NULL;
+
+       for(;;)
+       {
+               switch((s == end) ? 0 : *s)
+               {
+                       case 0:
+                               if(valid)
+                                       *valid = TRUE;
+                               return len;
+                       case STRING_COLOR_TAG:
+                               ++s;
+                               switch((s == end) ? 0 : *s)
+                               {
+                                       case STRING_COLOR_RGB_TAG_CHAR:
+                                               if (s+1 != end && isxdigit(s[1]) &&
+                                                       s+2 != end && isxdigit(s[2]) &&
+                                                       s+3 != end && isxdigit(s[3]) )
+                                               {
+                                                       s+=3;
+                                                       break;
+                                               }
+                                               ++len; // STRING_COLOR_TAG
+                                               ++len; // STRING_COLOR_RGB_TAG_CHAR
+                                               break;
+                                       case 0: // ends with unfinished color code!
+                                               ++len;
+                                               if(valid)
+                                                       *valid = FALSE;
+                                               return len;
+                                       case STRING_COLOR_TAG: // escaped ^
+                                               ++len;
+                                               break;
+                                       case '0': case '1': case '2': case '3': case '4':
+                                       case '5': case '6': case '7': case '8': case '9': // color code
+                                               break;
+                                       default: // not a color code
+                                               ++len; // STRING_COLOR_TAG
+                                               ++len; // the character
+                                               break;
+                               }
+                               break;
+                       default:
+                               ++len;
+                               break;
+               }
+
+               // start of a wide character
+               if (*s & 0xC0)
+               {
+                       for (++s; *s >= 0x80 && *s <= 0xC0; ++s);
+                       continue;
+               }
+               // part of a wide character, we ignore that one
+               if (*s <= 0xBF)
+                       --len;
+               ++s;
+       }
+       // never get here
+}
diff --git a/utf8lib.h b/utf8lib.h
new file mode 100644 (file)
index 0000000..f435bbd
--- /dev/null
+++ b/utf8lib.h
@@ -0,0 +1,46 @@
+/*
+ * UTF-8 utility functions for DarkPlaces
+ */
+#ifndef UTF8LIB_H__
+#define UTF8LIB_H__
+
+#include "qtypes.h"
+
+// types for unicode strings
+// let them be 32 bit for now
+// normally, whcar_t is 16 or 32 bit, 16 on linux I think, 32 on haiku and maybe windows
+#ifdef _MSC_VER
+#include <stdint.h>
+typedef __int32 U_int32;
+#else
+#include <sys/types.h>
+typedef int32_t U_int32;
+#endif
+
+// Uchar, a wide character
+typedef U_int32 Uchar;
+
+// Initialize UTF8, this registers cvars which allows for UTF8 to be disabled
+// completely.
+// When UTF8 is disabled, every u8_ function will work exactly as you'd expect
+// a non-utf8 version to work: u8_strlen() will wrap to strlen()
+// u8_byteofs() and u8_charidx() will simply return whatever is passed as index parameter
+// u8_getchar() will will just return the next byte, u8_fromchar will write one byte, ...
+extern cvar_t    utf8_enable;
+void   u8_Init(void);
+
+size_t u8_strlen(const char*);
+size_t u8_strnlen(const char*, size_t);
+int    u8_byteofs(const char*, size_t, size_t*);
+int    u8_charidx(const char*, size_t, size_t*);
+size_t u8_bytelen(const char*, size_t);
+size_t u8_prevbyte(const char*, size_t);
+Uchar  u8_getchar(const char*, const char**);
+int    u8_fromchar(Uchar, char*, size_t);
+size_t u8_wcstombs(char*, const Uchar*, size_t);
+size_t u8_COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid);
+
+// returns a static buffer, use this for inlining
+char  *u8_encodech(Uchar ch, size_t*);
+
+#endif // UTF8LIB_H__