Adding FreeType2 and UTF-8 Support.
authorblub <blub@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 23 Dec 2009 10:43:52 +0000 (10:43 +0000)
committerblub <blub@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 23 Dec 2009 10:43:52 +0000 (10:43 +0000)
UTF-8 is disabled by default, FreeType is enabled.
new cvars: utf8_enable (0)
           r_font_disable_freetype (1)
   r_font_use_alpha_textures (0, not really finished yet)
   r_font_size_snapping (1 - 0 looks bad when vid_conwidth/height and vid_width/height are too different)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9641 d7cf8633-e32d-0410-b094-e92efae38249

14 files changed:
common.c
console.c
draw.h
gl_draw.c
gl_rmain.c
gl_textures.c
glquake.h
host.c
host_cmd.c
keys.c
makefile.inc
prvm_cmds.c
r_textures.h
vid_sdl.c

index c002be8..70f36e7 100644 (file)
--- a/common.c
+++ b/common.c
@@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // common.c -- misc functions used in client and server
 
 #include "quakedef.h"
+#include "utf8lib.h"
 
 #include <stdlib.h>
 #include <fcntl.h>
@@ -696,6 +697,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
        int result = 0;
        size_t wordLen;
        size_t dummy;
+       size_t wordChars;
 
        dummy = 0;
        wordWidth(passthroughCW, NULL, &dummy, -1);
@@ -708,12 +710,12 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                switch(ch)
                {
                        case 0: // end of string
-                               result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
+                               result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                goto out;
                                break;
                        case '\n': // end of line
-                               result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
+                               result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                ++cursor;
                                startOfLine = cursor;
@@ -738,8 +740,9 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                        }
                                }
                                out_inner:
-                               spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
-                               if(wordLen < 1)
+                               wordChars = strnlen(cursor, wordLen);
+                               spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordChars, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
+                               if(wordChars < 1)
                                {
                                        wordLen = 1;
                                        spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
@@ -753,7 +756,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                else
                                {
                                        // output current line
-                                       result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
+                                       result += processLine(passthroughPL, startOfLine, u8_strnlen(startOfLine, cursor - startOfLine), spaceUsedInLine, isContinuation);
                                        isContinuation = true;
                                        startOfLine = cursor;
                                        cursor += wordLen;
index 71a5f86..6a59b97 100644 (file)
--- a/console.c
+++ b/console.c
@@ -26,6 +26,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #endif
 #include <time.h>
 
+// for u8_encodech
+#include "ft2.h"
+
 float con_cursorspeed = 4;
 
 // lines up from bottom to display
@@ -1363,7 +1366,24 @@ void Con_DrawInput (void)
 
        // add the cursor frame
        if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
-               text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
+       {
+               if (!utf8_enable.integer)
+                       text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
+               else if (y + 3 < (int)sizeof(editlinecopy)-1)
+               {
+                       int ofs = u8_bytelen(text + key_linepos, 1);
+                       size_t len;
+                       const char *curbuf;
+                       curbuf = u8_encodech(0xE000 + 11 + 130 * key_insert, &len);
+
+                       if (curbuf)
+                       {
+                               memmove(text + key_linepos + len, text + key_linepos + ofs, sizeof(editlinecopy) - key_linepos - len);
+                               memcpy(text + key_linepos, curbuf, len);
+                       }
+               } else
+                       text[key_linepos] = '-' + ('+' - '-') * key_insert;
+       }
 
 //     text[key_linepos + 1] = 0;
 
@@ -1402,10 +1422,16 @@ float Con_WordWidthFunc(void *passthrough, const char *w, size_t *length, float
                ti->colorindex = -1;
                return ti->fontsize * ti->font->maxwidth;
        }
+       /*
        if(maxWidth >= 0)
                return DrawQ_TextWidth_Font_UntilWidth(w, length, false, ti->font, maxWidth / ti->fontsize) * ti->fontsize;
        else if(maxWidth == -1)
                return DrawQ_TextWidth_Font(w, *length, false, ti->font) * ti->fontsize;
+       */
+       if(maxWidth >= 0)
+               return DrawQ_TextWidth_Font_UntilWidth_Size(w, ti->fontsize, ti->fontsize, length, false, ti->font, maxWidth);
+       else if(maxWidth == -1)
+               return DrawQ_TextWidth_Font_Size(w, ti->fontsize, ti->fontsize, *length, false, ti->font);
        else
        {
                printf("Con_WordWidthFunc: can't get here (maxWidth should never be %f)\n", maxWidth);
@@ -1590,20 +1616,23 @@ void Con_DrawNotify (void)
        if(numChatlines)
        {
                v = chatstart + numChatlines * con_chatsize.value;
-               Con_DrawNotifyRect(CON_MASK_CHAT, CON_MASK_INPUT, con_chattime.value, 0, chatstart, vid_conwidth.value * con_chatwidth.value, v - chatstart, con_chatsize.value, 0.0, 1.0, "^3\014\014\014 "); // 015 is ·> character in conchars.tga
+               Con_DrawNotifyRect(CON_MASK_CHAT, CON_MASK_INPUT, con_chattime.value, 0, chatstart, vid_conwidth.value * con_chatwidth.value, v - chatstart, con_chatsize.value, 0.0, 1.0, /*"^3\014\014\014 "*/ "^3\xee\x80\x8d\xee\x80\x8d\xee\x80\x8d "); // 015 is ·> character in conchars.tga
        }
 
        if (key_dest == key_message)
        {
+               //static char *cursor[2] = { "\xee\x80\x8a", "\xee\x80\x8b" }; // { off, on }
                int colorindex = -1;
+               const char *cursor;
+               cursor = u8_encodech(0xE00A + ((int)(realtime * con_cursorspeed)&1), NULL);
 
                // LordHavoc: speedup, and other improvements
                if (chat_mode < 0)
-                       dpsnprintf(temptext, sizeof(temptext), "]%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
+                       dpsnprintf(temptext, sizeof(temptext), "]%s%s", chat_buffer, cursor);
                else if(chat_mode)
-                       dpsnprintf(temptext, sizeof(temptext), "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
+                       dpsnprintf(temptext, sizeof(temptext), "say_team:%s%s", chat_buffer, cursor);
                else
-                       dpsnprintf(temptext, sizeof(temptext), "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
+                       dpsnprintf(temptext, sizeof(temptext), "say:%s%s", chat_buffer, cursor);
 
                // FIXME word wrap
                inputsize = (numChatlines ? con_chatsize : con_notifysize).value;
diff --git a/draw.h b/draw.h
index e365126..114f90c 100644 (file)
--- a/draw.h
+++ b/draw.h
@@ -90,6 +90,8 @@ DRAWFLAG_MASK = 0xFF,   // ONLY R_BeginPolygon()
 DRAWFLAG_MIPMAP = 0x100 // ONLY R_BeginPolygon()
 };
 
+#define MAX_FONT_SIZES 8
+#define MAX_FONT_FALLBACKS 3
 typedef struct dp_font_s
 {
        rtexture_t *tex;
@@ -98,6 +100,12 @@ typedef struct dp_font_s
        float scale; // scales the font (without changing line height!)
        char texpath[MAX_QPATH];
        char title[MAX_QPATH];
+
+       int req_face; // requested face index, usually 0
+       float req_sizes[MAX_FONT_SIZES]; // sizes to render the font with, 0 still defaults to 16 (backward compatibility when loadfont doesn't get a size parameter) and -1 = disabled
+       char fallbacks[MAX_FONT_FALLBACKS][MAX_QPATH];
+       int fallback_faces[MAX_FONT_FALLBACKS];
+       struct ft2_font_s *ft2;
 }
 dp_font_t;
 
@@ -137,9 +145,13 @@ void DrawQ_Fill(float x, float y, float width, float height, float red, float gr
 // if r_textshadow is not zero, an additional instance of the text is drawn first at an offset with an inverted shade of gray (black text produces a white shadow, brightly colored text produces a black shadow)
 float DrawQ_String(float x, float y, const char *text, size_t maxlen, float scalex, float scaley, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes);
 float DrawQ_String_Font(float x, float y, const char *text, size_t maxlen, float scalex, float scaley, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt);
-float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt);
-float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth);
-float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth);
+// you are STRONGLY DISCOURAGED to use a version without the _Size suffix!!!
+/* don't use: */float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt);
+/* use this:  */float DrawQ_TextWidth_Font_Size(const char *text, float w, float h, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt);
+/* don't use: */float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth);
+/* use this:  */float DrawQ_TextWidth_Font_UntilWidth_Size(const char *text, float w, float h, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth);
+/* don't use: */float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth);
+/* use this:  */float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w, float h, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth);
 // draw a very fancy pic (per corner texcoord/color control), the order is tl, tr, bl, br
 void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags);
 // draw a triangle mesh
index 691a7b2..66b2b89 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -25,6 +25,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "cl_video.h"
 #include "cl_dyntexture.h"
 
+#include "ft2.h"
+#include "ft2_fontdefs.h"
+
 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
 
 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
@@ -544,6 +547,20 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
        if(drawtexturepool == NULL)
                return; // before gl_draw_start, so will be loaded later
 
+       if(fnt->ft2)
+       {
+               // clear freetype font
+               Font_UnloadFont(fnt->ft2);
+               Mem_Free(fnt->ft2);
+               fnt->ft2 = NULL;
+       }
+
+       if(fnt->req_face != -1)
+       {
+               if(!Font_LoadFont(fnt->texpath, fnt))
+                       Con_Printf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
+       }
+
        fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
        if(fnt->tex == r_texture_notexture)
        {
@@ -635,15 +652,54 @@ static dp_font_t *FindFont(const char *title)
        return NULL;
 }
 
+static inline float snap_to_pixel_x(float x, float roundUpAt)
+{
+       float pixelpos = x * vid.width / vid_conwidth.value;
+       int snap = (int) pixelpos;
+       if (pixelpos - snap >= roundUpAt) ++snap;
+       return ((float)snap * vid_conwidth.value / vid.width);
+       /*
+       x = (int)(x * vid.width / vid_conwidth.value);
+       x = (x * vid_conwidth.value / vid.width);
+       return x;
+       */
+}
+
+static inline float snap_to_pixel_y(float y, float roundUpAt)
+{
+       float pixelpos = y * vid.height / vid_conheight.value;
+       int snap = (int) pixelpos;
+       if (pixelpos - snap > roundUpAt) ++snap;
+       return ((float)snap * vid_conheight.value / vid.height);
+       /*
+       y = (int)(y * vid.height / vid_conheight.value);
+       y = (y * vid_conheight.value / vid.height);
+       return y;
+       */
+}
+
 static void LoadFont_f(void)
 {
        dp_font_t *f;
-       int i;
+       int i, si;
+       float sz, sn;
+       const char *filelist, *c, *cm;
+       char mainfont[MAX_QPATH];
+
        if(Cmd_Argc() < 2)
        {
                Con_Printf("Available font commands:\n");
                for(i = 0; i < MAX_FONTS; ++i)
-                       Con_Printf("  loadfont %s gfx/tgafile\n", dp_fonts[i].title);
+                       Con_Printf("  loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title);
+               Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
+                          "can specify multiple fonts and faces\n"
+                          "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
+                          "to load face 2 of the font gfx/vera-sans and use face 1\n"
+                          "of gfx/fallback as fallback font.\n"
+                          "You can also specify a list of font sizes to load, like this:\n"
+                          "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
+                          "In many cases, 8 12 16 24 32 should be a good choice.\n"
+                       );
                return;
        }
        f = FindFont(Cmd_Argv(1));
@@ -652,7 +708,93 @@ static void LoadFont_f(void)
                Con_Printf("font function not found\n");
                return;
        }
-       LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
+
+       if(Cmd_Argc() < 3)
+               filelist = "gfx/conchars";
+       else
+               filelist = Cmd_Argv(2);
+
+       memset(f->fallbacks, 0, sizeof(f->fallbacks));
+       memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
+
+       // first font is handled "normally"
+       c = strchr(filelist, ':');
+       cm = strchr(filelist, ',');
+       if(c && (!cm || c < cm))
+               f->req_face = atoi(c+1);
+       else
+       {
+               f->req_face = 0;
+               c = cm;
+       }
+
+       if(!c || (c - filelist) > MAX_QPATH)
+               strlcpy(mainfont, filelist, sizeof(mainfont));
+       else
+       {
+               memcpy(mainfont, filelist, c - filelist);
+               mainfont[c - filelist] = 0;
+       }
+
+       for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
+       {
+               c = strchr(filelist, ',');
+               if(!c)
+                       break;
+               filelist = c + 1;
+               if(!*filelist)
+                       break;
+               c = strchr(filelist, ':');
+               cm = strchr(filelist, ',');
+               if(c && (!cm || c < cm))
+                       f->fallback_faces[i] = atoi(c+1);
+               else
+               {
+                       f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
+                       c = cm;
+               }
+               if(!c || (c-filelist) > MAX_QPATH)
+               {
+                       strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
+               }
+               else
+               {
+                       memcpy(f->fallbacks[i], filelist, c - filelist);
+                       f->fallbacks[i][c - filelist] = 0;
+               }
+       }
+
+       // for now: by default load only one size: the default size
+       f->req_sizes[0] = 0;
+       for(i = 1; i < MAX_FONT_SIZES; ++i)
+               f->req_sizes[i] = -1;
+
+       // for some reason this argc is 3 even when using 2 arguments here, maybe nexuiz screws up
+       if(Cmd_Argc() >= 3)
+       {
+               for(i = 0; i < Cmd_Argc()-3; ++i)
+               {
+                       sz = atof(Cmd_Argv(i+3));
+                       if (IS_NAN(sz)) // do not use crap sizes
+                               continue;
+                       // now try to scale to our actual size:
+                       if (vid.width > 0)
+                               sn = snap_to_pixel_y(sz, 0.5);
+                       else
+                       {
+                               sn = sz * vid_height.value / vid_conheight.value;
+                               si = (int)sn;
+                               if ( sn - (float)si >= 0.5 )
+                                       ++si;
+                               sn = si * vid_conheight.value / vid_height.value;
+                       }
+                       if (!IS_NAN(sn))
+                               f->req_sizes[i] = sn;
+                       else
+                               f->req_sizes[i] = sz;
+               }
+       }
+       LoadFont(true, mainfont, f);
 }
 
 /*
@@ -668,6 +810,8 @@ static void gl_draw_start(void)
        numcachepics = 0;
        memset(cachepichash, 0, sizeof(cachepichash));
 
+       font_start();
+
        for(i = 0; i < MAX_FONTS; ++i)
                LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
 
@@ -677,6 +821,8 @@ static void gl_draw_start(void)
 
 static void gl_draw_shutdown(void)
 {
+       font_shutdown();
+
        R_FreeTexturePool(&drawtexturepool);
 
        numcachepics = 0;
@@ -685,6 +831,7 @@ static void gl_draw_shutdown(void)
 
 static void gl_draw_newmap(void)
 {
+       font_newmap();
 }
 
 void GL_Draw_Init (void)
@@ -708,6 +855,7 @@ void GL_Draw_Init (void)
        for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
                if(!FONT_USER[i].title[0])
                        dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
+       Font_Init();
 }
 
 void _DrawQ_Setup(void)
@@ -925,13 +1073,41 @@ static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g,
        }
 }
 
-float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
+float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w, float h, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
 {
-       int num, colorindex = STRING_COLOR_DEFAULT;
+       int colorindex = STRING_COLOR_DEFAULT;
        size_t i;
        float x = 0;
-       char ch;
+       Uchar ch, mapch, nextch;
+       Uchar prevch = 0; // used for kerning
        int tempcolorindex;
+       float kx;
+       int map_index = 0;
+       ft2_font_map_t *fontmap = NULL;
+       ft2_font_map_t *map = NULL;
+       ft2_font_map_t *prevmap = NULL;
+       ft2_font_t *ft2 = fnt->ft2;
+       // float ftbase_x;
+       qboolean snap = true;
+
+       if (!h) h = w;
+       if (!h) {
+               w = h = 1;
+               snap = false;
+       }
+       // do this in the end
+       w *= fnt->scale;
+       h *= fnt->scale;
+
+       // find the most fitting size:
+       if (ft2 != NULL)
+       {
+               if (snap)
+                       map_index = Font_IndexForSize(ft2, h, &w, &h);
+               else
+                       map_index = Font_IndexForSize(ft2, h, NULL, NULL);
+               fontmap = Font_MapForIndex(ft2, map_index);
+       }
 
        if (*maxlen < 1)
                *maxlen = 1<<30;
@@ -941,42 +1117,53 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl
        else
                colorindex = *outcolor;
 
-       maxwidth /= fnt->scale;
+       // maxwidth /= fnt->scale; // w and h are multiplied by it already
+       // ftbase_x = snap_to_pixel_x(0);
 
-       for (i = 0;i < *maxlen && text[i];i++)
+       for (i = 0;i < *maxlen && *text;)
        {
-               if (text[i] == ' ')
+               nextch = ch = u8_getchar(text, &text);
+               //i = text - text_start;
+               if (!ch)
+                       break;
+               if (snap)
+                       x = snap_to_pixel_x(x, 0.4);
+               if (ch == ' ' && !fontmap)
                {
-                       if(x + fnt->width_of[(int) ' '] > maxwidth)
+                       if(x + fnt->width_of[(int) ' '] * w > maxwidth)
                                break; // oops, can't draw this
-                       x += fnt->width_of[(int) ' '];
+                       x += fnt->width_of[(int) ' '] * w;
+                       ++i;
                        continue;
                }
-               if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
+               if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
                {
-                       ch = text[++i];
-            if (ch <= '9' && ch >= '0') // ^[0-9] found
+                       ++i;
+                       ch = *text; // colors are ascii, so no u8_ needed
+                       if (ch <= '9' && ch >= '0') // ^[0-9] found
                        {
                                colorindex = ch - '0';
-                continue;
+                               ++text;
+                               ++i;
+                               continue;
                        }
                        else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
                        {
                                // building colorindex...
-                               ch = tolower(text[i+1]);
+                               ch = tolower(text[1]);
                                tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
                                if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
                                else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
                                else tempcolorindex = 0;
                                if (tempcolorindex)
                                {
-                                       ch = tolower(text[i+2]);
+                                       ch = tolower(text[2]);
                                        if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
                                        else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
                                        else tempcolorindex = 0;
                                        if (tempcolorindex)
                                        {
-                                               ch = tolower(text[i+3]);
+                                               ch = tolower(text[3]);
                                                if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
                                                else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
                                                else tempcolorindex = 0;
@@ -984,20 +1171,54 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl
                                                {
                                                        colorindex = tempcolorindex | 0xf;
                                                        // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
-                                                       i+=3;
+                                                       i+=4;
+                                                       text += 4;
                                                        continue;
                                                }
                                        }
                                }
                        }
                        else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
+                       {
                                i++;
+                               text++;
+                       }
                        i--;
                }
-               num = (unsigned char) text[i];
-               if(x + fnt->width_of[num] > maxwidth)
-                       break; // oops, can't draw this
-               x += fnt->width_of[num];
+               ch = nextch;
+               ++i;
+
+               if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
+               {
+                       if (ch > 0xE000)
+                               ch -= 0xE000;
+                       if (ch > 0xFF)
+                               continue;
+                       if (fontmap)
+                               map = ft2_oldstyle_map;
+                       prevch = 0;
+                       if(x + fnt->width_of[ch] * w > maxwidth)
+                               break; // oops, can't draw this
+                       x += fnt->width_of[ch] * w;
+               } else {
+                       if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
+                       {
+                               map = FontMap_FindForChar(fontmap, ch);
+                               if (!map)
+                               {
+                                       if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
+                                               break;
+                                       if (!map)
+                                               break;
+                               }
+                       }
+                       mapch = ch - map->start;
+                       if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
+                               x += kx * w;
+                       x += map->glyphs[mapch].advance_x * w;
+                       prevmap = map;
+                       prevch = ch;
+               }
        }
 
        *maxlen = i;
@@ -1005,12 +1226,12 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl
        if (outcolor)
                *outcolor = colorindex;
 
-       return x * fnt->scale;
+       return x;
 }
 
 float DrawQ_String_Font(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
 {
-       int num, shadow, colorindex = STRING_COLOR_DEFAULT;
+       int shadow, colorindex = STRING_COLOR_DEFAULT;
        size_t i;
        float x = startx, y, s, t, u, v, thisw;
        float *av, *at, *ac;
@@ -1019,17 +1240,47 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max
        static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
        static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
        static float color4f[QUADELEMENTS_MAXQUADS*4*4];
-       int ch;
+       Uchar ch, mapch, nextch;
+       Uchar prevch = 0; // used for kerning
        int tempcolorindex;
+       int map_index = 0;
+       ft2_font_map_t *prevmap = NULL; // the previous map
+       ft2_font_map_t *map = NULL;     // the currently used map
+       ft2_font_map_t *fontmap = NULL; // the font map for the size
+       float ftbase_y;
+       const char *text_start = text;
+       float kx, ky;
+       ft2_font_t *ft2 = fnt->ft2;
+       qboolean snap = true;
+       float pix_x, pix_y;
 
        int tw, th;
        tw = R_TextureWidth(fnt->tex);
        th = R_TextureHeight(fnt->tex);
 
+       if (!h) h = w;
+       if (!h) {
+               h = w = 1;
+               snap = false;
+       }
+
        starty -= (fnt->scale - 1) * h * 0.5; // center
        w *= fnt->scale;
        h *= fnt->scale;
 
+       if (ft2 != NULL)
+       {
+               if (snap)
+                       map_index = Font_IndexForSize(ft2, h, &w, &h);
+               else
+                       map_index = Font_IndexForSize(ft2, h, NULL, NULL);
+               fontmap = Font_MapForIndex(ft2, map_index);
+       }
+
+       // draw the font at its baseline when using freetype
+       //ftbase_x = 0;
+       ftbase_y = h * (4.5/6.0);
+
        if (maxlen < 1)
                maxlen = 1<<30;
 
@@ -1037,6 +1288,8 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max
 
        R_Mesh_ColorPointer(color4f, 0, 0);
        R_Mesh_ResetTextureState();
+       if (!fontmap)
+               R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
        R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
        R_Mesh_VertexPointer(vertex3f, 0, 0);
        R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
@@ -1046,8 +1299,15 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max
        av = vertex3f;
        batchcount = 0;
 
+       //ftbase_x = snap_to_pixel_x(ftbase_x);
+       ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
+
+       pix_x = vid.width / vid_conwidth.value;
+       pix_y = vid.height / vid_conheight.value;
        for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
        {
+               text = text_start;
+
                if (!outcolor || *outcolor == -1)
                        colorindex = STRING_COLOR_DEFAULT;
                else
@@ -1057,44 +1317,59 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max
 
                x = startx;
                y = starty;
+               /*
                if (shadow)
                {
-                       x += r_textshadow.value;
-                       y += r_textshadow.value;
+                       x += r_textshadow.value * vid.width / vid_conwidth.value;
+                       y += r_textshadow.value * vid.height / vid_conheight.value;
                }
-               for (i = 0;i < maxlen && text[i];i++)
+               */
+               for (i = 0;i < maxlen && *text;)
                {
-                       if (text[i] == ' ')
+                       nextch = ch = u8_getchar(text, &text);
+                       //i = text - text_start;
+                       if (!ch)
+                               break;
+                       if (snap)
+                       {
+                               x = snap_to_pixel_x(x, 0.4);
+                               y = snap_to_pixel_y(y, 0.4);
+                       }
+                       if (ch == ' ' && !fontmap)
                        {
                                x += fnt->width_of[(int) ' '] * w;
+                               ++i;
                                continue;
                        }
-                       if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
+                       if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
                        {
-                               ch = text[++i];
+                               ++i;
+                               ch = *text; // colors are ascii, so no u8_ needed
                                if (ch <= '9' && ch >= '0') // ^[0-9] found
                                {
                                        colorindex = ch - '0';
                                        DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
+                                       ++text;
+                                       ++i;
                                        continue;
                                }
                                else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
                                {
                                        // building colorindex...
-                                       ch = tolower(text[i+1]);
+                                       ch = tolower(text[1]);
                                        tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
                                        if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
                                        else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
                                        else tempcolorindex = 0;
                                        if (tempcolorindex)
                                        {
-                                               ch = tolower(text[i+2]);
+                                               ch = tolower(text[2]);
                                                if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
                                                else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
                                                else tempcolorindex = 0;
                                                if (tempcolorindex)
                                                {
-                                                       ch = tolower(text[i+3]);
+                                                       ch = tolower(text[3]);
                                                        if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
                                                        else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
                                                        else tempcolorindex = 0;
@@ -1104,50 +1379,177 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max
                                                                // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
                                                                //Con_Printf("^1colorindex:^7 %x\n", colorindex);
                                                                DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
-                                                               i+=3;
+                                                               i+=4;
+                                                               text+=4;
                                                                continue;
                                                        }
                                                }
                                        }
                                }
                                else if (ch == STRING_COLOR_TAG)
+                               {
                                        i++;
+                                       text++;
+                               }
                                i--;
                        }
-                       num = (unsigned char) text[i];
-                       thisw = fnt->width_of[num];
-                       // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
-                       s = (num & 15)*0.0625f + (0.5f / tw);
-                       t = (num >> 4)*0.0625f + (0.5f / th);
-                       u = 0.0625f * thisw - (1.0f / tw);
-                       v = 0.0625f - (1.0f / th);
-                       ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
-                       ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
-                       ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
-                       ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
-                       at[ 0] = s              ; at[ 1] = t    ;
-                       at[ 2] = s+u    ; at[ 3] = t    ;
-                       at[ 4] = s+u    ; at[ 5] = t+v  ;
-                       at[ 6] = s              ; at[ 7] = t+v  ;
-                       av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
-                       av[ 3] = x+w*thisw      ; av[ 4] = y    ; av[ 5] = 10;
-                       av[ 6] = x+w*thisw      ; av[ 7] = y+h  ; av[ 8] = 10;
-                       av[ 9] = x                      ; av[10] = y+h  ; av[11] = 10;
-                       ac += 16;
-                       at += 8;
-                       av += 12;
-                       batchcount++;
-                       if (batchcount >= QUADELEMENTS_MAXQUADS)
+                       // get the backup
+                       ch = nextch;
+                       ++i;
+                       // using a value of -1 for the oldstyle map because NULL means uninitialized...
+                       // this way we don't need to rebind fnt->tex for every old-style character
+                       // E000..E0FF: emulate old-font characters (to still have smileys and such available)
+                       if (shadow)
                        {
-                               GL_LockArrays(0, batchcount * 4);
-                               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
-                               GL_LockArrays(0, 0);
-                               batchcount = 0;
-                               ac = color4f;
-                               at = texcoord2f;
-                               av = vertex3f;
+                               x += pix_x * r_textshadow.value;
+                               y += pix_y * r_textshadow.value;
+                       }
+                       if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
+                       {
+                               if (ch > 0xE000)
+                                       ch -= 0xE000;
+                               if (ch > 0xFF)
+                                       continue;
+                               if (fontmap)
+                               {
+                                       if (map != ft2_oldstyle_map)
+                                       {
+                                               if (batchcount)
+                                               {
+                                                       // switching from freetype to non-freetype rendering
+                                                       GL_LockArrays(0, batchcount * 4);
+                                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                                       GL_LockArrays(0, 0);
+                                                       batchcount = 0;
+                                                       ac = color4f;
+                                                       at = texcoord2f;
+                                                       av = vertex3f;
+                                               }
+                                               R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
+                                               map = ft2_oldstyle_map;
+                                       }
+                               }
+                               prevch = 0;
+                               //num = (unsigned char) text[i];
+                               //thisw = fnt->width_of[num];
+                               thisw = fnt->width_of[ch];
+                               // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
+                               s = (ch & 15)*0.0625f + (0.5f / tw);
+                               t = (ch >> 4)*0.0625f + (0.5f / th);
+                               u = 0.0625f * thisw - (1.0f / tw);
+                               v = 0.0625f - (1.0f / th);
+                               ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
+                               ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
+                               ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
+                               ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
+                               at[ 0] = s              ; at[ 1] = t    ;
+                               at[ 2] = s+u    ; at[ 3] = t    ;
+                               at[ 4] = s+u    ; at[ 5] = t+v  ;
+                               at[ 6] = s              ; at[ 7] = t+v  ;
+                               av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
+                               av[ 3] = x+w*thisw      ; av[ 4] = y    ; av[ 5] = 10;
+                               av[ 6] = x+w*thisw      ; av[ 7] = y+h  ; av[ 8] = 10;
+                               av[ 9] = x                      ; av[10] = y+h  ; av[11] = 10;
+                               ac += 16;
+                               at += 8;
+                               av += 12;
+                               batchcount++;
+                               if (batchcount >= QUADELEMENTS_MAXQUADS)
+                               {
+                                       GL_LockArrays(0, batchcount * 4);
+                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                       GL_LockArrays(0, 0);
+                                       batchcount = 0;
+                                       ac = color4f;
+                                       at = texcoord2f;
+                                       av = vertex3f;
+                               }
+                               x += thisw * w;
+                       } else {
+                               if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
+                               {
+                                       // new charmap - need to render
+                                       if (batchcount)
+                                       {
+                                               // we need a different character map, render what we currently have:
+                                               GL_LockArrays(0, batchcount * 4);
+                                               R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                               GL_LockArrays(0, 0);
+                                               batchcount = 0;
+                                               ac = color4f;
+                                               at = texcoord2f;
+                                               av = vertex3f;
+                                       }
+                                       // find the new map
+                                       map = FontMap_FindForChar(fontmap, ch);
+                                       if (!map)
+                                       {
+                                               if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
+                                               {
+                                                       shadow = -1;
+                                                       break;
+                                               }
+                                               if (!map)
+                                               {
+                                                       // this shouldn't happen
+                                                       shadow = -1;
+                                                       break;
+                                               }
+                                       }
+                                       R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
+                               }
+
+                               mapch = ch - map->start;
+                               thisw = map->glyphs[mapch].advance_x;
+
+                               //x += ftbase_x;
+                               y += ftbase_y;
+                               if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
+                               {
+                                       x += kx * w;
+                                       y += ky * h;
+                               }
+                               else
+                                       kx = ky = 0;
+                               ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
+                               ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
+                               ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
+                               ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
+                               at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
+                               at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
+                               at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
+                               at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
+                               av[ 0] = x + w * map->glyphs[mapch].vxmin; av[ 1] = y + h * map->glyphs[mapch].vymin; av[ 2] = 10;
+                               av[ 3] = x + w * map->glyphs[mapch].vxmax; av[ 4] = y + h * map->glyphs[mapch].vymin; av[ 5] = 10;
+                               av[ 6] = x + w * map->glyphs[mapch].vxmax; av[ 7] = y + h * map->glyphs[mapch].vymax; av[ 8] = 10;
+                               av[ 9] = x + w * map->glyphs[mapch].vxmin; av[10] = y + h * map->glyphs[mapch].vymax; av[11] = 10;
+                               //x -= ftbase_x;
+                               y -= ftbase_y;
+
+                               x += thisw * w;
+                               ac += 16;
+                               at += 8;
+                               av += 12;
+                               batchcount++;
+                               if (batchcount >= QUADELEMENTS_MAXQUADS)
+                               {
+                                       GL_LockArrays(0, batchcount * 4);
+                                       R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
+                                       GL_LockArrays(0, 0);
+                                       batchcount = 0;
+                                       ac = color4f;
+                                       at = texcoord2f;
+                                       av = vertex3f;
+                               }
+
+                               prevmap = map;
+                               prevch = ch;
+                       }
+                       if (shadow)
+                       {
+                               x -= pix_x * r_textshadow.value;
+                               y -= pix_y * r_textshadow.value;
                        }
-                       x += thisw * w;
                }
        }
        if (batchcount > 0)
@@ -1174,11 +1576,26 @@ float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolor
        return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
 }
 
+float DrawQ_TextWidth_Font_Size(const char *text, float w, float h, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
+{
+       return DrawQ_TextWidth_Font_UntilWidth_Size(text, w, h, &maxlen, ignorecolorcodes, fnt, 1000000000);
+}
+
 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
 {
        return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
 }
 
+float DrawQ_TextWidth_Font_UntilWidth_Size(const char *text, float w, float h, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
+{
+       return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, w, h, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
+}
+
+float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
+{
+       return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, 0, 0, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth);
+}
+
 #if 0
 // not used
 // no ^xrgb management
index d45a0ee..0e3266f 100644 (file)
@@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "r_shadow.h"
 #include "polygon.h"
 #include "image.h"
+#include "ft2.h"
 
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
@@ -4191,12 +4192,14 @@ extern void gl_backend_init(void);
 extern void Sbar_Init(void);
 extern void R_LightningBeams_Init(void);
 extern void Mod_RenderInit(void);
+extern void Font_Init(void);
 
 void Render_Init(void)
 {
        gl_backend_init();
        R_Textures_Init();
        GL_Main_Init();
+       Font_Init();
        GL_Draw_Init();
        R_Shadow_Init();
        R_Sky_Init();
index 2507472..249ced5 100644 (file)
@@ -63,6 +63,8 @@ static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA   , 4, 4, 0.
 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA   , 4, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
+static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA  , 1, 4, 4.0f, GL_ALPHA  , 4, GL_UNSIGNED_BYTE};
+static textypeinfo_t textype_alpha_compress         = {TEXTYPE_ALPHA  , 1, 4, 1.0f, GL_ALPHA  , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
 
 typedef enum gltexturetype_e
 {
@@ -173,6 +175,8 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
                                return &textype_rgba_alpha_compress;
                        case TEXTYPE_BGRA:
                                return &textype_bgra_alpha_compress;
+                       case TEXTYPE_ALPHA:
+                               return &textype_alpha_compress;
                        default:
                                Host_Error("R_GetTexTypeInfo: unknown texture format");
                                return NULL;
@@ -188,6 +192,8 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
                                return &textype_rgba_compress;
                        case TEXTYPE_BGRA:
                                return &textype_bgra_compress;
+                       case TEXTYPE_ALPHA:
+                               return &textype_alpha_compress;
                        default:
                                Host_Error("R_GetTexTypeInfo: unknown texture format");
                                return NULL;
@@ -206,6 +212,8 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
                                return &textype_rgba_alpha;
                        case TEXTYPE_BGRA:
                                return &textype_bgra_alpha;
+                       case TEXTYPE_ALPHA:
+                               return &textype_alpha;
                        default:
                                Host_Error("R_GetTexTypeInfo: unknown texture format");
                                return NULL;
@@ -223,6 +231,8 @@ static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
                                return &textype_bgra;
                        case TEXTYPE_SHADOWMAP:
                                return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
+                       case TEXTYPE_ALPHA:
+                               return &textype_alpha;
                        default:
                                Host_Error("R_GetTexTypeInfo: unknown texture format");
                                return NULL;
@@ -1029,6 +1039,9 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                break;
        case TEXTYPE_SHADOWMAP:
                break;
+       case TEXTYPE_ALPHA:
+               flags |= TEXF_ALPHA;
+               break;
        default:
                Host_Error("R_LoadTexture: unknown texture type");
        }
index d657de4..b8a2243 100644 (file)
--- a/glquake.h
+++ b/glquake.h
@@ -204,6 +204,7 @@ typedef ptrdiff_t GLsizeiptrARB;
 #define GL_OUT_OF_MEMORY                       0x0505
 
 #define GL_DITHER                              0x0BD0
+#define GL_ALPHA                               0x1906
 #define GL_RGB                                 0x1907
 #define GL_RGBA                                        0x1908
 
diff --git a/host.c b/host.c
index 549f0df..67ab1b5 100644 (file)
--- a/host.c
+++ b/host.c
@@ -975,6 +975,7 @@ char engineversion[128];
 
 qboolean sys_nostdout = false;
 
+extern void u8_Init(void);
 extern void Render_Init(void);
 extern void Mathlib_Init(void);
 extern void FS_Init(void);
@@ -1042,6 +1043,7 @@ static void Host_Init (void)
        Con_Init();
 
        // initialize various cvars that could not be initialized earlier
+       u8_Init();
        Curl_Init_Commands();
        Cmd_Init_Commands();
        Sys_Init_Commands();
index ece1566..80bc0a7 100644 (file)
@@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "sv_demo.h"
 #include "image.h"
 
+#include "utf8lib.h"
+
 // for secure rcon authentication
 #include "hmac.h"
 #include "mdfour.h"
@@ -1027,7 +1029,7 @@ void Host_Name_f (void)
                host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
        }
 
-       COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
+       u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
        if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
        {
                size_t l;
diff --git a/keys.c b/keys.c
index a2878c0..8c4aeff 100644 (file)
--- a/keys.c
+++ b/keys.c
@@ -22,6 +22,7 @@
 
 #include "quakedef.h"
 #include "cl_video.h"
+#include "utf8lib.h"
 
 cvar_t con_closeontoggleconsole = {CVAR_SAVE, "con_closeontoggleconsole","1", "allows toggleconsole binds to close the console as well"};
 
@@ -475,7 +476,7 @@ Interactive line editing and console scrollback
 ====================
 */
 static void
-Key_Console (int key, int ascii)
+Key_Console (int key, int unicode)
 {
        // LordHavoc: copied most of this from Q2 to improve keyboard handling
        switch (key)
@@ -552,6 +553,7 @@ Key_Console (int key, int ascii)
                        if (i > 0)
                        {
                                // terencehill: insert the clipboard text between the characters of the line
+                               /*
                                char *temp = (char *) Z_Malloc(MAX_INPUTLINE);
                                cbd[i]=0;
                                temp[0]=0;
@@ -563,6 +565,12 @@ Key_Console (int key, int ascii)
                                        strlcat(key_line, temp, sizeof(key_line));
                                Z_Free(temp);
                                key_linepos += i;
+                               */
+                               // blub: I'm changing this to use memmove() like the rest of the code does.
+                               cbd[i] = 0;
+                               memmove(key_line + key_linepos + i, key_line + key_linepos, sizeof(key_line) - key_linepos - i);
+                               memcpy(key_line + key_linepos, cbd, i);
+                               key_linepos += i;
                        }
                        Z_Free(cbd);
                }
@@ -704,7 +712,8 @@ Key_Console (int key, int ascii)
                else if(keydown[K_SHIFT]) // move cursor to the previous character ignoring colors
                {
                        int             pos;
-                       pos = key_linepos-1;
+                       size_t          inchar;
+                       pos = u8_prevbyte(key_line, key_linepos);
                        while (pos)
                                if(pos-1 > 0 && key_line[pos-1] == STRING_COLOR_TAG && isdigit(key_line[pos]))
                                        pos-=2;
@@ -718,10 +727,14 @@ Key_Console (int key, int ascii)
                                        pos--;
                                        break;
                                }
-                       key_linepos = pos + 1;
+                       // we need to move to the beginning of the character when in a wide character:
+                       u8_charidx(key_line, pos + 1, &inchar);
+                       key_linepos = pos + 1 - inchar;
                }
                else
-                       key_linepos--;
+               {
+                       key_linepos = u8_prevbyte(key_line, key_linepos);
+               }
                return;
        }
 
@@ -730,8 +743,9 @@ Key_Console (int key, int ascii)
        {
                if (key_linepos > 1)
                {
-                       strlcpy(key_line + key_linepos - 1, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
-                       key_linepos--;
+                       int newpos = u8_prevbyte(key_line, key_linepos);
+                       strlcpy(key_line + newpos, key_line + key_linepos, sizeof(key_line) + 1 - key_linepos);
+                       key_linepos = newpos;
                }
                return;
        }
@@ -742,7 +756,7 @@ Key_Console (int key, int ascii)
                size_t linelen;
                linelen = strlen(key_line);
                if (key_linepos < (int)linelen)
-                       memmove(key_line + key_linepos, key_line + key_linepos + 1, linelen - key_linepos);
+                       memmove(key_line + key_linepos, key_line + key_linepos + u8_bytelen(key_line + key_linepos, 1), linelen - key_linepos);
                return;
        }
 
@@ -796,7 +810,7 @@ Key_Console (int key, int ascii)
                        // skip the char
                        if (key_line[pos] == STRING_COLOR_TAG && key_line[pos+1] == STRING_COLOR_TAG) // consider ^^ as a character
                                pos++;
-                       pos++;
+                       pos += u8_bytelen(key_line + pos, 1);
                        
                        // now go beyond all next consecutive color tags, if any
                        if(pos < len)
@@ -812,7 +826,7 @@ Key_Console (int key, int ascii)
                        key_linepos = pos;
                }
                else
-                       key_linepos++;
+                       key_linepos += u8_bytelen(key_line + key_linepos, 1);
                return;
        }
 
@@ -923,22 +937,31 @@ Key_Console (int key, int ascii)
        }
 
        // non printable
-       if (ascii < 32)
+       if (unicode < 32)
                return;
 
        if (key_linepos < MAX_INPUTLINE-1)
        {
+               char buf[16];
                int len;
+               int blen;
+               blen = u8_fromchar(unicode, buf, sizeof(buf));
+               if (!blen)
+                       return;
                len = (int)strlen(&key_line[key_linepos]);
                // check insert mode, or always insert if at end of line
                if (key_insert || len == 0)
                {
                        // can't use strcpy to move string to right
                        len++;
-                       memmove(&key_line[key_linepos + 1], &key_line[key_linepos], len);
+                       //memmove(&key_line[key_linepos + u8_bytelen(key_line + key_linepos, 1)], &key_line[key_linepos], len);
+                       memmove(&key_line[key_linepos + blen], &key_line[key_linepos], len);
                }
-               key_line[key_linepos] = ascii;
-               key_linepos++;
+               memcpy(key_line + key_linepos, buf, blen);
+               key_linepos += blen;
+               //key_linepos += u8_fromchar(unicode, key_line + key_linepos, sizeof(key_line) - key_linepos - 1);
+               //key_line[key_linepos] = ascii;
+               //key_linepos++;
        }
 }
 
@@ -953,7 +976,6 @@ extern int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos);
 static void
 Key_Message (int key, int ascii)
 {
-
        if (key == K_ENTER || ascii == 10 || ascii == 13)
        {
                if(chat_mode < 0)
@@ -978,7 +1000,7 @@ Key_Message (int key, int ascii)
 
        if (key == K_BACKSPACE) {
                if (chat_bufferlen) {
-                       chat_bufferlen--;
+                       chat_bufferlen = u8_prevbyte(chat_buffer, chat_bufferlen);
                        chat_buffer[chat_bufferlen] = 0;
                }
                return;
@@ -995,8 +1017,10 @@ Key_Message (int key, int ascii)
        if (!ascii)
                return;                                                 // non printable
 
-       chat_buffer[chat_bufferlen++] = ascii;
-       chat_buffer[chat_bufferlen] = 0;
+       chat_bufferlen += u8_fromchar(ascii, chat_buffer+chat_bufferlen, sizeof(chat_buffer) - chat_bufferlen - 1);
+
+       //chat_buffer[chat_bufferlen++] = ascii;
+       //chat_buffer[chat_bufferlen] = 0;
 }
 
 //============================================================================
index 4d11490..30e41b3 100644 (file)
@@ -111,6 +111,8 @@ OBJ_COMMON= \
        filematch.o \
        fractalnoise.o \
        fs.o \
+       ft2.o \
+       utf8lib.o \
        gl_backend.o \
        gl_draw.o \
        gl_rmain.o \
index 2313320..e3e1b32 100644 (file)
@@ -10,6 +10,8 @@
 #include "libcurl.h"
 #include <time.h>
 
+#include "ft2.h"
+
 extern cvar_t prvm_backtraceforwarnings;
 
 // LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
@@ -2167,7 +2169,8 @@ void VM_strlen(void)
 {
        VM_SAFEPARMCOUNT(1,VM_strlen);
 
-       PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
+       //PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
+       PRVM_G_FLOAT(OFS_RETURN) = u8_strlen(PRVM_G_STRING(OFS_PARM0));
 }
 
 // DRESK - Decolorized String
@@ -2210,7 +2213,8 @@ void VM_strlennocol(void)
 
        szString = PRVM_G_STRING(OFS_PARM0);
 
-       nCnt = COM_StringLengthNoColors(szString, 0, NULL);
+       //nCnt = COM_StringLengthNoColors(szString, 0, NULL);
+       nCnt = u8_COM_StringLengthNoColors(szString, 0, NULL);
 
        PRVM_G_FLOAT(OFS_RETURN) = nCnt;
 }
@@ -2290,12 +2294,15 @@ string  substring(string s, float start, float length)
 // returns a section of a string as a tempstring
 void VM_substring(void)
 {
-       int start, length, slength, maxlen;
+       int start, length;
+       int u_slength = 0, u_start;
+       size_t u_length;
        const char *s;
        char string[VM_STRINGTEMP_LENGTH];
 
        VM_SAFEPARMCOUNT(3,VM_substring);
 
+       /*
        s = PRVM_G_STRING(OFS_PARM0);
        start = (int)PRVM_G_FLOAT(OFS_PARM1);
        length = (int)PRVM_G_FLOAT(OFS_PARM2);
@@ -2313,6 +2320,40 @@ void VM_substring(void)
        memcpy(string, s + start, length);
        string[length] = 0;
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
+       */
+       
+       s = PRVM_G_STRING(OFS_PARM0);
+       start = (int)PRVM_G_FLOAT(OFS_PARM1);
+       length = (int)PRVM_G_FLOAT(OFS_PARM2);
+
+       if (start < 0) // FTE_STRINGS feature
+       {
+               u_slength = u8_strlen(s);
+               start += u_slength;
+               start = bound(0, start, u_slength);
+       }
+
+       if (length < 0) // FTE_STRINGS feature
+       {
+               if (!u_slength) // it's not calculated when it's not needed above
+                       u_slength = u8_strlen(s);
+               length += u_slength - start + 1;
+       }
+               
+       // positive start, positive length
+       u_start = u8_byteofs(s, start, NULL);
+       if (u_start < 0)
+       {
+               PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
+               return;
+       }
+       u_length = u8_bytelen(s + u_start, length);
+       if (u_length >= sizeof(string)-1)
+               u_length = sizeof(string)-1;
+       
+       memcpy(string, s + u_start, u_length);
+       string[u_length] = 0;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
 }
 
 /*
@@ -3095,6 +3136,7 @@ string    chr(float ascii)
 */
 void VM_chr(void)
 {
+       /*
        char tmp[2];
        VM_SAFEPARMCOUNT(1, VM_chr);
 
@@ -3102,6 +3144,17 @@ void VM_chr(void)
        tmp[1] = 0;
 
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
+       */
+       
+       char tmp[8];
+       int len;
+       VM_SAFEPARMCOUNT(1, VM_chr);
+
+       len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
+       if (len < 0)
+               len = 0;
+       tmp[len] = 0;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
 }
 
 //=============================================================================
@@ -3262,6 +3315,7 @@ void VM_drawstring(void)
                Con_Printf("VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
 
        DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
+       //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true);
        PRVM_G_FLOAT(OFS_RETURN) = 1;
 }
 
@@ -3315,10 +3369,30 @@ float   stringwidth(string text, float allowColorCodes, float size)
 void VM_stringwidth(void)
 {
        const char  *string;
-       float sz, mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
+       float *szv;
+       float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
        int colors;
+       float x[200];
        VM_SAFEPARMCOUNTRANGE(2,3,VM_drawstring);
 
+       if(prog->argc == 3)
+       {
+               szv = PRVM_G_VECTOR(OFS_PARM2);
+               mult = 1;
+       }
+       else
+       {
+               static float defsize[] = {0, 0};
+               szv = defsize;
+               mult = 1;
+       }
+       x[180] = 3;
+
+       string = PRVM_G_STRING(OFS_PARM0);
+       colors = (int)PRVM_G_FLOAT(OFS_PARM1);
+
+       PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font_Size(string, szv[0], szv[1], 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
+/*
        if(prog->argc == 3)
        {
                mult = sz = PRVM_G_FLOAT(OFS_PARM2);
@@ -3333,6 +3407,8 @@ void VM_stringwidth(void)
        colors = (int)PRVM_G_FLOAT(OFS_PARM1);
 
        PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
+*/
+
 }
 /*
 =========
@@ -4929,6 +5005,7 @@ void VM_strstrofs (void)
        instr = PRVM_G_STRING(OFS_PARM0);
        match = PRVM_G_STRING(OFS_PARM1);
        firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
+       firstofs = u8_bytelen(instr, firstofs);
 
        if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
        {
@@ -4947,10 +5024,17 @@ void VM_strstrofs (void)
 void VM_str2chr (void)
 {
        const char *s;
+       Uchar ch;
+       int index;
        VM_SAFEPARMCOUNT(2, VM_str2chr);
        s = PRVM_G_STRING(OFS_PARM0);
-       if((unsigned)PRVM_G_FLOAT(OFS_PARM1) < strlen(s))
-               PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
+       index = u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
+
+       if((unsigned)index < strlen(s))
+       {
+               ch = u8_getchar(s + index, NULL);
+               PRVM_G_FLOAT(OFS_RETURN) = ch;
+       }
        else
                PRVM_G_FLOAT(OFS_RETURN) = 0;
 }
@@ -4958,6 +5042,7 @@ void VM_str2chr (void)
 //#223 string(float c, ...) chr2str (FTE_STRINGS)
 void VM_chr2str (void)
 {
+       /*
        char    t[9];
        int             i;
        VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
@@ -4965,6 +5050,19 @@ void VM_chr2str (void)
                t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
        t[i] = 0;
        PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
+       */
+       char t[9 * 4 + 1];
+       int i;
+       size_t len = 0;
+       VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
+       for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
+       {
+               int add = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
+               if(add > 0)
+                       len += add;
+       }
+       t[len] = 0;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
 }
 
 static int chrconv_number(int i, int base, int conv)
index 12b5cc5..503163f 100644 (file)
@@ -39,6 +39,8 @@ typedef enum textype_e
        TEXTYPE_BGRA,
        // 16bit D16 (16bit depth) or 32bit S8D24 (24bit depth, 8bit stencil unused)
        TEXTYPE_SHADOWMAP,
+       // 8bit ALPHA (used for freetype fonts)
+       TEXTYPE_ALPHA,
 }
 textype_t;
 
index 7a3076a..a00029b 100644 (file)
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -388,7 +388,7 @@ void Sys_SendKeyEvents( void )
                                break;
                        case SDL_KEYDOWN:
                        case SDL_KEYUP:
-                               Key_Event( MapKey( event.key.keysym.sym ), (char)event.key.keysym.unicode, (event.key.state == SDL_PRESSED) );
+                               Key_Event( MapKey( event.key.keysym.sym ), event.key.keysym.unicode, (event.key.state == SDL_PRESSED) );
                                break;
                        case SDL_ACTIVEEVENT:
                                if( event.active.state & SDL_APPACTIVE )