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