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