CSQC polygonbegin functionality now uses the CL_MeshEntities system, this finally...
[xonotic/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "wad.h"
24
25 #include "cl_video.h"
26
27 #include "ft2.h"
28 #include "ft2_fontdefs.h"
29
30 struct cachepic_s
31 {
32         // size of pic
33         int width, height;
34         // this flag indicates that it should be loaded and unloaded on demand
35         int autoload;
36         // texture flags to upload with
37         int texflags;
38         // texture may be freed after a while
39         int lastusedframe;
40         // renderable texture
41         skinframe_t *skinframe;
42         // used for hash lookups
43         struct cachepic_s *chain;
44         // flags - CACHEPICFLAG_NEWPIC for example
45         unsigned int flags;
46         // name of pic
47         char name[MAX_QPATH];
48 };
49
50 dp_fonts_t dp_fonts;
51 static mempool_t *fonts_mempool = NULL;
52
53 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)"};
54 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
55 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
56
57 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
58 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
59 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
60 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
61 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
62 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
63 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
64 cvar_t r_nearest_2d = {CVAR_SAVE, "r_nearest_2d", "0", "use nearest filtering on all 2d textures (including conchars)"};
65 cvar_t r_nearest_conchars = {CVAR_SAVE, "r_nearest_conchars", "0", "use nearest filtering on conchars texture"};
66
67 //=============================================================================
68 /* Support Routines */
69
70 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
71 static cachepic_t cachepics[MAX_CACHED_PICS];
72 static int numcachepics;
73
74 rtexturepool_t *drawtexturepool;
75
76 int draw_frame = 1;
77
78 /*
79 ================
80 Draw_CachePic
81 ================
82 */
83 // FIXME: move this to client somehow
84 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
85 {
86         int crc, hashkey;
87         cachepic_t *pic;
88         int texflags;
89
90         texflags = TEXF_ALPHA;
91         if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
92                 texflags |= TEXF_CLAMP;
93         if (cachepicflags & CACHEPICFLAG_MIPMAP)
94                 texflags |= TEXF_MIPMAP;
95         if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
96                 texflags |= TEXF_COMPRESS;
97         if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer)
98                 texflags |= TEXF_FORCENEAREST;
99
100         // check whether the picture has already been cached
101         crc = CRC_Block((unsigned char *)path, strlen(path));
102         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
103         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
104         {
105                 if (!strcmp(path, pic->name))
106                 {
107                         // if it was created (or replaced) by Draw_NewPic, just return it
108                         if (pic->flags & CACHEPICFLAG_NEWPIC)
109                         {
110                                 if (pic->skinframe)
111                                         R_SkinFrame_MarkUsed(pic->skinframe);
112                                 pic->lastusedframe = draw_frame;
113                                 return pic;
114                         }
115                         if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS | TEXF_MIPMAP))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag, and ignore TEXF_MIPMAP because QC specifies that
116                         {
117                                 if (!pic->skinframe || !pic->skinframe->base)
118                                         goto reload;
119                                 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
120                                         pic->autoload = false; // caller is making this pic persistent
121                                 R_SkinFrame_MarkUsed(pic->skinframe);
122                                 pic->lastusedframe = draw_frame;
123                                 return pic;
124                         }
125                 }
126         }
127
128         if (numcachepics == MAX_CACHED_PICS)
129         {
130                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
131                 // FIXME: support NULL in callers?
132                 return cachepics; // return the first one
133         }
134         pic = cachepics + (numcachepics++);
135         memset(pic, 0, sizeof(*pic));
136         strlcpy (pic->name, path, sizeof(pic->name));
137         // link into list
138         pic->chain = cachepichash[hashkey];
139         cachepichash[hashkey] = pic;
140
141 reload:
142         if (pic->skinframe)
143                 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
144
145         pic->flags = cachepicflags;
146         pic->texflags = texflags;
147         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) != 0;
148         pic->lastusedframe = draw_frame;
149
150         // load high quality image (this falls back to low quality too)
151         pic->skinframe = R_SkinFrame_LoadExternal(pic->name, texflags, (cachepicflags & CACHEPICFLAG_QUIET) == 0, (cachepicflags & CACHEPICFLAG_FAILONMISSING) == 0);
152
153         // get the dimensions of the image we loaded (if it was successful)
154         if (pic->skinframe && pic->skinframe->base)
155         {
156                 pic->width = R_TextureWidth(pic->skinframe->base);
157                 pic->height = R_TextureHeight(pic->skinframe->base);
158         }
159
160         // check for a low quality version of the pic and use its size if possible, to match the stock hud
161         Image_GetStockPicSize(pic->name, &pic->width, &pic->height);
162
163         return pic;
164 }
165
166 cachepic_t *Draw_CachePic (const char *path)
167 {
168         return Draw_CachePic_Flags (path, 0); // default to persistent!
169 }
170
171 const char *Draw_GetPicName(cachepic_t *pic)
172 {
173         if (pic == NULL)
174                 return "";
175         return pic->name;
176 }
177
178 int Draw_GetPicWidth(cachepic_t *pic)
179 {
180         if (pic == NULL)
181                 return 0;
182         return pic->width;
183 }
184
185 int Draw_GetPicHeight(cachepic_t *pic)
186 {
187         if (pic == NULL)
188                 return 0;
189         return pic->height;
190 }
191
192 qboolean Draw_IsPicLoaded(cachepic_t *pic)
193 {
194         if (pic == NULL)
195                 return false;
196         if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
197                 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true);
198         // skinframe will only be NULL if the pic was created with CACHEPICFLAG_FAILONMISSING and not found
199         return pic->skinframe != NULL && pic->skinframe->base != NULL;
200 }
201
202 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
203 {
204         if (pic == NULL)
205                 return NULL;
206         if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
207                 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true);
208         pic->lastusedframe = draw_frame;
209         return pic->skinframe ? pic->skinframe->base : NULL;
210 }
211
212 void Draw_Frame(void)
213 {
214         int i;
215         cachepic_t *pic;
216         static double nextpurgetime;
217         if (nextpurgetime > realtime)
218                 return;
219         nextpurgetime = realtime + 0.05;
220         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
221                 if (pic->autoload && pic->skinframe && pic->skinframe->base && pic->lastusedframe < draw_frame)
222                         R_SkinFrame_PurgeSkinFrame(pic->skinframe);
223         draw_frame++;
224 }
225
226 cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels_bgra, textype_t textype, int texflags)
227 {
228         int crc, hashkey;
229         cachepic_t *pic;
230
231         crc = CRC_Block((unsigned char *)picname, strlen(picname));
232         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
233         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
234                 if (!strcmp (picname, pic->name))
235                         break;
236
237         if (pic)
238         {
239                 if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height)
240                 {
241                         R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1);
242                         R_SkinFrame_MarkUsed(pic->skinframe);
243                         pic->lastusedframe = draw_frame;
244                         return pic;
245                 }
246         }
247         else
248         {
249                 if (numcachepics == MAX_CACHED_PICS)
250                 {
251                         Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
252                         // FIXME: support NULL in callers?
253                         return cachepics; // return the first one
254                 }
255                 pic = cachepics + (numcachepics++);
256                 memset(pic, 0, sizeof(*pic));
257                 strlcpy (pic->name, picname, sizeof(pic->name));
258                 // link into list
259                 pic->chain = cachepichash[hashkey];
260                 cachepichash[hashkey] = pic;
261         }
262
263         R_SkinFrame_PurgeSkinFrame(pic->skinframe);
264
265         pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
266         pic->flags |= (texflags & TEXF_CLAMP) ? 0 : CACHEPICFLAG_NOCLAMP;
267         pic->flags |= (texflags & TEXF_FORCENEAREST) ? CACHEPICFLAG_NEAREST : 0;
268         pic->width = width;
269         pic->height = height;
270         pic->skinframe = R_SkinFrame_LoadInternalBGRA(picname, texflags, pixels_bgra, width, height, vid.sRGB2D);
271         pic->lastusedframe = draw_frame;
272         return pic;
273 }
274
275 void Draw_FreePic(const char *picname)
276 {
277         int crc;
278         int hashkey;
279         cachepic_t *pic;
280         // this doesn't really free the pic, but does free its texture
281         crc = CRC_Block((unsigned char *)picname, strlen(picname));
282         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
283         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
284         {
285                 if (!strcmp (picname, pic->name) && pic->skinframe)
286                 {
287                         R_SkinFrame_PurgeSkinFrame(pic->skinframe);
288                         pic->width = 0;
289                         pic->height = 0;
290                         return;
291                 }
292         }
293 }
294
295 static float snap_to_pixel_x(float x, float roundUpAt);
296 extern int con_linewidth; // to force rewrapping
297 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
298 {
299         int i, ch;
300         float maxwidth;
301         char widthfile[MAX_QPATH];
302         char *widthbuf;
303         fs_offset_t widthbufsize;
304
305         if(override || !fnt->texpath[0])
306         {
307                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
308                 // load the cvars when the font is FIRST loader
309                 fnt->settings.scale = scale;
310                 fnt->settings.voffset = voffset;
311                 fnt->settings.antialias = r_font_antialias.integer;
312                 fnt->settings.hinting = r_font_hinting.integer;
313                 fnt->settings.outline = r_font_postprocess_outline.value;
314                 fnt->settings.blur = r_font_postprocess_blur.value;
315                 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
316                 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
317                 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
318         }
319         // fix bad scale
320         if (fnt->settings.scale <= 0)
321                 fnt->settings.scale = 1;
322
323         if(drawtexturepool == NULL)
324                 return; // before gl_draw_start, so will be loaded later
325
326         if(fnt->ft2)
327         {
328                 // clear freetype font
329                 Font_UnloadFont(fnt->ft2);
330                 Mem_Free(fnt->ft2);
331                 fnt->ft2 = NULL;
332         }
333
334         if(fnt->req_face != -1)
335         {
336                 if(!Font_LoadFont(fnt->texpath, fnt))
337                         Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
338         }
339
340         fnt->pic = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
341         if(!Draw_IsPicLoaded(fnt->pic))
342         {
343                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
344                 {
345                         if (!fnt->fallbacks[i][0])
346                                 break;
347                         fnt->pic = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
348                         if(Draw_IsPicLoaded(fnt->pic))
349                                 break;
350                 }
351                 if(!Draw_IsPicLoaded(fnt->pic))
352                 {
353                         fnt->pic = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0));
354                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
355                 }
356                 else
357                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
358         }
359         else
360                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
361
362         // unspecified width == 1 (base width)
363         for(ch = 0; ch < 256; ++ch)
364                 fnt->width_of[ch] = 1;
365
366         // FIXME load "name.width", if it fails, fill all with 1
367         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
368         {
369                 float extraspacing = 0;
370                 const char *p = widthbuf;
371
372                 ch = 0;
373                 while(ch < 256)
374                 {
375                         if(!COM_ParseToken_Simple(&p, false, false, true))
376                                 return;
377
378                         switch(*com_token)
379                         {
380                                 case '0':
381                                 case '1':
382                                 case '2':
383                                 case '3':
384                                 case '4':
385                                 case '5':
386                                 case '6':
387                                 case '7':
388                                 case '8':
389                                 case '9':
390                                 case '+':
391                                 case '-':
392                                 case '.':
393                                         fnt->width_of[ch] = atof(com_token) + extraspacing;
394                                         ch++;
395                                         break;
396                                 default:
397                                         if(!strcmp(com_token, "extraspacing"))
398                                         {
399                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
400                                                         return;
401                                                 extraspacing = atof(com_token);
402                                         }
403                                         else if(!strcmp(com_token, "scale"))
404                                         {
405                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
406                                                         return;
407                                                 fnt->settings.scale = atof(com_token);
408                                         }
409                                         else
410                                         {
411                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
412                                                 if(!COM_ParseToken_Simple(&p, false, false, true))
413                                                         return;
414                                         }
415                                         break;
416                         }
417                 }
418
419                 Mem_Free(widthbuf);
420         }
421
422         if(fnt->ft2)
423         {
424                 for (i = 0; i < MAX_FONT_SIZES; ++i)
425                 {
426                         ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
427                         if (!map)
428                                 break;
429                         for(ch = 0; ch < 256; ++ch)
430                                 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
431                 }
432         }
433
434         maxwidth = fnt->width_of[0];
435         for(i = 1; i < 256; ++i)
436                 maxwidth = max(maxwidth, fnt->width_of[i]);
437         fnt->maxwidth = maxwidth;
438
439         // fix up maxwidth for overlap
440         fnt->maxwidth *= fnt->settings.scale;
441
442         if(fnt == FONT_CONSOLE)
443                 con_linewidth = -1; // rewrap console in next frame
444 }
445
446 extern cvar_t developer_font;
447 dp_font_t *FindFont(const char *title, qboolean allocate_new)
448 {
449         int i, oldsize;
450
451         // find font
452         for(i = 0; i < dp_fonts.maxsize; ++i)
453                 if(!strcmp(dp_fonts.f[i].title, title))
454                         return &dp_fonts.f[i];
455         // if not found - try allocate
456         if (allocate_new)
457         {
458                 // find any font with empty title
459                 for(i = 0; i < dp_fonts.maxsize; ++i)
460                 {
461                         if(!strcmp(dp_fonts.f[i].title, ""))
462                         {
463                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
464                                 return &dp_fonts.f[i];
465                         }
466                 }
467                 // if no any 'free' fonts - expand buffer
468                 oldsize = dp_fonts.maxsize;
469                 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
470                 if (developer_font.integer)
471                         Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
472                 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
473                 // relink ft2 structures
474                 for(i = 0; i < oldsize; ++i)
475                         if (dp_fonts.f[i].ft2)
476                                 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
477                 // register a font in first expanded slot
478                 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
479                 return &dp_fonts.f[oldsize];
480         }
481         return NULL;
482 }
483
484 static float snap_to_pixel_x(float x, float roundUpAt)
485 {
486         float pixelpos = x * vid.width / vid_conwidth.value;
487         int snap = (int) pixelpos;
488         if (pixelpos - snap >= roundUpAt) ++snap;
489         return ((float)snap * vid_conwidth.value / vid.width);
490         /*
491         x = (int)(x * vid.width / vid_conwidth.value);
492         x = (x * vid_conwidth.value / vid.width);
493         return x;
494         */
495 }
496
497 static float snap_to_pixel_y(float y, float roundUpAt)
498 {
499         float pixelpos = y * vid.height / vid_conheight.value;
500         int snap = (int) pixelpos;
501         if (pixelpos - snap > roundUpAt) ++snap;
502         return ((float)snap * vid_conheight.value / vid.height);
503         /*
504         y = (int)(y * vid.height / vid_conheight.value);
505         y = (y * vid_conheight.value / vid.height);
506         return y;
507         */
508 }
509
510 static void LoadFont_f(void)
511 {
512         dp_font_t *f;
513         int i, sizes;
514         const char *filelist, *c, *cm;
515         float sz, scale, voffset;
516         char mainfont[MAX_QPATH];
517
518         if(Cmd_Argc() < 2)
519         {
520                 Con_Printf("Available font commands:\n");
521                 for(i = 0; i < dp_fonts.maxsize; ++i)
522                         if (dp_fonts.f[i].title[0])
523                                 Con_Printf("  loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
524                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
525                            "can specify multiple fonts and faces\n"
526                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
527                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
528                            "of gfx/fallback as fallback font.\n"
529                            "You can also specify a list of font sizes to load, like this:\n"
530                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
531                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
532                            "custom switches:\n"
533                            " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
534                            " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
535                         );
536                 return;
537         }
538         f = FindFont(Cmd_Argv(1), true);
539         if(f == NULL)
540         {
541                 Con_Printf("font function not found\n");
542                 return;
543         }
544
545         if(Cmd_Argc() < 3)
546                 filelist = "gfx/conchars";
547         else
548                 filelist = Cmd_Argv(2);
549
550         memset(f->fallbacks, 0, sizeof(f->fallbacks));
551         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
552
553         // first font is handled "normally"
554         c = strchr(filelist, ':');
555         cm = strchr(filelist, ',');
556         if(c && (!cm || c < cm))
557                 f->req_face = atoi(c+1);
558         else
559         {
560                 f->req_face = 0;
561                 c = cm;
562         }
563
564         if(!c || (c - filelist) > MAX_QPATH)
565                 strlcpy(mainfont, filelist, sizeof(mainfont));
566         else
567         {
568                 memcpy(mainfont, filelist, c - filelist);
569                 mainfont[c - filelist] = 0;
570         }
571
572         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
573         {
574                 c = strchr(filelist, ',');
575                 if(!c)
576                         break;
577                 filelist = c + 1;
578                 if(!*filelist)
579                         break;
580                 c = strchr(filelist, ':');
581                 cm = strchr(filelist, ',');
582                 if(c && (!cm || c < cm))
583                         f->fallback_faces[i] = atoi(c+1);
584                 else
585                 {
586                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
587                         c = cm;
588                 }
589                 if(!c || (c-filelist) > MAX_QPATH)
590                 {
591                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
592                 }
593                 else
594                 {
595                         memcpy(f->fallbacks[i], filelist, c - filelist);
596                         f->fallbacks[i][c - filelist] = 0;
597                 }
598         }
599
600         // for now: by default load only one size: the default size
601         f->req_sizes[0] = 0;
602         for(i = 1; i < MAX_FONT_SIZES; ++i)
603                 f->req_sizes[i] = -1;
604
605         scale = 1;
606         voffset = 0;
607         if(Cmd_Argc() >= 4)
608         {
609                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
610                 {
611                         // special switches
612                         if (!strcmp(Cmd_Argv(i), "scale"))
613                         {
614                                 i++;
615                                 if (i < Cmd_Argc())
616                                         scale = atof(Cmd_Argv(i));
617                                 continue;
618                         }
619                         if (!strcmp(Cmd_Argv(i), "voffset"))
620                         {
621                                 i++;
622                                 if (i < Cmd_Argc())
623                                         voffset = atof(Cmd_Argv(i));
624                                 continue;
625                         }
626
627                         if (sizes == -1)
628                                 continue; // no slot for other sizes
629
630                         // parse one of sizes
631                         sz = atof(Cmd_Argv(i));
632                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
633                         {
634                                 // search for duplicated sizes
635                                 int j;
636                                 for (j=0; j<sizes; j++)
637                                         if (f->req_sizes[j] == sz)
638                                                 break;
639                                 if (j != sizes)
640                                         continue; // sz already in req_sizes, don't add it again
641
642                                 if (sizes == MAX_FONT_SIZES)
643                                 {
644                                         Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
645                                         sizes = -1;
646                                         continue;
647                                 }
648                                 f->req_sizes[sizes] = sz;
649                                 sizes++;
650                         }
651                 }
652         }
653
654         LoadFont(true, mainfont, f, scale, voffset);
655 }
656
657 /*
658 ===============
659 Draw_Init
660 ===============
661 */
662 static void gl_draw_start(void)
663 {
664         int i;
665         char vabuf[1024];
666         drawtexturepool = R_AllocTexturePool();
667
668         numcachepics = 0;
669         memset(cachepichash, 0, sizeof(cachepichash));
670
671         font_start();
672
673         // load default font textures
674         for(i = 0; i < dp_fonts.maxsize; ++i)
675                 if (dp_fonts.f[i].title[0])
676                         LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
677
678         // draw the loading screen so people have something to see in the newly opened window
679         SCR_UpdateLoadingScreen(true, true);
680 }
681
682 static void gl_draw_shutdown(void)
683 {
684         font_shutdown();
685
686         R_FreeTexturePool(&drawtexturepool);
687
688         numcachepics = 0;
689         memset(cachepichash, 0, sizeof(cachepichash));
690 }
691
692 static void gl_draw_newmap(void)
693 {
694         int i;
695         font_newmap();
696
697         // mark all of the persistent pics so they are not purged...
698         for (i = 0; i < numcachepics; i++)
699         {
700                 cachepic_t *pic = cachepics + i;
701                 if (!pic->autoload && pic->skinframe)
702                         R_SkinFrame_MarkUsed(pic->skinframe);
703         }
704 }
705
706 void GL_Draw_Init (void)
707 {
708         int i, j;
709
710         Cvar_RegisterVariable(&r_font_postprocess_blur);
711         Cvar_RegisterVariable(&r_font_postprocess_outline);
712         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
713         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
714         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
715         Cvar_RegisterVariable(&r_font_hinting);
716         Cvar_RegisterVariable(&r_font_antialias);
717         Cvar_RegisterVariable(&r_textshadow);
718         Cvar_RegisterVariable(&r_textbrightness);
719         Cvar_RegisterVariable(&r_textcontrast);
720         Cvar_RegisterVariable(&r_nearest_2d);
721         Cvar_RegisterVariable(&r_nearest_conchars);
722
723         // allocate fonts storage
724         fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
725         dp_fonts.maxsize = MAX_FONTS;
726         dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
727         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
728
729         // assign starting font names
730         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
731         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
732         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
733         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
734         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
735         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
736         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
737         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
738         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
739         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
740                 if(!FONT_USER(i)->title[0])
741                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
742
743         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
744         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
745 }
746
747 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
748 {
749         if (r_refdef.draw2dstage == 1)
750                 return;
751         DrawQ_FlushUI();
752         r_refdef.draw2dstage = 1;
753         R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
754 }
755
756 qboolean r_draw2d_force = false;
757
758 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
759 {
760         dp_model_t *mod = CL_Mesh_UI();
761         msurface_t *surf;
762         int e0, e1, e2, e3;
763         _DrawQ_Setup();
764         if (!pic)
765                 pic = Draw_CachePic("white");
766         if (width == 0)
767                 width = pic->width;
768         if (height == 0)
769                 height = pic->height;
770         surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
771         e0 = Mod_Mesh_IndexForVertex(mod, surf, x        , y         , 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
772         e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y         , 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
773         e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
774         e3 = Mod_Mesh_IndexForVertex(mod, surf, x        , y + height, 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
775         Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
776         Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
777 }
778
779 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
780 {
781         float af = DEG2RAD(-angle); // forward
782         float ar = DEG2RAD(-angle + 90); // right
783         float sinaf = sin(af);
784         float cosaf = cos(af);
785         float sinar = sin(ar);
786         float cosar = cos(ar);
787         dp_model_t *mod = CL_Mesh_UI();
788         msurface_t *surf;
789         int e0, e1, e2, e3;
790         _DrawQ_Setup();
791         if (!pic)
792                 pic = Draw_CachePic("white");
793         if (width == 0)
794                 width = pic->width;
795         if (height == 0)
796                 height = pic->height;
797         surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
798         e0 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x - cosar * org_y, y - sinaf * org_x - sinar * org_y, 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
799         e1 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) - cosar * org_y, y + sinaf * (width - org_x) - sinar * org_y, 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
800         e2 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) + cosar * (height - org_y), sinaf*(width - org_x) + sinar * (height - org_y), 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
801         e3 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x + cosar * (height - org_y), y - sinaf * org_x + sinar * (height - org_y), 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
802         Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
803         Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
804 }
805
806 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
807 {
808         DrawQ_Pic(x, y, Draw_CachePic("white"), width, height, red, green, blue, alpha, flags);
809 }
810
811 /// color tag printing
812 static const vec4_t string_colors[] =
813 {
814         // Quake3 colors
815         // LordHavoc: why on earth is cyan before magenta in Quake3?
816         // LordHavoc: note: Doom3 uses white for [0] and [7]
817         {0.0, 0.0, 0.0, 1.0}, // black
818         {1.0, 0.0, 0.0, 1.0}, // red
819         {0.0, 1.0, 0.0, 1.0}, // green
820         {1.0, 1.0, 0.0, 1.0}, // yellow
821         {0.0, 0.0, 1.0, 1.0}, // blue
822         {0.0, 1.0, 1.0, 1.0}, // cyan
823         {1.0, 0.0, 1.0, 1.0}, // magenta
824         {1.0, 1.0, 1.0, 1.0}, // white
825         // [515]'s BX_COLOREDTEXT extension
826         {1.0, 1.0, 1.0, 0.5}, // half transparent
827         {0.5, 0.5, 0.5, 1.0}  // half brightness
828         // Black's color table
829         //{1.0, 1.0, 1.0, 1.0},
830         //{1.0, 0.0, 0.0, 1.0},
831         //{0.0, 1.0, 0.0, 1.0},
832         //{0.0, 0.0, 1.0, 1.0},
833         //{1.0, 1.0, 0.0, 1.0},
834         //{0.0, 1.0, 1.0, 1.0},
835         //{1.0, 0.0, 1.0, 1.0},
836         //{0.1, 0.1, 0.1, 1.0}
837 };
838
839 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
840
841 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
842 {
843         float C = r_textcontrast.value;
844         float B = r_textbrightness.value;
845         if (colorindex & 0x10000) // that bit means RGB color
846         {
847                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
848                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
849                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
850                 color[3] = (colorindex & 0xf) / 15.0;
851         }
852         else
853                 Vector4Copy(string_colors[colorindex], color);
854         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
855         if (shadow)
856         {
857                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
858                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
859         }
860 }
861
862 // NOTE: this function always draws exactly one character if maxwidth <= 0
863 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
864 {
865         const char *text_start = text;
866         int colorindex = STRING_COLOR_DEFAULT;
867         size_t i;
868         float x = 0;
869         Uchar ch, mapch, nextch;
870         Uchar prevch = 0; // used for kerning
871         int tempcolorindex;
872         float kx;
873         int map_index = 0;
874         size_t bytes_left;
875         ft2_font_map_t *fontmap = NULL;
876         ft2_font_map_t *map = NULL;
877         //ft2_font_map_t *prevmap = NULL;
878         ft2_font_t *ft2 = fnt->ft2;
879         // float ftbase_x;
880         qboolean snap = true;
881         qboolean least_one = false;
882         float dw; // display w
883         //float dh; // display h
884         const float *width_of;
885
886         if (!h) h = w;
887         if (!h) {
888                 w = h = 1;
889                 snap = false;
890         }
891         // do this in the end
892         w *= fnt->settings.scale;
893         h *= fnt->settings.scale;
894
895         // find the most fitting size:
896         if (ft2 != NULL)
897         {
898                 if (snap)
899                         map_index = Font_IndexForSize(ft2, h, &w, &h);
900                 else
901                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
902                 fontmap = Font_MapForIndex(ft2, map_index);
903         }
904
905         dw = w * sw;
906         //dh = h * sh;
907
908         if (*maxlen < 1)
909                 *maxlen = 1<<30;
910
911         if (!outcolor || *outcolor == -1)
912                 colorindex = STRING_COLOR_DEFAULT;
913         else
914                 colorindex = *outcolor;
915
916         // maxwidth /= fnt->scale; // w and h are multiplied by it already
917         // ftbase_x = snap_to_pixel_x(0);
918         
919         if(maxwidth <= 0)
920         {
921                 least_one = true;
922                 maxwidth = -maxwidth;
923         }
924
925         //if (snap)
926         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
927
928         if (fontmap)
929                 width_of = fontmap->width_of;
930         else
931                 width_of = fnt->width_of;
932
933         i = 0;
934         while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text)
935         {
936                 size_t i0 = i;
937                 nextch = ch = u8_getnchar(text, &text, bytes_left);
938                 i = text - text_start;
939                 if (!ch)
940                         break;
941                 if (ch == ' ' && !fontmap)
942                 {
943                         if(!least_one || i0) // never skip the first character
944                         if(x + width_of[(int) ' '] * dw > maxwidth)
945                         {
946                                 i = i0;
947                                 break; // oops, can't draw this
948                         }
949                         x += width_of[(int) ' '] * dw;
950                         continue;
951                 }
952                 // i points to the char after ^
953                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
954                 {
955                         ch = *text; // colors are ascii, so no u8_ needed
956                         if (ch <= '9' && ch >= '0') // ^[0-9] found
957                         {
958                                 colorindex = ch - '0';
959                                 ++text;
960                                 ++i;
961                                 continue;
962                         }
963                         // i points to the char after ^...
964                         // i+3 points to 3 in ^x123
965                         // i+3 == *maxlen would mean that char is missing
966                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
967                         {
968                                 // building colorindex...
969                                 ch = tolower(text[1]);
970                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
971                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
972                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
973                                 else tempcolorindex = 0;
974                                 if (tempcolorindex)
975                                 {
976                                         ch = tolower(text[2]);
977                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
978                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
979                                         else tempcolorindex = 0;
980                                         if (tempcolorindex)
981                                         {
982                                                 ch = tolower(text[3]);
983                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
984                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
985                                                 else tempcolorindex = 0;
986                                                 if (tempcolorindex)
987                                                 {
988                                                         colorindex = tempcolorindex | 0xf;
989                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
990                                                         i+=4;
991                                                         text += 4;
992                                                         continue;
993                                                 }
994                                         }
995                                 }
996                         }
997                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
998                         {
999                                 i++;
1000                                 text++;
1001                         }
1002                         i--;
1003                 }
1004                 ch = nextch;
1005
1006                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1007                 {
1008                         if (ch > 0xE000)
1009                                 ch -= 0xE000;
1010                         if (ch > 0xFF)
1011                                 continue;
1012                         if (fontmap)
1013                                 map = ft2_oldstyle_map;
1014                         prevch = 0;
1015                         if(!least_one || i0) // never skip the first character
1016                         if(x + width_of[ch] * dw > maxwidth)
1017                         {
1018                                 i = i0;
1019                                 break; // oops, can't draw this
1020                         }
1021                         x += width_of[ch] * dw;
1022                 } else {
1023                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1024                         {
1025                                 map = FontMap_FindForChar(fontmap, ch);
1026                                 if (!map)
1027                                 {
1028                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1029                                                 break;
1030                                         if (!map)
1031                                                 break;
1032                                 }
1033                         }
1034                         mapch = ch - map->start;
1035                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1036                                 x += kx * dw;
1037                         x += map->glyphs[mapch].advance_x * dw;
1038                         //prevmap = map;
1039                         prevch = ch;
1040                 }
1041         }
1042
1043         *maxlen = i;
1044
1045         if (outcolor)
1046                 *outcolor = colorindex;
1047
1048         return x;
1049 }
1050
1051 float DrawQ_Color[4];
1052 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1053 {
1054         int shadow, colorindex = STRING_COLOR_DEFAULT;
1055         size_t i;
1056         float x = startx, y, s, t, u, v, thisw;
1057         Uchar ch, mapch, nextch;
1058         Uchar prevch = 0; // used for kerning
1059         int tempcolorindex;
1060         int map_index = 0;
1061         //ft2_font_map_t *prevmap = NULL; // the previous map
1062         ft2_font_map_t *map = NULL;     // the currently used map
1063         ft2_font_map_t *fontmap = NULL; // the font map for the size
1064         float ftbase_y;
1065         const char *text_start = text;
1066         float kx, ky;
1067         ft2_font_t *ft2 = fnt->ft2;
1068         qboolean snap = true;
1069         float pix_x, pix_y;
1070         size_t bytes_left;
1071         float dw, dh;
1072         const float *width_of;
1073         dp_model_t *mod = CL_Mesh_UI();
1074         msurface_t *surf = NULL;
1075         int e0, e1, e2, e3;
1076         int tw, th;
1077         tw = Draw_GetPicWidth(fnt->pic);
1078         th = Draw_GetPicHeight(fnt->pic);
1079
1080         _DrawQ_Setup();
1081
1082         if (!h) h = w;
1083         if (!h) {
1084                 h = w = 1;
1085                 snap = false;
1086         }
1087
1088         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1089         w *= fnt->settings.scale;
1090         h *= fnt->settings.scale;
1091
1092         if (ft2 != NULL)
1093         {
1094                 if (snap)
1095                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1096                 else
1097                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1098                 fontmap = Font_MapForIndex(ft2, map_index);
1099         }
1100
1101         dw = w * sw;
1102         dh = h * sh;
1103
1104         // draw the font at its baseline when using freetype
1105         //ftbase_x = 0;
1106         ftbase_y = dh * (4.5/6.0);
1107
1108         if (maxlen < 1)
1109                 maxlen = 1<<30;
1110
1111         if(!r_draw2d.integer && !r_draw2d_force)
1112                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1113
1114         //ftbase_x = snap_to_pixel_x(ftbase_x);
1115         if(snap)
1116         {
1117                 startx = snap_to_pixel_x(startx, 0.4);
1118                 starty = snap_to_pixel_y(starty, 0.4);
1119                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1120         }
1121
1122         pix_x = vid.width / vid_conwidth.value;
1123         pix_y = vid.height / vid_conheight.value;
1124
1125         if (fontmap)
1126                 width_of = fontmap->width_of;
1127         else
1128                 width_of = fnt->width_of;
1129
1130         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1131         {
1132                 prevch = 0;
1133                 text = text_start;
1134
1135                 if (!outcolor || *outcolor == -1)
1136                         colorindex = STRING_COLOR_DEFAULT;
1137                 else
1138                         colorindex = *outcolor;
1139
1140                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1141
1142                 x = startx;
1143                 y = starty;
1144                 /*
1145                 if (shadow)
1146                 {
1147                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1148                         y += r_textshadow.value * vid.height / vid_conheight.value;
1149                 }
1150                 */
1151                 while (((bytes_left = maxlen - (text - text_start)) > 0) && *text)
1152                 {
1153                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1154                         i = text - text_start;
1155                         if (!ch)
1156                                 break;
1157                         if (ch == ' ' && !fontmap)
1158                         {
1159                                 x += width_of[(int) ' '] * dw;
1160                                 continue;
1161                         }
1162                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1163                         {
1164                                 ch = *text; // colors are ascii, so no u8_ needed
1165                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1166                                 {
1167                                         colorindex = ch - '0';
1168                                         DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1169                                         ++text;
1170                                         ++i;
1171                                         continue;
1172                                 }
1173                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1174                                 {
1175                                         // building colorindex...
1176                                         ch = tolower(text[1]);
1177                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1178                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1179                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1180                                         else tempcolorindex = 0;
1181                                         if (tempcolorindex)
1182                                         {
1183                                                 ch = tolower(text[2]);
1184                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1185                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1186                                                 else tempcolorindex = 0;
1187                                                 if (tempcolorindex)
1188                                                 {
1189                                                         ch = tolower(text[3]);
1190                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1191                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1192                                                         else tempcolorindex = 0;
1193                                                         if (tempcolorindex)
1194                                                         {
1195                                                                 colorindex = tempcolorindex | 0xf;
1196                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1197                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1198                                                                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1199                                                                 i+=4;
1200                                                                 text+=4;
1201                                                                 continue;
1202                                                         }
1203                                                 }
1204                                         }
1205                                 }
1206                                 else if (ch == STRING_COLOR_TAG)
1207                                 {
1208                                         i++;
1209                                         text++;
1210                                 }
1211                                 i--;
1212                         }
1213                         // get the backup
1214                         ch = nextch;
1215                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1216                         // this way we don't need to rebind fnt->tex for every old-style character
1217                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1218                         if (shadow)
1219                         {
1220                                 x += 1.0/pix_x * r_textshadow.value;
1221                                 y += 1.0/pix_y * r_textshadow.value;
1222                         }
1223                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1224                         {
1225                                 if (ch >= 0xE000)
1226                                         ch -= 0xE000;
1227                                 if (ch > 0xFF)
1228                                         goto out;
1229                                 if (fontmap)
1230                                         map = ft2_oldstyle_map;
1231                                 prevch = 0;
1232                                 //num = (unsigned char) text[i];
1233                                 //thisw = fnt->width_of[num];
1234                                 thisw = fnt->width_of[ch];
1235                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1236                                 if (r_nearest_conchars.integer)
1237                                 {
1238                                         s = (ch & 15)*0.0625f;
1239                                         t = (ch >> 4)*0.0625f;
1240                                         u = 0.0625f * thisw;
1241                                         v = 0.0625f;
1242                                 }
1243                                 else
1244                                 {
1245                                         s = (ch & 15)*0.0625f + (0.5f / tw);
1246                                         t = (ch >> 4)*0.0625f + (0.5f / th);
1247                                         u = 0.0625f * thisw - (1.0f / tw);
1248                                         v = 0.0625f - (1.0f / th);
1249                                 }
1250                                 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, fnt->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1251                                 e0 = Mod_Mesh_IndexForVertex(mod, surf, x         , y   , 10, 0, 0, -1, s  , t  , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1252                                 e1 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y   , 10, 0, 0, -1, s+u, t  , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1253                                 e2 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y+dh, 10, 0, 0, -1, s+u, t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1254                                 e3 = Mod_Mesh_IndexForVertex(mod, surf, x         , y+dh, 10, 0, 0, -1, s  , t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1255                                 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1256                                 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1257                                 x += width_of[ch] * dw;
1258                         } else {
1259                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1260                                 {
1261                                         // find the new map
1262                                         map = FontMap_FindForChar(fontmap, ch);
1263                                         if (!map)
1264                                         {
1265                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1266                                                 {
1267                                                         shadow = -1;
1268                                                         break;
1269                                                 }
1270                                                 if (!map)
1271                                                 {
1272                                                         // this shouldn't happen
1273                                                         shadow = -1;
1274                                                         break;
1275                                                 }
1276                                         }
1277                                 }
1278
1279                                 mapch = ch - map->start;
1280                                 thisw = map->glyphs[mapch].advance_x;
1281
1282                                 //x += ftbase_x;
1283                                 y += ftbase_y;
1284                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1285                                 {
1286                                         x += kx * dw;
1287                                         y += ky * dh;
1288                                 }
1289                                 else
1290                                         kx = ky = 0;
1291                                 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, map->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1292                                 e0 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1293                                 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1294                                 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1295                                 e3 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1296                                 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1297                                 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1298                                 //x -= ftbase_x;
1299                                 y -= ftbase_y;
1300
1301                                 x += thisw * dw;
1302
1303                                 //prevmap = map;
1304                                 prevch = ch;
1305                         }
1306 out:
1307                         if (shadow)
1308                         {
1309                                 x -= 1.0/pix_x * r_textshadow.value;
1310                                 y -= 1.0/pix_y * r_textshadow.value;
1311                         }
1312                 }
1313         }
1314
1315         if (outcolor)
1316                 *outcolor = colorindex;
1317         
1318         // note: this relies on the proper text (not shadow) being drawn last
1319         return x;
1320 }
1321
1322 float DrawQ_String(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)
1323 {
1324         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1325 }
1326
1327 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1328 {
1329         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1330 }
1331
1332 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1333 {
1334         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1335 }
1336
1337 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1338 {
1339         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1340 }
1341
1342 #if 0
1343 // not used
1344 // no ^xrgb management
1345 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1346 {
1347         int color, numchars = 0;
1348         char *outputend2c = output2c + maxoutchars - 2;
1349         if (!outcolor || *outcolor == -1)
1350                 color = STRING_COLOR_DEFAULT;
1351         else
1352                 color = *outcolor;
1353         if (!maxreadchars)
1354                 maxreadchars = 1<<30;
1355         textend = text + maxreadchars;
1356         while (text != textend && *text)
1357         {
1358                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1359                 {
1360                         if (text[1] == STRING_COLOR_TAG)
1361                                 text++;
1362                         else if (text[1] >= '0' && text[1] <= '9')
1363                         {
1364                                 color = text[1] - '0';
1365                                 text += 2;
1366                                 continue;
1367                         }
1368                 }
1369                 if (output2c >= outputend2c)
1370                         break;
1371                 *output2c++ = *text++;
1372                 *output2c++ = color;
1373                 numchars++;
1374         }
1375         output2c[0] = output2c[1] = 0;
1376         if (outcolor)
1377                 *outcolor = color;
1378         return numchars;
1379 }
1380 #endif
1381
1382 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)
1383 {
1384         dp_model_t *mod = CL_Mesh_UI();
1385         msurface_t *surf;
1386         int e0, e1, e2, e3;
1387         _DrawQ_Setup();
1388         if (!pic)
1389                 pic = Draw_CachePic("white");
1390         if (width == 0)
1391                 width = pic->width;
1392         if (height == 0)
1393                 height = pic->height;
1394         surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
1395         e0 = Mod_Mesh_IndexForVertex(mod, surf, x        , y         , 0, 0, 0, -1, s1, t1, 0, 0, r1, g1, b1, a1);
1396         e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y         , 0, 0, 0, -1, s2, t2, 0, 0, r2, g2, b2, a2);
1397         e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, s4, t4, 0, 0, r4, g4, b4, a4);
1398         e3 = Mod_Mesh_IndexForVertex(mod, surf, x        , y + height, 0, 0, 0, -1, s3, t3, 0, 0, r3, g3, b3, a3);
1399         Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1400         Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1401 }
1402
1403 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1404 {
1405         dp_model_t *mod = CL_Mesh_UI();
1406         msurface_t *surf;
1407         int e0, e1, e2, e3;
1408         float offsetx, offsety;
1409         _DrawQ_Setup();
1410         // width is measured in real pixels
1411         if (fabs(x2 - x1) > fabs(y2 - y1))
1412         {
1413                 offsetx = 0;
1414                 offsety = width * vid_conheight.value / vid.height;
1415         }
1416         else
1417         {
1418                 offsetx = width * vid_conwidth.value / vid.width;
1419                 offsety = 0;
1420         }
1421         surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_VERTEXCOLOR), true);
1422         e0 = Mod_Mesh_IndexForVertex(mod, surf, x1 - offsetx, y1 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1423         e1 = Mod_Mesh_IndexForVertex(mod, surf, x2 - offsetx, y2 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1424         e2 = Mod_Mesh_IndexForVertex(mod, surf, x2 + offsetx, y2 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1425         e3 = Mod_Mesh_IndexForVertex(mod, surf, x1 + offsetx, y1 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1426         Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1427         Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1428 }
1429
1430 void DrawQ_SetClipArea(float x, float y, float width, float height)
1431 {
1432         int ix, iy, iw, ih;
1433         _DrawQ_Setup();
1434         DrawQ_FlushUI();
1435
1436         // We have to convert the con coords into real coords
1437         // OGL uses bottom to top (origin is in bottom left)
1438         ix = (int)(0.5 + x * ((float)r_refdef.view.width / vid_conwidth.integer)) + r_refdef.view.x;
1439         iy = (int)(0.5 + y * ((float)r_refdef.view.height / vid_conheight.integer)) + r_refdef.view.y;
1440         iw = (int)(0.5 + width * ((float)r_refdef.view.width / vid_conwidth.integer));
1441         ih = (int)(0.5 + height * ((float)r_refdef.view.height / vid_conheight.integer));
1442         switch(vid.renderpath)
1443         {
1444         case RENDERPATH_GL11:
1445         case RENDERPATH_GL13:
1446         case RENDERPATH_GL20:
1447         case RENDERPATH_GLES1:
1448         case RENDERPATH_GLES2:
1449         case RENDERPATH_SOFT:
1450                 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1451                 break;
1452         case RENDERPATH_D3D9:
1453                 GL_Scissor(ix, iy, iw, ih);
1454                 break;
1455         case RENDERPATH_D3D10:
1456                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1457                 break;
1458         case RENDERPATH_D3D11:
1459                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1460                 break;
1461         }
1462
1463         GL_ScissorTest(true);
1464 }
1465
1466 void DrawQ_ResetClipArea(void)
1467 {
1468         DrawQ_FlushUI();
1469         _DrawQ_Setup();
1470         GL_ScissorTest(false);
1471 }
1472
1473 void DrawQ_Finish(void)
1474 {
1475         DrawQ_FlushUI();
1476         r_refdef.draw2dstage = 0;
1477 }
1478
1479 void DrawQ_RecalcView(void)
1480 {
1481         DrawQ_FlushUI();
1482         if(r_refdef.draw2dstage)
1483                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1484 }
1485
1486 void DrawQ_FlushUI(void)
1487 {
1488         int i;
1489         dp_model_t *mod = CL_Mesh_UI();
1490         if (mod->num_surfaces == 0)
1491                 return;
1492
1493         if (!r_draw2d.integer && !r_draw2d_force)
1494         {
1495                 Mod_Mesh_Reset(mod);
1496                 return;
1497         }
1498
1499         // TODO: render the mesh using R_Q1BSP_Draw or similar, for full material support.
1500         GL_DepthMask(false);
1501         R_Mesh_PrepareVertices_Generic_Arrays(mod->surfmesh.num_vertices, mod->surfmesh.data_vertex3f, mod->surfmesh.data_lightmapcolor4f, mod->surfmesh.data_texcoordtexture2f);
1502         for (i = 0; i < mod->num_surfaces; i++)
1503         {
1504                 msurface_t *surf = mod->data_surfaces + i;
1505                 texture_t *tex = surf->texture;
1506                 if (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
1507                         GL_BlendFunc(tex->customblendfunc[0], tex->customblendfunc[1]);
1508                 else if (tex->currentmaterialflags & MATERIALFLAG_ADD)
1509                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1510                 else if (tex->currentmaterialflags & MATERIALFLAG_ALPHA)
1511                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1512                 else
1513                         GL_BlendFunc(GL_ONE, GL_ZERO);
1514                 R_SetupShader_Generic(tex->currentskinframe->base, NULL, GL_MODULATE, 1, (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND) ? false : true, true, false);
1515                 R_Mesh_Draw(surf->num_firstvertex, surf->num_vertices, surf->num_firsttriangle, surf->num_triangles, mod->surfmesh.data_element3i, NULL, 0, mod->surfmesh.data_element3s, NULL, 0);
1516         }
1517
1518         Mod_Mesh_Reset(mod);
1519 }