]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
UNMERGE
[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 #include "cl_dyntexture.h"
27
28 #include "ft2.h"
29 #include "ft2_fontdefs.h"
30
31 dp_fonts_t dp_fonts;
32 static mempool_t *fonts_mempool = NULL;
33
34 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)"};
35 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)"};
36 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)"};
37
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
45
46 extern cvar_t v_glslgamma;
47
48 //=============================================================================
49 /* Support Routines */
50
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
55
56 static rtexturepool_t *drawtexturepool;
57
58 static const unsigned char concharimage[FONT_FILESIZE] =
59 {
60 #include "lhfont.h"
61 };
62
63 static rtexture_t *draw_generateconchars(void)
64 {
65         int i;
66         unsigned char *data;
67         double random;
68         rtexture_t *tex;
69
70         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
71 // Gold numbers
72         for (i = 0;i < 8192;i++)
73         {
74                 random = lhrandom (0.0,1.0);
75                 data[i*4+3] = data[i*4+0];
76                 data[i*4+2] = 83 + (unsigned char)(random * 64);
77                 data[i*4+1] = 71 + (unsigned char)(random * 32);
78                 data[i*4+0] = 23 + (unsigned char)(random * 16);
79         }
80 // White chars
81         for (i = 8192;i < 32768;i++)
82         {
83                 random = lhrandom (0.0,1.0);
84                 data[i*4+3] = data[i*4+0];
85                 data[i*4+2] = 95 + (unsigned char)(random * 64);
86                 data[i*4+1] = 95 + (unsigned char)(random * 64);
87                 data[i*4+0] = 95 + (unsigned char)(random * 64);
88         }
89 // Gold numbers
90         for (i = 32768;i < 40960;i++)
91         {
92                 random = lhrandom (0.0,1.0);
93                 data[i*4+3] = data[i*4+0];
94                 data[i*4+2] = 83 + (unsigned char)(random * 64);
95                 data[i*4+1] = 71 + (unsigned char)(random * 32);
96                 data[i*4+0] = 23 + (unsigned char)(random * 16);
97         }
98 // Red chars
99         for (i = 40960;i < 65536;i++)
100         {
101                 random = lhrandom (0.0,1.0);
102                 data[i*4+3] = data[i*4+0];
103                 data[i*4+2] = 96 + (unsigned char)(random * 64);
104                 data[i*4+1] = 43 + (unsigned char)(random * 32);
105                 data[i*4+0] = 27 + (unsigned char)(random * 32);
106         }
107
108 #if 0
109         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
110 #endif
111
112         tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
113         Mem_Free(data);
114         return tex;
115 }
116
117 static rtexture_t *draw_generateditherpattern(void)
118 {
119         int x, y;
120         unsigned char pixels[8][8];
121         for (y = 0;y < 8;y++)
122                 for (x = 0;x < 8;x++)
123                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
125 }
126
127 typedef struct embeddedpic_s
128 {
129         const char *name;
130         int width;
131         int height;
132         const char *pixels;
133 }
134 embeddedpic_t;
135
136 static const embeddedpic_t embeddedpics[] =
137 {
138         {
139         "gfx/prydoncursor001", 16, 16,
140         "477777774......."
141         "77.....6........"
142         "7.....6........."
143         "7....6.........."
144         "7.....6........."
145         "7..6...6........"
146         "7.6.6...6......."
147         "76...6...6......"
148         "4.....6.6......."
149         ".......6........"
150         "................"
151         "................"
152         "................"
153         "................"
154         "................"
155         "................"
156         },
157         {
158         "ui/mousepointer", 16, 16,
159         "477777774......."
160         "77.....6........"
161         "7.....6........."
162         "7....6.........."
163         "7.....6........."
164         "7..6...6........"
165         "7.6.6...6......."
166         "76...6...6......"
167         "4.....6.6......."
168         ".......6........"
169         "................"
170         "................"
171         "................"
172         "................"
173         "................"
174         "................"
175         },
176         {
177         "gfx/crosshair1", 16, 16,
178         "................"
179         "................"
180         "................"
181         "...33......33..."
182         "...355....553..."
183         "....577..775...."
184         ".....77..77....."
185         "................"
186         "................"
187         ".....77..77....."
188         "....577..775...."
189         "...355....553..."
190         "...33......33..."
191         "................"
192         "................"
193         "................"
194         },
195         {
196         "gfx/crosshair2", 16, 16,
197         "................"
198         "................"
199         "................"
200         "...3........3..."
201         "....5......5...."
202         ".....7....7....."
203         "......7..7......"
204         "................"
205         "................"
206         "......7..7......"
207         ".....7....7....."
208         "....5......5...."
209         "...3........3..."
210         "................"
211         "................"
212         "................"
213         },
214         {
215         "gfx/crosshair3", 16, 16,
216         "................"
217         ".......77......."
218         ".......77......."
219         "................"
220         "................"
221         ".......44......."
222         ".......44......."
223         ".77..44..44..77."
224         ".77..44..44..77."
225         ".......44......."
226         ".......44......."
227         "................"
228         "................"
229         ".......77......."
230         ".......77......."
231         "................"
232         },
233         {
234         "gfx/crosshair4", 16, 16,
235         "................"
236         "................"
237         "................"
238         "................"
239         "................"
240         "................"
241         "................"
242         "................"
243         "........7777777."
244         "........752....."
245         "........72......"
246         "........7......."
247         "........7......."
248         "........7......."
249         "........7......."
250         "................"
251         },
252         {
253         "gfx/crosshair5", 8, 8,
254         "........"
255         "........"
256         "....7..."
257         "........"
258         "..7.7.7."
259         "........"
260         "....7..."
261         "........"
262         },
263         {
264         "gfx/crosshair6", 2, 2,
265         "77"
266         "77"
267         },
268         {
269         "gfx/crosshair7", 16, 16,
270         "................"
271         ".3............3."
272         "..5...2332...5.."
273         "...7.3....3.7..."
274         "....7......7...."
275         "...3.7....7.3..."
276         "..2...7..7...2.."
277         "..3..........3.."
278         "..3..........3.."
279         "..2...7..7...2.."
280         "...3.7....7.3..."
281         "....7......7...."
282         "...7.3....3.7..."
283         "..5...2332...5.."
284         ".3............3."
285         "................"
286         },
287         {NULL, 0, 0, NULL}
288 };
289
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
291 {
292         const embeddedpic_t *p;
293         for (p = embeddedpics;p->name;p++)
294                 if (!strcmp(name, p->name))
295                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296         if (!strcmp(name, "gfx/conchars"))
297                 return draw_generateconchars();
298         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299                 return draw_generateditherpattern();
300         if (!quiet)
301                 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302         return r_texture_notexture;
303 }
304
305
306 /*
307 ================
308 Draw_CachePic
309 ================
310 */
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
313 {
314         int crc, hashkey;
315         unsigned char *pixels;
316         cachepic_t *pic;
317         fs_offset_t lmpsize;
318         unsigned char *lmpdata;
319         char lmpname[MAX_QPATH];
320         int texflags;
321         int j;
322
323         texflags = TEXF_ALPHA;
324         if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
325                 texflags |= TEXF_CLAMP;
326         if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
327                 texflags |= TEXF_COMPRESS;
328
329         // check whether the picture has already been cached
330         crc = CRC_Block((unsigned char *)path, strlen(path));
331         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
332         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
333                 if (!strcmp (path, pic->name))
334                         if(!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
335                         {
336                                 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
337                                         pic->autoload = false; // persist it
338                                 return pic;
339                         }
340
341         if (numcachepics == MAX_CACHED_PICS)
342         {
343                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
344                 // FIXME: support NULL in callers?
345                 return cachepics; // return the first one
346         }
347         pic = cachepics + (numcachepics++);
348         strlcpy (pic->name, path, sizeof(pic->name));
349         // link into list
350         pic->chain = cachepichash[hashkey];
351         cachepichash[hashkey] = pic;
352
353         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
354         pic->tex = CL_GetDynTexture( path );
355         // if so, set the width/height, too
356         if( pic->tex ) {
357                 pic->width = R_TextureWidth(pic->tex);
358                 pic->height = R_TextureHeight(pic->tex);
359                 // we're done now (early-out)
360                 return pic;
361         }
362
363         pic->hasalpha = true; // assume alpha unless we know it has none
364         pic->texflags = texflags;
365         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
366
367         // load a high quality image from disk if possible
368         pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer, NULL);
369         if (pixels == NULL && !strncmp(path, "gfx/", 4))
370                 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, NULL);
371         if (pixels)
372         {
373                 pic->hasalpha = false;
374                 if (pic->texflags & TEXF_ALPHA)
375                 {
376                         for (j = 3;j < image_width * image_height * 4;j += 4)
377                         {
378                                 if (pixels[j] < 255)
379                                 {
380                                         pic->hasalpha = true;
381                                         break;
382                                 }
383                         }
384                 }
385
386                 pic->width = image_width;
387                 pic->height = image_height;
388                 if (!pic->autoload)
389                         pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
390         }
391         else
392         {
393                 pic->autoload = false;
394                 // never compress the fallback images
395                 pic->texflags &= ~TEXF_COMPRESS;
396         }
397
398         // now read the low quality version (wad or lmp file), and take the pic
399         // size from that even if we don't upload the texture, this way the pics
400         // show up the right size in the menu even if they were replaced with
401         // higher or lower resolution versions
402         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
403         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
404         {
405                 if (developer_loading.integer)
406                         Con_Printf("loading lump \"%s\"\n", path);
407
408                 if (lmpsize >= 9)
409                 {
410                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
411                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
412                         // if no high quality replacement image was found, upload the original low quality texture
413                         if (!pixels)
414                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
415                 }
416                 Mem_Free(lmpdata);
417         }
418         else if ((lmpdata = W_GetLumpName (path + 4)))
419         {
420                 if (developer_loading.integer)
421                         Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
422
423                 if (!strcmp(path, "gfx/conchars"))
424                 {
425                         // conchars is a raw image and with color 0 as transparent instead of 255
426                         pic->width = 128;
427                         pic->height = 128;
428                         // if no high quality replacement image was found, upload the original low quality texture
429                         if (!pixels)
430                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
431                 }
432                 else
433                 {
434                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
435                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
436                         // if no high quality replacement image was found, upload the original low quality texture
437                         if (!pixels)
438                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
439                 }
440         }
441
442         if (pixels)
443         {
444                 Mem_Free(pixels);
445                 pixels = NULL;
446         }
447         else if (pic->tex == NULL)
448         {
449                 // if it's not found on disk, generate an image
450                 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
451                 pic->width = R_TextureWidth(pic->tex);
452                 pic->height = R_TextureHeight(pic->tex);
453         }
454
455         return pic;
456 }
457
458 cachepic_t *Draw_CachePic (const char *path)
459 {
460         return Draw_CachePic_Flags (path, 0); // default to persistent!
461 }
462
463 int draw_frame = 1;
464
465 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
466 {
467         if (pic->autoload && !pic->tex)
468         {
469                 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
470                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
471                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
472                 if (pic->tex == NULL)
473                         pic->tex = draw_generatepic(pic->name, true);
474         }
475         pic->lastusedframe = draw_frame;
476         return pic->tex;
477 }
478
479 void Draw_Frame(void)
480 {
481         int i;
482         cachepic_t *pic;
483         static double nextpurgetime;
484         if (nextpurgetime > realtime)
485                 return;
486         nextpurgetime = realtime + 0.05;
487         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
488         {
489                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
490                 {
491                         R_FreeTexture(pic->tex);
492                         pic->tex = NULL;
493                 }
494         }
495         draw_frame++;
496 }
497
498 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
499 {
500         int crc, hashkey;
501         cachepic_t *pic;
502
503         crc = CRC_Block((unsigned char *)picname, strlen(picname));
504         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
505         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
506                 if (!strcmp (picname, pic->name))
507                         break;
508
509         if (pic)
510         {
511                 if (pic->tex && pic->width == width && pic->height == height)
512                 {
513                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
514                         return pic;
515                 }
516         }
517         else
518         {
519                 if (pic == NULL)
520                 {
521                         if (numcachepics == MAX_CACHED_PICS)
522                         {
523                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
524                                 // FIXME: support NULL in callers?
525                                 return cachepics; // return the first one
526                         }
527                         pic = cachepics + (numcachepics++);
528                         strlcpy (pic->name, picname, sizeof(pic->name));
529                         // link into list
530                         pic->chain = cachepichash[hashkey];
531                         cachepichash[hashkey] = pic;
532                 }
533         }
534
535         pic->width = width;
536         pic->height = height;
537         if (pic->tex)
538                 R_FreeTexture(pic->tex);
539         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
540         return pic;
541 }
542
543 void Draw_FreePic(const char *picname)
544 {
545         int crc;
546         int hashkey;
547         cachepic_t *pic;
548         // this doesn't really free the pic, but does free it's texture
549         crc = CRC_Block((unsigned char *)picname, strlen(picname));
550         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
551         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
552         {
553                 if (!strcmp (picname, pic->name) && pic->tex)
554                 {
555                         R_FreeTexture(pic->tex);
556                         pic->tex = NULL;
557                         pic->width = 0;
558                         pic->height = 0;
559                         return;
560                 }
561         }
562 }
563
564 static float snap_to_pixel_x(float x, float roundUpAt);
565 extern int con_linewidth; // to force rewrapping
566 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
567 {
568         int i, ch;
569         float maxwidth;
570         char widthfile[MAX_QPATH];
571         char *widthbuf;
572         fs_offset_t widthbufsize;
573
574         if(override || !fnt->texpath[0])
575         {
576                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
577                 // load the cvars when the font is FIRST loader
578                 fnt->settings.scale = scale;
579                 fnt->settings.voffset = voffset;
580                 fnt->settings.antialias = r_font_antialias.integer;
581                 fnt->settings.hinting = r_font_hinting.integer;
582                 fnt->settings.outline = r_font_postprocess_outline.value;
583                 fnt->settings.blur = r_font_postprocess_blur.value;
584                 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
585                 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
586                 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
587         }
588         // fix bad scale
589         if (fnt->settings.scale <= 0)
590                 fnt->settings.scale = 1;
591
592         if(drawtexturepool == NULL)
593                 return; // before gl_draw_start, so will be loaded later
594
595         if(fnt->ft2)
596         {
597                 // clear freetype font
598                 Font_UnloadFont(fnt->ft2);
599                 Mem_Free(fnt->ft2);
600                 fnt->ft2 = NULL;
601         }
602
603         if(fnt->req_face != -1)
604         {
605                 if(!Font_LoadFont(fnt->texpath, fnt))
606                         Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
607         }
608
609         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
610         if(fnt->tex == r_texture_notexture)
611         {
612                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
613                 {
614                         if (!fnt->fallbacks[i][0])
615                                 break;
616                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
617                         if(fnt->tex != r_texture_notexture)
618                                 break;
619                 }
620                 if(fnt->tex == r_texture_notexture)
621                 {
622                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
623                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
624                 }
625                 else
626                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
627         }
628         else
629                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
630
631         // unspecified width == 1 (base width)
632         for(ch = 0; ch < 256; ++ch)
633                 fnt->width_of[ch] = 1;
634
635         // FIXME load "name.width", if it fails, fill all with 1
636         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
637         {
638                 float extraspacing = 0;
639                 const char *p = widthbuf;
640
641                 ch = 0;
642                 while(ch < 256)
643                 {
644                         if(!COM_ParseToken_Simple(&p, false, false))
645                                 return;
646
647                         switch(*com_token)
648                         {
649                                 case '0':
650                                 case '1':
651                                 case '2':
652                                 case '3':
653                                 case '4':
654                                 case '5':
655                                 case '6':
656                                 case '7':
657                                 case '8':
658                                 case '9':
659                                 case '+':
660                                 case '-':
661                                 case '.':
662                                         fnt->width_of[ch] = atof(com_token) + extraspacing;
663                                         ch++;
664                                         break;
665                                 default:
666                                         if(!strcmp(com_token, "extraspacing"))
667                                         {
668                                                 if(!COM_ParseToken_Simple(&p, false, false))
669                                                         return;
670                                                 extraspacing = atof(com_token);
671                                         }
672                                         else if(!strcmp(com_token, "scale"))
673                                         {
674                                                 if(!COM_ParseToken_Simple(&p, false, false))
675                                                         return;
676                                                 fnt->settings.scale = atof(com_token);
677                                         }
678                                         else
679                                         {
680                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
681                                                 if(!COM_ParseToken_Simple(&p, false, false))
682                                                         return;
683                                         }
684                                         break;
685                         }
686                 }
687
688                 Mem_Free(widthbuf);
689         }
690
691         if(fnt->ft2)
692         {
693                 for (i = 0; i < MAX_FONT_SIZES; ++i)
694                 {
695                         ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
696                         if (!map)
697                                 break;
698                         for(ch = 0; ch < 256; ++ch)
699                                 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
700                 }
701         }
702
703         maxwidth = fnt->width_of[0];
704         for(i = 1; i < 256; ++i)
705                 maxwidth = max(maxwidth, fnt->width_of[i]);
706         fnt->maxwidth = maxwidth;
707
708         // fix up maxwidth for overlap
709         fnt->maxwidth *= fnt->settings.scale;
710
711         if(fnt == FONT_CONSOLE)
712                 con_linewidth = -1; // rewrap console in next frame
713 }
714
715 extern cvar_t developer_font;
716 dp_font_t *FindFont(const char *title, qboolean allocate_new)
717 {
718         int i;
719
720         // find font
721         for(i = 0; i < dp_fonts.maxsize; ++i)
722                 if(!strcmp(dp_fonts.f[i].title, title))
723                         return &dp_fonts.f[i];
724         // if not found - try allocate
725         if (allocate_new)
726         {
727                 // find any font with empty title
728                 for(i = 0; i < dp_fonts.maxsize; ++i)
729                 {
730                         if(!strcmp(dp_fonts.f[i].title, ""))
731                         {
732                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
733                                 return &dp_fonts.f[i];
734                         }
735                 }
736                 // if no any 'free' fonts - expand buffer
737                 i = dp_fonts.maxsize;
738                 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
739                 if (developer_font.integer)
740                         Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
741                 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
742                 // register a font in first expanded slot
743                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
744                 return &dp_fonts.f[i];
745         }
746         return NULL;
747 }
748
749 static float snap_to_pixel_x(float x, float roundUpAt)
750 {
751         float pixelpos = x * vid.width / vid_conwidth.value;
752         int snap = (int) pixelpos;
753         if (pixelpos - snap >= roundUpAt) ++snap;
754         return ((float)snap * vid_conwidth.value / vid.width);
755         /*
756         x = (int)(x * vid.width / vid_conwidth.value);
757         x = (x * vid_conwidth.value / vid.width);
758         return x;
759         */
760 }
761
762 static float snap_to_pixel_y(float y, float roundUpAt)
763 {
764         float pixelpos = y * vid.height / vid_conheight.value;
765         int snap = (int) pixelpos;
766         if (pixelpos - snap > roundUpAt) ++snap;
767         return ((float)snap * vid_conheight.value / vid.height);
768         /*
769         y = (int)(y * vid.height / vid_conheight.value);
770         y = (y * vid_conheight.value / vid.height);
771         return y;
772         */
773 }
774
775 static void LoadFont_f(void)
776 {
777         dp_font_t *f;
778         int i, sizes;
779         const char *filelist, *c, *cm;
780         float sz, scale, voffset;
781         char mainfont[MAX_QPATH];
782
783         if(Cmd_Argc() < 2)
784         {
785                 Con_Printf("Available font commands:\n");
786                 for(i = 0; i < dp_fonts.maxsize; ++i)
787                         if (dp_fonts.f[i].title[0])
788                                 Con_Printf("  loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
789                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
790                            "can specify multiple fonts and faces\n"
791                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
792                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
793                            "of gfx/fallback as fallback font.\n"
794                            "You can also specify a list of font sizes to load, like this:\n"
795                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
796                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
797                            "custom switches:\n"
798                            " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
799                            " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
800                         );
801                 return;
802         }
803         f = FindFont(Cmd_Argv(1), true);
804         if(f == NULL)
805         {
806                 Con_Printf("font function not found\n");
807                 return;
808         }
809
810         if(Cmd_Argc() < 3)
811                 filelist = "gfx/conchars";
812         else
813                 filelist = Cmd_Argv(2);
814
815         memset(f->fallbacks, 0, sizeof(f->fallbacks));
816         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
817
818         // first font is handled "normally"
819         c = strchr(filelist, ':');
820         cm = strchr(filelist, ',');
821         if(c && (!cm || c < cm))
822                 f->req_face = atoi(c+1);
823         else
824         {
825                 f->req_face = 0;
826                 c = cm;
827         }
828
829         if(!c || (c - filelist) > MAX_QPATH)
830                 strlcpy(mainfont, filelist, sizeof(mainfont));
831         else
832         {
833                 memcpy(mainfont, filelist, c - filelist);
834                 mainfont[c - filelist] = 0;
835         }
836
837         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
838         {
839                 c = strchr(filelist, ',');
840                 if(!c)
841                         break;
842                 filelist = c + 1;
843                 if(!*filelist)
844                         break;
845                 c = strchr(filelist, ':');
846                 cm = strchr(filelist, ',');
847                 if(c && (!cm || c < cm))
848                         f->fallback_faces[i] = atoi(c+1);
849                 else
850                 {
851                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
852                         c = cm;
853                 }
854                 if(!c || (c-filelist) > MAX_QPATH)
855                 {
856                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
857                 }
858                 else
859                 {
860                         memcpy(f->fallbacks[i], filelist, c - filelist);
861                         f->fallbacks[i][c - filelist] = 0;
862                 }
863         }
864
865         // for now: by default load only one size: the default size
866         f->req_sizes[0] = 0;
867         for(i = 1; i < MAX_FONT_SIZES; ++i)
868                 f->req_sizes[i] = -1;
869
870         scale = 1;
871         voffset = 0;
872         if(Cmd_Argc() >= 4)
873         {
874                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
875                 {
876                         // special switches
877                         if (!strcmp(Cmd_Argv(i), "scale"))
878                         {
879                                 i++;
880                                 if (i < Cmd_Argc())
881                                         scale = atof(Cmd_Argv(i));
882                                 continue;
883                         }
884                         if (!strcmp(Cmd_Argv(i), "voffset"))
885                         {
886                                 i++;
887                                 if (i < Cmd_Argc())
888                                         voffset = atof(Cmd_Argv(i));
889                                 continue;
890                         }
891                         // parse one of sizes
892                         sz = atof(Cmd_Argv(i));
893                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
894                         {
895                                 f->req_sizes[sizes] = sz;
896                                 sizes++;
897                         }
898                 }
899         }
900
901         LoadFont(true, mainfont, f, scale, voffset);
902 }
903
904 /*
905 ===============
906 Draw_Init
907 ===============
908 */
909 static void gl_draw_start(void)
910 {
911         int i;
912         drawtexturepool = R_AllocTexturePool();
913
914         numcachepics = 0;
915         memset(cachepichash, 0, sizeof(cachepichash));
916
917         font_start();
918
919         // load default font textures
920         for(i = 0; i < dp_fonts.maxsize; ++i)
921                 if (dp_fonts.f[i].title[0])
922                         LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
923
924         // draw the loading screen so people have something to see in the newly opened window
925         SCR_UpdateLoadingScreen(true);
926 }
927
928 static void gl_draw_shutdown(void)
929 {
930         font_shutdown();
931
932         R_FreeTexturePool(&drawtexturepool);
933
934         numcachepics = 0;
935         memset(cachepichash, 0, sizeof(cachepichash));
936 }
937
938 static void gl_draw_newmap(void)
939 {
940         font_newmap();
941 }
942
943 void GL_Draw_Init (void)
944 {
945         int i, j;
946
947         Cvar_RegisterVariable(&r_font_postprocess_blur);
948         Cvar_RegisterVariable(&r_font_postprocess_outline);
949         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
950         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
951         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
952         Cvar_RegisterVariable(&r_font_hinting);
953         Cvar_RegisterVariable(&r_font_antialias);
954         Cvar_RegisterVariable(&r_textshadow);
955         Cvar_RegisterVariable(&r_textbrightness);
956         Cvar_RegisterVariable(&r_textcontrast);
957
958         // allocate fonts storage
959         fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
960         dp_fonts.maxsize = MAX_FONTS;
961         dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
962         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
963
964         // assign starting font names
965         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
966         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
967         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
968         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
969         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
970         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
971         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
972         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
973         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
974         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
975                 if(!FONT_USER(i)->title[0])
976                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
977
978         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
979         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
980 }
981
982 static void _DrawQ_Setup(void)
983 {
984         r_viewport_t viewport;
985         if (r_refdef.draw2dstage == 1)
986                 return;
987         r_refdef.draw2dstage = 1;
988         CHECKGLERROR
989         R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
990         R_SetViewport(&viewport);
991         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
992         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
993         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
994         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
995         R_EntityMatrix(&identitymatrix);
996
997         GL_DepthRange(0, 1);
998         GL_PolygonOffset(0, 0);
999         GL_DepthTest(false);
1000         GL_Color(1,1,1,1);
1001         GL_AlphaTest(false);
1002 }
1003
1004 qboolean r_draw2d_force = false;
1005 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1006 {
1007         _DrawQ_Setup();
1008         CHECKGLERROR
1009         if(!r_draw2d.integer && !r_draw2d_force)
1010                 return;
1011         DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1012 }
1013 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1014 {
1015         if(flags == DRAWFLAG_ADDITIVE)
1016         {
1017                 GL_DepthMask(false);
1018                 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1019         }
1020         else if(flags == DRAWFLAG_MODULATE)
1021         {
1022                 GL_DepthMask(false);
1023                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1024         }
1025         else if(flags == DRAWFLAG_2XMODULATE)
1026         {
1027                 GL_DepthMask(false);
1028                 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1029         }
1030         else if(flags == DRAWFLAG_SCREEN)
1031         {
1032                 GL_DepthMask(false);
1033                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1034         }
1035         else if(alpha)
1036         {
1037                 GL_DepthMask(false);
1038                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1039         }
1040         else
1041         {
1042                 GL_DepthMask(true);
1043                 GL_BlendFunc(GL_ONE, GL_ZERO);
1044         }
1045 }
1046
1047 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1048 {
1049         float floats[20];
1050
1051         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1052         if(!r_draw2d.integer && !r_draw2d_force)
1053                 return;
1054         GL_Color(red, green, blue, alpha);
1055
1056         R_Mesh_VertexPointer(floats, 0, 0);
1057         R_Mesh_ColorPointer(NULL, 0, 0);
1058         R_Mesh_ResetTextureState();
1059         if (pic)
1060         {
1061                 if (width == 0)
1062                         width = pic->width;
1063                 if (height == 0)
1064                         height = pic->height;
1065                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1066                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1067
1068 #if 1
1069                 floats[12] = 0.0f;floats[13] = 0.0f;
1070                 floats[14] = 1.0f;floats[15] = 0.0f;
1071                 floats[16] = 1.0f;floats[17] = 1.0f;
1072                 floats[18] = 0.0f;floats[19] = 1.0f;
1073 #else
1074       // AK07: lets be texel correct on the corners
1075       {
1076          float horz_offset = 0.5f / pic->width;
1077          float vert_offset = 0.5f / pic->height;
1078
1079                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1080                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1081                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1082                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1083       }
1084 #endif
1085         }
1086         else
1087                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1088
1089         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1090         floats[0] = floats[9] = x;
1091         floats[1] = floats[4] = y;
1092         floats[3] = floats[6] = x + width;
1093         floats[7] = floats[10] = y + height;
1094
1095         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1096 }
1097
1098 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)
1099 {
1100         float floats[20];
1101         float af = DEG2RAD(-angle); // forward
1102         float ar = DEG2RAD(-angle + 90); // right
1103         float sinaf = sin(af);
1104         float cosaf = cos(af);
1105         float sinar = sin(ar);
1106         float cosar = cos(ar);
1107
1108         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1109         if(!r_draw2d.integer && !r_draw2d_force)
1110                 return;
1111         GL_Color(red, green, blue, alpha);
1112
1113         R_Mesh_VertexPointer(floats, 0, 0);
1114         R_Mesh_ColorPointer(NULL, 0, 0);
1115         R_Mesh_ResetTextureState();
1116         if (pic)
1117         {
1118                 if (width == 0)
1119                         width = pic->width;
1120                 if (height == 0)
1121                         height = pic->height;
1122                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1123                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1124
1125                 floats[12] = 0.0f;floats[13] = 0.0f;
1126                 floats[14] = 1.0f;floats[15] = 0.0f;
1127                 floats[16] = 1.0f;floats[17] = 1.0f;
1128                 floats[18] = 0.0f;floats[19] = 1.0f;
1129         }
1130         else
1131                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1132
1133         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1134
1135 // top left
1136         floats[0] = x - cosaf*org_x - cosar*org_y;
1137         floats[1] = y - sinaf*org_x - sinar*org_y;
1138
1139 // top right
1140         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1141         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1142
1143 // bottom right
1144         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1145         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1146
1147 // bottom left
1148         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1149         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1150
1151         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1152 }
1153
1154 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1155 {
1156         float floats[12];
1157
1158         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1159         if(!r_draw2d.integer && !r_draw2d_force)
1160                 return;
1161         GL_Color(red, green, blue, alpha);
1162
1163         R_Mesh_VertexPointer(floats, 0, 0);
1164         R_Mesh_ColorPointer(NULL, 0, 0);
1165         R_Mesh_ResetTextureState();
1166         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1167
1168         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1169         floats[0] = floats[9] = x;
1170         floats[1] = floats[4] = y;
1171         floats[3] = floats[6] = x + width;
1172         floats[7] = floats[10] = y + height;
1173
1174         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1175 }
1176
1177 /// color tag printing
1178 static const vec4_t string_colors[] =
1179 {
1180         // Quake3 colors
1181         // LordHavoc: why on earth is cyan before magenta in Quake3?
1182         // LordHavoc: note: Doom3 uses white for [0] and [7]
1183         {0.0, 0.0, 0.0, 1.0}, // black
1184         {1.0, 0.0, 0.0, 1.0}, // red
1185         {0.0, 1.0, 0.0, 1.0}, // green
1186         {1.0, 1.0, 0.0, 1.0}, // yellow
1187         {0.0, 0.0, 1.0, 1.0}, // blue
1188         {0.0, 1.0, 1.0, 1.0}, // cyan
1189         {1.0, 0.0, 1.0, 1.0}, // magenta
1190         {1.0, 1.0, 1.0, 1.0}, // white
1191         // [515]'s BX_COLOREDTEXT extension
1192         {1.0, 1.0, 1.0, 0.5}, // half transparent
1193         {0.5, 0.5, 0.5, 1.0}  // half brightness
1194         // Black's color table
1195         //{1.0, 1.0, 1.0, 1.0},
1196         //{1.0, 0.0, 0.0, 1.0},
1197         //{0.0, 1.0, 0.0, 1.0},
1198         //{0.0, 0.0, 1.0, 1.0},
1199         //{1.0, 1.0, 0.0, 1.0},
1200         //{0.0, 1.0, 1.0, 1.0},
1201         //{1.0, 0.0, 1.0, 1.0},
1202         //{0.1, 0.1, 0.1, 1.0}
1203 };
1204
1205 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1206
1207 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1208 {
1209         float C = r_textcontrast.value;
1210         float B = r_textbrightness.value;
1211         if (colorindex & 0x10000) // that bit means RGB color
1212         {
1213                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1214                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1215                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1216                 color[3] = (colorindex & 0xf) / 15.0;
1217         }
1218         else
1219                 Vector4Copy(string_colors[colorindex], color);
1220         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1221         if (shadow)
1222         {
1223                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1224                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1225         }
1226 }
1227
1228 // NOTE: this function always draws exactly one character if maxwidth <= 0
1229 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)
1230 {
1231         const char *text_start = text;
1232         int colorindex = STRING_COLOR_DEFAULT;
1233         size_t i;
1234         float x = 0;
1235         Uchar ch, mapch, nextch;
1236         Uchar prevch = 0; // used for kerning
1237         int tempcolorindex;
1238         float kx;
1239         int map_index = 0;
1240         size_t bytes_left;
1241         ft2_font_map_t *fontmap = NULL;
1242         ft2_font_map_t *map = NULL;
1243         //ft2_font_map_t *prevmap = NULL;
1244         ft2_font_t *ft2 = fnt->ft2;
1245         // float ftbase_x;
1246         qboolean snap = true;
1247         qboolean least_one = false;
1248         float dw; // display w
1249         //float dh; // display h
1250         const float *width_of;
1251
1252         if (!h) h = w;
1253         if (!h) {
1254                 w = h = 1;
1255                 snap = false;
1256         }
1257         // do this in the end
1258         w *= fnt->settings.scale;
1259         h *= fnt->settings.scale;
1260
1261         // find the most fitting size:
1262         if (ft2 != NULL)
1263         {
1264                 if (snap)
1265                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1266                 else
1267                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1268                 fontmap = Font_MapForIndex(ft2, map_index);
1269         }
1270
1271         dw = w * sw;
1272         //dh = h * sh;
1273
1274         if (*maxlen < 1)
1275                 *maxlen = 1<<30;
1276
1277         if (!outcolor || *outcolor == -1)
1278                 colorindex = STRING_COLOR_DEFAULT;
1279         else
1280                 colorindex = *outcolor;
1281
1282         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1283         // ftbase_x = snap_to_pixel_x(0);
1284         
1285         if(maxwidth <= 0)
1286         {
1287                 least_one = true;
1288                 maxwidth = -maxwidth;
1289         }
1290
1291         //if (snap)
1292         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1293
1294         if (fontmap)
1295                 width_of = fontmap->width_of;
1296         else
1297                 width_of = fnt->width_of;
1298
1299         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1300         {
1301                 size_t i0 = i;
1302                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1303                 i = text - text_start;
1304                 if (!ch)
1305                         break;
1306                 if (ch == ' ' && !fontmap)
1307                 {
1308                         if(!least_one || i0) // never skip the first character
1309                         if(x + width_of[(int) ' '] * dw > maxwidth)
1310                         {
1311                                 i = i0;
1312                                 break; // oops, can't draw this
1313                         }
1314                         x += width_of[(int) ' '] * dw;
1315                         continue;
1316                 }
1317                 // i points to the char after ^
1318                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1319                 {
1320                         ch = *text; // colors are ascii, so no u8_ needed
1321                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1322                         {
1323                                 colorindex = ch - '0';
1324                                 ++text;
1325                                 ++i;
1326                                 continue;
1327                         }
1328                         // i points to the char after ^...
1329                         // i+3 points to 3 in ^x123
1330                         // i+3 == *maxlen would mean that char is missing
1331                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1332                         {
1333                                 // building colorindex...
1334                                 ch = tolower(text[1]);
1335                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1336                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1337                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1338                                 else tempcolorindex = 0;
1339                                 if (tempcolorindex)
1340                                 {
1341                                         ch = tolower(text[2]);
1342                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1343                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1344                                         else tempcolorindex = 0;
1345                                         if (tempcolorindex)
1346                                         {
1347                                                 ch = tolower(text[3]);
1348                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1349                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1350                                                 else tempcolorindex = 0;
1351                                                 if (tempcolorindex)
1352                                                 {
1353                                                         colorindex = tempcolorindex | 0xf;
1354                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1355                                                         i+=4;
1356                                                         text += 4;
1357                                                         continue;
1358                                                 }
1359                                         }
1360                                 }
1361                         }
1362                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1363                         {
1364                                 i++;
1365                                 text++;
1366                         }
1367                         i--;
1368                 }
1369                 ch = nextch;
1370
1371                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1372                 {
1373                         if (ch > 0xE000)
1374                                 ch -= 0xE000;
1375                         if (ch > 0xFF)
1376                                 continue;
1377                         if (fontmap)
1378                                 map = ft2_oldstyle_map;
1379                         prevch = 0;
1380                         if(!least_one || i0) // never skip the first character
1381                         if(x + width_of[ch] * dw > maxwidth)
1382                         {
1383                                 i = i0;
1384                                 break; // oops, can't draw this
1385                         }
1386                         x += width_of[ch] * dw;
1387                 } else {
1388                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1389                         {
1390                                 map = FontMap_FindForChar(fontmap, ch);
1391                                 if (!map)
1392                                 {
1393                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1394                                                 break;
1395                                         if (!map)
1396                                                 break;
1397                                 }
1398                         }
1399                         mapch = ch - map->start;
1400                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1401                                 x += kx * dw;
1402                         x += map->glyphs[mapch].advance_x * dw;
1403                         //prevmap = map;
1404                         prevch = ch;
1405                 }
1406         }
1407
1408         *maxlen = i;
1409
1410         if (outcolor)
1411                 *outcolor = colorindex;
1412
1413         return x;
1414 }
1415
1416 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)
1417 {
1418         int shadow, colorindex = STRING_COLOR_DEFAULT;
1419         size_t i;
1420         float x = startx, y, s, t, u, v, thisw;
1421         float *av, *at, *ac;
1422         float color[4];
1423         int batchcount;
1424         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1425         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1426         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1427         Uchar ch, mapch, nextch;
1428         Uchar prevch = 0; // used for kerning
1429         int tempcolorindex;
1430         int map_index = 0;
1431         //ft2_font_map_t *prevmap = NULL; // the previous map
1432         ft2_font_map_t *map = NULL;     // the currently used map
1433         ft2_font_map_t *fontmap = NULL; // the font map for the size
1434         float ftbase_y;
1435         const char *text_start = text;
1436         float kx, ky;
1437         ft2_font_t *ft2 = fnt->ft2;
1438         qboolean snap = true;
1439         float pix_x, pix_y;
1440         size_t bytes_left;
1441         float dw, dh;
1442         const float *width_of;
1443
1444         int tw, th;
1445         tw = R_TextureWidth(fnt->tex);
1446         th = R_TextureHeight(fnt->tex);
1447
1448         if (!h) h = w;
1449         if (!h) {
1450                 h = w = 1;
1451                 snap = false;
1452         }
1453
1454         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1455         w *= fnt->settings.scale;
1456         h *= fnt->settings.scale;
1457
1458         if (ft2 != NULL)
1459         {
1460                 if (snap)
1461                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1462                 else
1463                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1464                 fontmap = Font_MapForIndex(ft2, map_index);
1465         }
1466
1467         dw = w * sw;
1468         dh = h * sh;
1469
1470         // draw the font at its baseline when using freetype
1471         //ftbase_x = 0;
1472         ftbase_y = dh * (4.5/6.0);
1473
1474         if (maxlen < 1)
1475                 maxlen = 1<<30;
1476
1477         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1478         if(!r_draw2d.integer && !r_draw2d_force)
1479                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1480
1481         R_Mesh_ColorPointer(color4f, 0, 0);
1482         R_Mesh_ResetTextureState();
1483         if (!fontmap)
1484                 R_Mesh_TexBind(0, fnt->tex);
1485         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1486         R_Mesh_VertexPointer(vertex3f, 0, 0);
1487         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1488
1489         ac = color4f;
1490         at = texcoord2f;
1491         av = vertex3f;
1492         batchcount = 0;
1493
1494         //ftbase_x = snap_to_pixel_x(ftbase_x);
1495         if(snap)
1496         {
1497                 startx = snap_to_pixel_x(startx, 0.4);
1498                 starty = snap_to_pixel_y(starty, 0.4);
1499                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1500         }
1501
1502         pix_x = vid.width / vid_conwidth.value;
1503         pix_y = vid.height / vid_conheight.value;
1504
1505         if (fontmap)
1506                 width_of = fontmap->width_of;
1507         else
1508                 width_of = fnt->width_of;
1509
1510         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1511         {
1512                 prevch = 0;
1513                 text = text_start;
1514
1515                 if (!outcolor || *outcolor == -1)
1516                         colorindex = STRING_COLOR_DEFAULT;
1517                 else
1518                         colorindex = *outcolor;
1519
1520                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1521
1522                 x = startx;
1523                 y = starty;
1524                 /*
1525                 if (shadow)
1526                 {
1527                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1528                         y += r_textshadow.value * vid.height / vid_conheight.value;
1529                 }
1530                 */
1531                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1532                 {
1533                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1534                         i = text - text_start;
1535                         if (!ch)
1536                                 break;
1537                         if (ch == ' ' && !fontmap)
1538                         {
1539                                 x += width_of[(int) ' '] * dw;
1540                                 continue;
1541                         }
1542                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1543                         {
1544                                 ch = *text; // colors are ascii, so no u8_ needed
1545                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1546                                 {
1547                                         colorindex = ch - '0';
1548                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1549                                         ++text;
1550                                         ++i;
1551                                         continue;
1552                                 }
1553                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1554                                 {
1555                                         // building colorindex...
1556                                         ch = tolower(text[1]);
1557                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1558                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1559                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1560                                         else tempcolorindex = 0;
1561                                         if (tempcolorindex)
1562                                         {
1563                                                 ch = tolower(text[2]);
1564                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1565                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1566                                                 else tempcolorindex = 0;
1567                                                 if (tempcolorindex)
1568                                                 {
1569                                                         ch = tolower(text[3]);
1570                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1571                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1572                                                         else tempcolorindex = 0;
1573                                                         if (tempcolorindex)
1574                                                         {
1575                                                                 colorindex = tempcolorindex | 0xf;
1576                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1577                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1578                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1579                                                                 i+=4;
1580                                                                 text+=4;
1581                                                                 continue;
1582                                                         }
1583                                                 }
1584                                         }
1585                                 }
1586                                 else if (ch == STRING_COLOR_TAG)
1587                                 {
1588                                         i++;
1589                                         text++;
1590                                 }
1591                                 i--;
1592                         }
1593                         // get the backup
1594                         ch = nextch;
1595                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1596                         // this way we don't need to rebind fnt->tex for every old-style character
1597                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1598                         if (shadow)
1599                         {
1600                                 x += 1.0/pix_x * r_textshadow.value;
1601                                 y += 1.0/pix_y * r_textshadow.value;
1602                         }
1603                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1604                         {
1605                                 if (ch > 0xE000)
1606                                         ch -= 0xE000;
1607                                 if (ch > 0xFF)
1608                                         continue;
1609                                 if (fontmap)
1610                                 {
1611                                         if (map != ft2_oldstyle_map)
1612                                         {
1613                                                 if (batchcount)
1614                                                 {
1615                                                         // switching from freetype to non-freetype rendering
1616                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1617                                                         batchcount = 0;
1618                                                         ac = color4f;
1619                                                         at = texcoord2f;
1620                                                         av = vertex3f;
1621                                                 }
1622                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1623                                                 map = ft2_oldstyle_map;
1624                                         }
1625                                 }
1626                                 prevch = 0;
1627                                 //num = (unsigned char) text[i];
1628                                 //thisw = fnt->width_of[num];
1629                                 thisw = fnt->width_of[ch];
1630                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1631                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1632                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1633                                 u = 0.0625f * thisw - (1.0f / tw);
1634                                 v = 0.0625f - (1.0f / th);
1635                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1636                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1637                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1638                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1639                                 at[ 0] = s              ; at[ 1] = t    ;
1640                                 at[ 2] = s+u    ; at[ 3] = t    ;
1641                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1642                                 at[ 6] = s              ; at[ 7] = t+v  ;
1643                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1644                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1645                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1646                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1647                                 ac += 16;
1648                                 at += 8;
1649                                 av += 12;
1650                                 batchcount++;
1651                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1652                                 {
1653                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1654                                         batchcount = 0;
1655                                         ac = color4f;
1656                                         at = texcoord2f;
1657                                         av = vertex3f;
1658                                 }
1659                                 x += width_of[ch] * dw;
1660                         } else {
1661                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1662                                 {
1663                                         // new charmap - need to render
1664                                         if (batchcount)
1665                                         {
1666                                                 // we need a different character map, render what we currently have:
1667                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1668                                                 batchcount = 0;
1669                                                 ac = color4f;
1670                                                 at = texcoord2f;
1671                                                 av = vertex3f;
1672                                         }
1673                                         // find the new map
1674                                         map = FontMap_FindForChar(fontmap, ch);
1675                                         if (!map)
1676                                         {
1677                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1678                                                 {
1679                                                         shadow = -1;
1680                                                         break;
1681                                                 }
1682                                                 if (!map)
1683                                                 {
1684                                                         // this shouldn't happen
1685                                                         shadow = -1;
1686                                                         break;
1687                                                 }
1688                                         }
1689                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1690                                 }
1691
1692                                 mapch = ch - map->start;
1693                                 thisw = map->glyphs[mapch].advance_x;
1694
1695                                 //x += ftbase_x;
1696                                 y += ftbase_y;
1697                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1698                                 {
1699                                         x += kx * dw;
1700                                         y += ky * dh;
1701                                 }
1702                                 else
1703                                         kx = ky = 0;
1704                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1705                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1706                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1707                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1708                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1709                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1710                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1711                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1712                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1713                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1714                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1715                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1716                                 //x -= ftbase_x;
1717                                 y -= ftbase_y;
1718
1719                                 x += thisw * dw;
1720                                 ac += 16;
1721                                 at += 8;
1722                                 av += 12;
1723                                 batchcount++;
1724                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1725                                 {
1726                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1727                                         batchcount = 0;
1728                                         ac = color4f;
1729                                         at = texcoord2f;
1730                                         av = vertex3f;
1731                                 }
1732
1733                                 //prevmap = map;
1734                                 prevch = ch;
1735                         }
1736                         if (shadow)
1737                         {
1738                                 x -= 1.0/pix_x * r_textshadow.value;
1739                                 y -= 1.0/pix_y * r_textshadow.value;
1740                         }
1741                 }
1742         }
1743         if (batchcount > 0)
1744                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1745
1746         if (outcolor)
1747                 *outcolor = colorindex;
1748
1749         // note: this relies on the proper text (not shadow) being drawn last
1750         return x;
1751 }
1752
1753 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)
1754 {
1755         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1756 }
1757
1758 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)
1759 {
1760         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1761 }
1762
1763 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1764 {
1765         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1766 }
1767
1768 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1769 {
1770         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1771 }
1772
1773 #if 0
1774 // not used
1775 // no ^xrgb management
1776 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1777 {
1778         int color, numchars = 0;
1779         char *outputend2c = output2c + maxoutchars - 2;
1780         if (!outcolor || *outcolor == -1)
1781                 color = STRING_COLOR_DEFAULT;
1782         else
1783                 color = *outcolor;
1784         if (!maxreadchars)
1785                 maxreadchars = 1<<30;
1786         textend = text + maxreadchars;
1787         while (text != textend && *text)
1788         {
1789                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1790                 {
1791                         if (text[1] == STRING_COLOR_TAG)
1792                                 text++;
1793                         else if (text[1] >= '0' && text[1] <= '9')
1794                         {
1795                                 color = text[1] - '0';
1796                                 text += 2;
1797                                 continue;
1798                         }
1799                 }
1800                 if (output2c >= outputend2c)
1801                         break;
1802                 *output2c++ = *text++;
1803                 *output2c++ = color;
1804                 numchars++;
1805         }
1806         output2c[0] = output2c[1] = 0;
1807         if (outcolor)
1808                 *outcolor = color;
1809         return numchars;
1810 }
1811 #endif
1812
1813 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)
1814 {
1815         float floats[36];
1816
1817         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1818         if(!r_draw2d.integer && !r_draw2d_force)
1819                 return;
1820
1821         R_Mesh_VertexPointer(floats, 0, 0);
1822         R_Mesh_ColorPointer(floats + 20, 0, 0);
1823         R_Mesh_ResetTextureState();
1824         if (pic)
1825         {
1826                 if (width == 0)
1827                         width = pic->width;
1828                 if (height == 0)
1829                         height = pic->height;
1830                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1831                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1832                 floats[12] = s1;floats[13] = t1;
1833                 floats[14] = s2;floats[15] = t2;
1834                 floats[16] = s4;floats[17] = t4;
1835                 floats[18] = s3;floats[19] = t3;
1836         }
1837         else
1838                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1839
1840         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1841         floats[0] = floats[9] = x;
1842         floats[1] = floats[4] = y;
1843         floats[3] = floats[6] = x + width;
1844         floats[7] = floats[10] = y + height;
1845         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1846         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1847         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1848         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1849
1850         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1851 }
1852
1853 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1854 {
1855         _DrawQ_Setup();
1856         CHECKGLERROR
1857         if(!r_draw2d.integer && !r_draw2d_force)
1858                 return;
1859         DrawQ_ProcessDrawFlag(flags, hasalpha);
1860
1861         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1862         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1863         R_Mesh_ResetTextureState();
1864         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1865         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1866
1867         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1868 }
1869
1870 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1871 {
1872         int num;
1873
1874         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1875         if(!r_draw2d.integer && !r_draw2d_force)
1876                 return;
1877
1878         GL_Color(1,1,1,1);
1879         CHECKGLERROR
1880         qglBegin(GL_LINE_LOOP);
1881         for (num = 0;num < mesh->num_vertices;num++)
1882         {
1883                 if (mesh->data_color4f)
1884                         GL_Color(mesh->data_color4f[num*4+0], mesh->data_color4f[num*4+1], mesh->data_color4f[num*4+2], mesh->data_color4f[num*4+3]);
1885                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1886         }
1887         qglEnd();
1888         CHECKGLERROR
1889 }
1890
1891 //[515]: this is old, delete
1892 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1893 {
1894         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1895         if(!r_draw2d.integer && !r_draw2d_force)
1896                 return;
1897
1898         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1899
1900         CHECKGLERROR
1901         //qglLineWidth(width);CHECKGLERROR
1902
1903         GL_Color(r,g,b,alpha);
1904         CHECKGLERROR
1905         qglBegin(GL_LINES);
1906         qglVertex2f(x1, y1);
1907         qglVertex2f(x2, y2);
1908         qglEnd();
1909         CHECKGLERROR
1910 }
1911
1912 void DrawQ_SetClipArea(float x, float y, float width, float height)
1913 {
1914         int ix, iy, iw, ih;
1915         _DrawQ_Setup();
1916
1917         // We have to convert the con coords into real coords
1918         // OGL uses top to bottom
1919         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1920         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1921         iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1922         ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1923         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1924
1925         GL_ScissorTest(true);
1926 }
1927
1928 void DrawQ_ResetClipArea(void)
1929 {
1930         _DrawQ_Setup();
1931         GL_ScissorTest(false);
1932 }
1933
1934 void DrawQ_Finish(void)
1935 {
1936         r_refdef.draw2dstage = 0;
1937 }
1938
1939 void DrawQ_RecalcView(void)
1940 {
1941         if(r_refdef.draw2dstage)
1942                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1943 }
1944
1945 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1946 void R_DrawGamma(void)
1947 {
1948         float c[4];
1949         switch(vid.renderpath)
1950         {
1951         case RENDERPATH_GL20:
1952         case RENDERPATH_CGGL:
1953                 if (vid_usinghwgamma || v_glslgamma.integer)
1954                         return;
1955                 break;
1956         case RENDERPATH_GL13:
1957         case RENDERPATH_GL11:
1958                 if (vid_usinghwgamma)
1959                         return;
1960                 break;
1961         }
1962         // all the blends ignore depth
1963         R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1964         R_Mesh_ColorPointer(NULL, 0, 0);
1965         R_Mesh_ResetTextureState();
1966         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1967         GL_DepthMask(true);
1968         GL_DepthRange(0, 1);
1969         GL_PolygonOffset(0, 0);
1970         GL_DepthTest(false);
1971         if (v_color_enable.integer)
1972         {
1973                 c[0] = v_color_white_r.value;
1974                 c[1] = v_color_white_g.value;
1975                 c[2] = v_color_white_b.value;
1976         }
1977         else
1978                 c[0] = c[1] = c[2] = v_contrast.value;
1979         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1980         {
1981                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1982                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1983                 {
1984                         GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1985                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1986                         VectorScale(c, 0.5, c);
1987                 }
1988         }
1989         if (v_color_enable.integer)
1990         {
1991                 c[0] = v_color_black_r.value;
1992                 c[1] = v_color_black_g.value;
1993                 c[2] = v_color_black_b.value;
1994         }
1995         else
1996                 c[0] = c[1] = c[2] = v_brightness.value;
1997         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1998         {
1999                 GL_BlendFunc(GL_ONE, GL_ONE);
2000                 GL_Color(c[0], c[1], c[2], 1);
2001                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
2002         }
2003 }
2004