--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__
--- /dev/null
+/* 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__
--- /dev/null
+#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__
--- /dev/null
+#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
+}
--- /dev/null
+/*
+ * 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__