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