]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
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 c002be8d769b8b8314140f9068e54beb4b4f87f1..70f36e717067dec6a1b40427f4b52f2fbd316026 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 71a5f86928ccea41619a75c690db6287bb0eea85..6a59b970f4a1d72c830cd4017343ac7fc5ee1276 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 e3651266f3cafb7b30de8b13bc2be7b29d9022b1..114f90cc82eb0e463fd667e49ab2f7f8d061157e 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 691a7b291a3c3cc3bc57c7d1e732498b762c1e58..66b2b89ecea1454bd23908fc060ab39f644e7309 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 d45a0eef68132901ad828bcde10718d667434964..0e3266f2c241657453b7a6bdeb69ef3136a78b41 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 25074722fc49283cd7acde7dccd37b5021cd0dbc..249ced51c2554b21b3793b4a3bcfd2fe86354565 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 d657de4411abe71ead2d7303a45371e9d857e4fd..b8a224324b12ea115efc6e6de5b3ee978ff945db 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 549f0dfac471e615b523cd67fee4f7f7aaa63ead..67ab1b57f4c67dc6b92d938ecf27cee6c17d2657 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 ece15660e47075d666f22f12139a9054b671f6bd..80bc0a7f774e7ba00948b9703008f7f013a422f7 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 a2878c0c5cd8a42bc39ae7a6741b85a920d1c7ed..8c4aeff847ae7ca5caf6df07c081eecce5ae2da3 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 4d1149006d87dd7eba7c8aab5a3f58854ef93352..30e41b3c119d577284de0b64e39337475f382980 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 23133207bfd7e5de4238497465a2fef35bdb94dc..e3e1b322dbc1c3bde3c307a4c10a449d886821cc 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 12b5cc5ea579d46ba2bd43c07270f68bdac8bf65..503163f86a78be9d99b3a75472bfe51accdc8a95 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 7a3076a3ce731600d2cefed39094fe3c29637936..a00029bfc6658c8988b5903c49eb1a51a6194750 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 )