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