04d3eb8d92057d44658cc2268af35553425b1350
[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 != 0, NULL);
375         if (pixels == NULL && !strncmp(path, "gfx/", 4))
376                 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, 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 != 0);
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 != 0);
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 = (dp_font_t *)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         GL_DepthFunc(GL_LEQUAL);
1021         GL_PolygonOffset(0,0);
1022         GL_CullFace(GL_NONE);
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[36];
1078
1079         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1080         if(!r_draw2d.integer && !r_draw2d_force)
1081                 return;
1082
1083 //      R_Mesh_ResetTextureState();
1084         floats[12] = 0.0f;floats[13] = 0.0f;
1085         floats[14] = 1.0f;floats[15] = 0.0f;
1086         floats[16] = 1.0f;floats[17] = 1.0f;
1087         floats[18] = 0.0f;floats[19] = 1.0f;
1088         floats[20] = floats[24] = floats[28] = floats[32] = red;
1089         floats[21] = floats[25] = floats[29] = floats[33] = green;
1090         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1091         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1092         if (pic)
1093         {
1094                 if (width == 0)
1095                         width = pic->width;
1096                 if (height == 0)
1097                         height = pic->height;
1098                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1099
1100 #if 0
1101       // AK07: lets be texel correct on the corners
1102       {
1103          float horz_offset = 0.5f / pic->width;
1104          float vert_offset = 0.5f / pic->height;
1105
1106                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1107                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1108                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1109                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1110       }
1111 #endif
1112         }
1113         else
1114                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1115
1116         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1117         floats[0] = floats[9] = x;
1118         floats[1] = floats[4] = y;
1119         floats[3] = floats[6] = x + width;
1120         floats[7] = floats[10] = y + height;
1121
1122         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1123         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 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[36];
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
1140 //      R_Mesh_ResetTextureState();
1141         if (pic)
1142         {
1143                 if (width == 0)
1144                         width = pic->width;
1145                 if (height == 0)
1146                         height = pic->height;
1147                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1148         }
1149         else
1150                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1151
1152         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1153
1154 // top left
1155         floats[0] = x - cosaf*org_x - cosar*org_y;
1156         floats[1] = y - sinaf*org_x - sinar*org_y;
1157
1158 // top right
1159         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1160         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1161
1162 // bottom right
1163         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1164         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1165
1166 // bottom left
1167         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1168         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1169
1170         floats[12] = 0.0f;floats[13] = 0.0f;
1171         floats[14] = 1.0f;floats[15] = 0.0f;
1172         floats[16] = 1.0f;floats[17] = 1.0f;
1173         floats[18] = 0.0f;floats[19] = 1.0f;
1174         floats[20] = floats[24] = floats[28] = floats[32] = red;
1175         floats[21] = floats[25] = floats[29] = floats[33] = green;
1176         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1177         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1178
1179         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1180         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1181 }
1182
1183 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1184 {
1185         float floats[36];
1186
1187         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1188         if(!r_draw2d.integer && !r_draw2d_force)
1189                 return;
1190
1191 //      R_Mesh_ResetTextureState();
1192         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1193
1194         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1195         floats[0] = floats[9] = x;
1196         floats[1] = floats[4] = y;
1197         floats[3] = floats[6] = x + width;
1198         floats[7] = floats[10] = y + height;
1199         floats[12] = 0.0f;floats[13] = 0.0f;
1200         floats[14] = 1.0f;floats[15] = 0.0f;
1201         floats[16] = 1.0f;floats[17] = 1.0f;
1202         floats[18] = 0.0f;floats[19] = 1.0f;
1203         floats[20] = floats[24] = floats[28] = floats[32] = red;
1204         floats[21] = floats[25] = floats[29] = floats[33] = green;
1205         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1206         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1207
1208         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1209         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1210 }
1211
1212 /// color tag printing
1213 static const vec4_t string_colors[] =
1214 {
1215         // Quake3 colors
1216         // LordHavoc: why on earth is cyan before magenta in Quake3?
1217         // LordHavoc: note: Doom3 uses white for [0] and [7]
1218         {0.0, 0.0, 0.0, 1.0}, // black
1219         {1.0, 0.0, 0.0, 1.0}, // red
1220         {0.0, 1.0, 0.0, 1.0}, // green
1221         {1.0, 1.0, 0.0, 1.0}, // yellow
1222         {0.0, 0.0, 1.0, 1.0}, // blue
1223         {0.0, 1.0, 1.0, 1.0}, // cyan
1224         {1.0, 0.0, 1.0, 1.0}, // magenta
1225         {1.0, 1.0, 1.0, 1.0}, // white
1226         // [515]'s BX_COLOREDTEXT extension
1227         {1.0, 1.0, 1.0, 0.5}, // half transparent
1228         {0.5, 0.5, 0.5, 1.0}  // half brightness
1229         // Black's color table
1230         //{1.0, 1.0, 1.0, 1.0},
1231         //{1.0, 0.0, 0.0, 1.0},
1232         //{0.0, 1.0, 0.0, 1.0},
1233         //{0.0, 0.0, 1.0, 1.0},
1234         //{1.0, 1.0, 0.0, 1.0},
1235         //{0.0, 1.0, 1.0, 1.0},
1236         //{1.0, 0.0, 1.0, 1.0},
1237         //{0.1, 0.1, 0.1, 1.0}
1238 };
1239
1240 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1241
1242 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1243 {
1244         float C = r_textcontrast.value;
1245         float B = r_textbrightness.value;
1246         if (colorindex & 0x10000) // that bit means RGB color
1247         {
1248                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1249                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1250                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1251                 color[3] = (colorindex & 0xf) / 15.0;
1252         }
1253         else
1254                 Vector4Copy(string_colors[colorindex], color);
1255         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1256         if (shadow)
1257         {
1258                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1259                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1260         }
1261 }
1262
1263 // NOTE: this function always draws exactly one character if maxwidth <= 0
1264 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)
1265 {
1266         const char *text_start = text;
1267         int colorindex = STRING_COLOR_DEFAULT;
1268         size_t i;
1269         float x = 0;
1270         Uchar ch, mapch, nextch;
1271         Uchar prevch = 0; // used for kerning
1272         int tempcolorindex;
1273         float kx;
1274         int map_index = 0;
1275         size_t bytes_left;
1276         ft2_font_map_t *fontmap = NULL;
1277         ft2_font_map_t *map = NULL;
1278         //ft2_font_map_t *prevmap = NULL;
1279         ft2_font_t *ft2 = fnt->ft2;
1280         // float ftbase_x;
1281         qboolean snap = true;
1282         qboolean least_one = false;
1283         float dw; // display w
1284         //float dh; // display h
1285         const float *width_of;
1286
1287         if (!h) h = w;
1288         if (!h) {
1289                 w = h = 1;
1290                 snap = false;
1291         }
1292         // do this in the end
1293         w *= fnt->settings.scale;
1294         h *= fnt->settings.scale;
1295
1296         // find the most fitting size:
1297         if (ft2 != NULL)
1298         {
1299                 if (snap)
1300                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1301                 else
1302                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1303                 fontmap = Font_MapForIndex(ft2, map_index);
1304         }
1305
1306         dw = w * sw;
1307         //dh = h * sh;
1308
1309         if (*maxlen < 1)
1310                 *maxlen = 1<<30;
1311
1312         if (!outcolor || *outcolor == -1)
1313                 colorindex = STRING_COLOR_DEFAULT;
1314         else
1315                 colorindex = *outcolor;
1316
1317         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1318         // ftbase_x = snap_to_pixel_x(0);
1319         
1320         if(maxwidth <= 0)
1321         {
1322                 least_one = true;
1323                 maxwidth = -maxwidth;
1324         }
1325
1326         //if (snap)
1327         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1328
1329         if (fontmap)
1330                 width_of = fontmap->width_of;
1331         else
1332                 width_of = fnt->width_of;
1333
1334         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1335         {
1336                 size_t i0 = i;
1337                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1338                 i = text - text_start;
1339                 if (!ch)
1340                         break;
1341                 if (ch == ' ' && !fontmap)
1342                 {
1343                         if(!least_one || i0) // never skip the first character
1344                         if(x + width_of[(int) ' '] * dw > maxwidth)
1345                         {
1346                                 i = i0;
1347                                 break; // oops, can't draw this
1348                         }
1349                         x += width_of[(int) ' '] * dw;
1350                         continue;
1351                 }
1352                 // i points to the char after ^
1353                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1354                 {
1355                         ch = *text; // colors are ascii, so no u8_ needed
1356                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1357                         {
1358                                 colorindex = ch - '0';
1359                                 ++text;
1360                                 ++i;
1361                                 continue;
1362                         }
1363                         // i points to the char after ^...
1364                         // i+3 points to 3 in ^x123
1365                         // i+3 == *maxlen would mean that char is missing
1366                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1367                         {
1368                                 // building colorindex...
1369                                 ch = tolower(text[1]);
1370                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1371                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1372                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1373                                 else tempcolorindex = 0;
1374                                 if (tempcolorindex)
1375                                 {
1376                                         ch = tolower(text[2]);
1377                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1378                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1379                                         else tempcolorindex = 0;
1380                                         if (tempcolorindex)
1381                                         {
1382                                                 ch = tolower(text[3]);
1383                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1384                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1385                                                 else tempcolorindex = 0;
1386                                                 if (tempcolorindex)
1387                                                 {
1388                                                         colorindex = tempcolorindex | 0xf;
1389                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1390                                                         i+=4;
1391                                                         text += 4;
1392                                                         continue;
1393                                                 }
1394                                         }
1395                                 }
1396                         }
1397                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1398                         {
1399                                 i++;
1400                                 text++;
1401                         }
1402                         i--;
1403                 }
1404                 ch = nextch;
1405
1406                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1407                 {
1408                         if (ch > 0xE000)
1409                                 ch -= 0xE000;
1410                         if (ch > 0xFF)
1411                                 continue;
1412                         if (fontmap)
1413                                 map = ft2_oldstyle_map;
1414                         prevch = 0;
1415                         if(!least_one || i0) // never skip the first character
1416                         if(x + width_of[ch] * dw > maxwidth)
1417                         {
1418                                 i = i0;
1419                                 break; // oops, can't draw this
1420                         }
1421                         x += width_of[ch] * dw;
1422                 } else {
1423                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1424                         {
1425                                 map = FontMap_FindForChar(fontmap, ch);
1426                                 if (!map)
1427                                 {
1428                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1429                                                 break;
1430                                         if (!map)
1431                                                 break;
1432                                 }
1433                         }
1434                         mapch = ch - map->start;
1435                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1436                                 x += kx * dw;
1437                         x += map->glyphs[mapch].advance_x * dw;
1438                         //prevmap = map;
1439                         prevch = ch;
1440                 }
1441         }
1442
1443         *maxlen = i;
1444
1445         if (outcolor)
1446                 *outcolor = colorindex;
1447
1448         return x;
1449 }
1450
1451 float DrawQ_Color[4];
1452 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)
1453 {
1454         int shadow, colorindex = STRING_COLOR_DEFAULT;
1455         size_t i;
1456         float x = startx, y, s, t, u, v, thisw;
1457         float *av, *at, *ac;
1458         int batchcount;
1459         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1460         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1461         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1462         Uchar ch, mapch, nextch;
1463         Uchar prevch = 0; // used for kerning
1464         int tempcolorindex;
1465         int map_index = 0;
1466         //ft2_font_map_t *prevmap = NULL; // the previous map
1467         ft2_font_map_t *map = NULL;     // the currently used map
1468         ft2_font_map_t *fontmap = NULL; // the font map for the size
1469         float ftbase_y;
1470         const char *text_start = text;
1471         float kx, ky;
1472         ft2_font_t *ft2 = fnt->ft2;
1473         qboolean snap = true;
1474         float pix_x, pix_y;
1475         size_t bytes_left;
1476         float dw, dh;
1477         const float *width_of;
1478
1479         int tw, th;
1480         tw = R_TextureWidth(fnt->tex);
1481         th = R_TextureHeight(fnt->tex);
1482
1483         if (!h) h = w;
1484         if (!h) {
1485                 h = w = 1;
1486                 snap = false;
1487         }
1488
1489         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1490         w *= fnt->settings.scale;
1491         h *= fnt->settings.scale;
1492
1493         if (ft2 != NULL)
1494         {
1495                 if (snap)
1496                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1497                 else
1498                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1499                 fontmap = Font_MapForIndex(ft2, map_index);
1500         }
1501
1502         dw = w * sw;
1503         dh = h * sh;
1504
1505         // draw the font at its baseline when using freetype
1506         //ftbase_x = 0;
1507         ftbase_y = dh * (4.5/6.0);
1508
1509         if (maxlen < 1)
1510                 maxlen = 1<<30;
1511
1512         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1513         if(!r_draw2d.integer && !r_draw2d_force)
1514                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1515
1516 //      R_Mesh_ResetTextureState();
1517         if (!fontmap)
1518                 R_Mesh_TexBind(0, fnt->tex);
1519         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1520
1521         ac = color4f;
1522         at = texcoord2f;
1523         av = vertex3f;
1524         batchcount = 0;
1525
1526         //ftbase_x = snap_to_pixel_x(ftbase_x);
1527         if(snap)
1528         {
1529                 startx = snap_to_pixel_x(startx, 0.4);
1530                 starty = snap_to_pixel_y(starty, 0.4);
1531                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1532         }
1533
1534         pix_x = vid.width / vid_conwidth.value;
1535         pix_y = vid.height / vid_conheight.value;
1536
1537         if (fontmap)
1538                 width_of = fontmap->width_of;
1539         else
1540                 width_of = fnt->width_of;
1541
1542         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1543         {
1544                 prevch = 0;
1545                 text = text_start;
1546
1547                 if (!outcolor || *outcolor == -1)
1548                         colorindex = STRING_COLOR_DEFAULT;
1549                 else
1550                         colorindex = *outcolor;
1551
1552                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1553
1554                 x = startx;
1555                 y = starty;
1556                 /*
1557                 if (shadow)
1558                 {
1559                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1560                         y += r_textshadow.value * vid.height / vid_conheight.value;
1561                 }
1562                 */
1563                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1564                 {
1565                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1566                         i = text - text_start;
1567                         if (!ch)
1568                                 break;
1569                         if (ch == ' ' && !fontmap)
1570                         {
1571                                 x += width_of[(int) ' '] * dw;
1572                                 continue;
1573                         }
1574                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1575                         {
1576                                 ch = *text; // colors are ascii, so no u8_ needed
1577                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1578                                 {
1579                                         colorindex = ch - '0';
1580                                         DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1581                                         ++text;
1582                                         ++i;
1583                                         continue;
1584                                 }
1585                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1586                                 {
1587                                         // building colorindex...
1588                                         ch = tolower(text[1]);
1589                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1590                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1591                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1592                                         else tempcolorindex = 0;
1593                                         if (tempcolorindex)
1594                                         {
1595                                                 ch = tolower(text[2]);
1596                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1597                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1598                                                 else tempcolorindex = 0;
1599                                                 if (tempcolorindex)
1600                                                 {
1601                                                         ch = tolower(text[3]);
1602                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1603                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1604                                                         else tempcolorindex = 0;
1605                                                         if (tempcolorindex)
1606                                                         {
1607                                                                 colorindex = tempcolorindex | 0xf;
1608                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1609                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1610                                                                 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1611                                                                 i+=4;
1612                                                                 text+=4;
1613                                                                 continue;
1614                                                         }
1615                                                 }
1616                                         }
1617                                 }
1618                                 else if (ch == STRING_COLOR_TAG)
1619                                 {
1620                                         i++;
1621                                         text++;
1622                                 }
1623                                 i--;
1624                         }
1625                         // get the backup
1626                         ch = nextch;
1627                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1628                         // this way we don't need to rebind fnt->tex for every old-style character
1629                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1630                         if (shadow)
1631                         {
1632                                 x += 1.0/pix_x * r_textshadow.value;
1633                                 y += 1.0/pix_y * r_textshadow.value;
1634                         }
1635                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1636                         {
1637                                 if (ch >= 0xE000)
1638                                         ch -= 0xE000;
1639                                 if (ch > 0xFF)
1640                                         goto out;
1641                                 if (fontmap)
1642                                 {
1643                                         if (map != ft2_oldstyle_map)
1644                                         {
1645                                                 if (batchcount)
1646                                                 {
1647                                                         // switching from freetype to non-freetype rendering
1648                                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1649                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1650                                                         batchcount = 0;
1651                                                         ac = color4f;
1652                                                         at = texcoord2f;
1653                                                         av = vertex3f;
1654                                                 }
1655                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1656                                                 map = ft2_oldstyle_map;
1657                                         }
1658                                 }
1659                                 prevch = 0;
1660                                 //num = (unsigned char) text[i];
1661                                 //thisw = fnt->width_of[num];
1662                                 thisw = fnt->width_of[ch];
1663                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1664                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1665                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1666                                 u = 0.0625f * thisw - (1.0f / tw);
1667                                 v = 0.0625f - (1.0f / th);
1668                                 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1669                                 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1670                                 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1671                                 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1672                                 at[ 0] = s              ; at[ 1] = t    ;
1673                                 at[ 2] = s+u    ; at[ 3] = t    ;
1674                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1675                                 at[ 6] = s              ; at[ 7] = t+v  ;
1676                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1677                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1678                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1679                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1680                                 ac += 16;
1681                                 at += 8;
1682                                 av += 12;
1683                                 batchcount++;
1684                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1685                                 {
1686                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1687                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1688                                         batchcount = 0;
1689                                         ac = color4f;
1690                                         at = texcoord2f;
1691                                         av = vertex3f;
1692                                 }
1693                                 x += width_of[ch] * dw;
1694                         } else {
1695                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1696                                 {
1697                                         // new charmap - need to render
1698                                         if (batchcount)
1699                                         {
1700                                                 // we need a different character map, render what we currently have:
1701                                                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1702                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1703                                                 batchcount = 0;
1704                                                 ac = color4f;
1705                                                 at = texcoord2f;
1706                                                 av = vertex3f;
1707                                         }
1708                                         // find the new map
1709                                         map = FontMap_FindForChar(fontmap, ch);
1710                                         if (!map)
1711                                         {
1712                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1713                                                 {
1714                                                         shadow = -1;
1715                                                         break;
1716                                                 }
1717                                                 if (!map)
1718                                                 {
1719                                                         // this shouldn't happen
1720                                                         shadow = -1;
1721                                                         break;
1722                                                 }
1723                                         }
1724                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1725                                 }
1726
1727                                 mapch = ch - map->start;
1728                                 thisw = map->glyphs[mapch].advance_x;
1729
1730                                 //x += ftbase_x;
1731                                 y += ftbase_y;
1732                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1733                                 {
1734                                         x += kx * dw;
1735                                         y += ky * dh;
1736                                 }
1737                                 else
1738                                         kx = ky = 0;
1739                                 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1740                                 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1741                                 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1742                                 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1743                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1744                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1745                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1746                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1747                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1748                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1749                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1750                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1751                                 //x -= ftbase_x;
1752                                 y -= ftbase_y;
1753
1754                                 x += thisw * dw;
1755                                 ac += 16;
1756                                 at += 8;
1757                                 av += 12;
1758                                 batchcount++;
1759                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1760                                 {
1761                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1762                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1763                                         batchcount = 0;
1764                                         ac = color4f;
1765                                         at = texcoord2f;
1766                                         av = vertex3f;
1767                                 }
1768
1769                                 //prevmap = map;
1770                                 prevch = ch;
1771                         }
1772 out:
1773                         if (shadow)
1774                         {
1775                                 x -= 1.0/pix_x * r_textshadow.value;
1776                                 y -= 1.0/pix_y * r_textshadow.value;
1777                         }
1778                 }
1779         }
1780         if (batchcount > 0)
1781         {
1782                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1783                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1784         }
1785
1786         if (outcolor)
1787                 *outcolor = colorindex;
1788         
1789         // note: this relies on the proper text (not shadow) being drawn last
1790         return x;
1791 }
1792
1793 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)
1794 {
1795         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1796 }
1797
1798 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)
1799 {
1800         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1801 }
1802
1803 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1804 {
1805         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1806 }
1807
1808 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1809 {
1810         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1811 }
1812
1813 #if 0
1814 // not used
1815 // no ^xrgb management
1816 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1817 {
1818         int color, numchars = 0;
1819         char *outputend2c = output2c + maxoutchars - 2;
1820         if (!outcolor || *outcolor == -1)
1821                 color = STRING_COLOR_DEFAULT;
1822         else
1823                 color = *outcolor;
1824         if (!maxreadchars)
1825                 maxreadchars = 1<<30;
1826         textend = text + maxreadchars;
1827         while (text != textend && *text)
1828         {
1829                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1830                 {
1831                         if (text[1] == STRING_COLOR_TAG)
1832                                 text++;
1833                         else if (text[1] >= '0' && text[1] <= '9')
1834                         {
1835                                 color = text[1] - '0';
1836                                 text += 2;
1837                                 continue;
1838                         }
1839                 }
1840                 if (output2c >= outputend2c)
1841                         break;
1842                 *output2c++ = *text++;
1843                 *output2c++ = color;
1844                 numchars++;
1845         }
1846         output2c[0] = output2c[1] = 0;
1847         if (outcolor)
1848                 *outcolor = color;
1849         return numchars;
1850 }
1851 #endif
1852
1853 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)
1854 {
1855         float floats[36];
1856
1857         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1858         if(!r_draw2d.integer && !r_draw2d_force)
1859                 return;
1860
1861 //      R_Mesh_ResetTextureState();
1862         if (pic)
1863         {
1864                 if (width == 0)
1865                         width = pic->width;
1866                 if (height == 0)
1867                         height = pic->height;
1868                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1869         }
1870         else
1871                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1872
1873         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1874         floats[0] = floats[9] = x;
1875         floats[1] = floats[4] = y;
1876         floats[3] = floats[6] = x + width;
1877         floats[7] = floats[10] = y + height;
1878         floats[12] = s1;floats[13] = t1;
1879         floats[14] = s2;floats[15] = t2;
1880         floats[16] = s4;floats[17] = t4;
1881         floats[18] = s3;floats[19] = t3;
1882         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1883         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1884         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1885         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1886
1887         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1888         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1889 }
1890
1891 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1892 {
1893         _DrawQ_Setup();
1894         CHECKGLERROR
1895         if(!r_draw2d.integer && !r_draw2d_force)
1896                 return;
1897         DrawQ_ProcessDrawFlag(flags, hasalpha);
1898
1899 //      R_Mesh_ResetTextureState();
1900         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1901
1902         R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1903         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1904 }
1905
1906 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1907 {
1908         int num;
1909
1910         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1911         if(!r_draw2d.integer && !r_draw2d_force)
1912                 return;
1913
1914         GL_Color(1,1,1,1);
1915         switch(vid.renderpath)
1916         {
1917         case RENDERPATH_GL11:
1918         case RENDERPATH_GL13:
1919         case RENDERPATH_GL20:
1920         case RENDERPATH_CGGL:
1921                 CHECKGLERROR
1922                 qglBegin(GL_LINE_LOOP);
1923                 for (num = 0;num < mesh->num_vertices;num++)
1924                 {
1925                         if (mesh->data_color4f)
1926                                 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]);
1927                         qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1928                 }
1929                 qglEnd();
1930                 CHECKGLERROR
1931                 break;
1932         case RENDERPATH_D3D9:
1933                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1934                 break;
1935         case RENDERPATH_D3D10:
1936                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1937                 break;
1938         case RENDERPATH_D3D11:
1939                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1940                 break;
1941         }
1942 }
1943
1944 //[515]: this is old, delete
1945 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1946 {
1947         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1948         if(!r_draw2d.integer && !r_draw2d_force)
1949                 return;
1950
1951         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1952
1953         switch(vid.renderpath)
1954         {
1955         case RENDERPATH_GL11:
1956         case RENDERPATH_GL13:
1957         case RENDERPATH_GL20:
1958         case RENDERPATH_CGGL:
1959                 CHECKGLERROR
1960
1961                 //qglLineWidth(width);CHECKGLERROR
1962
1963                 GL_Color(r,g,b,alpha);
1964                 CHECKGLERROR
1965                 qglBegin(GL_LINES);
1966                 qglVertex2f(x1, y1);
1967                 qglVertex2f(x2, y2);
1968                 qglEnd();
1969                 CHECKGLERROR
1970                 break;
1971         case RENDERPATH_D3D9:
1972                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1973                 break;
1974         case RENDERPATH_D3D10:
1975                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1976                 break;
1977         case RENDERPATH_D3D11:
1978                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1979                 break;
1980         }
1981 }
1982
1983 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
1984 {
1985         int i;
1986         qboolean hasalpha = false;
1987         for (i = 0;i < numlines*2;i++)
1988                 if (color4f[i*4+3] < 1.0f)
1989                         hasalpha = true;
1990
1991         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
1992
1993         if(!r_draw2d.integer && !r_draw2d_force)
1994                 return;
1995
1996         switch(vid.renderpath)
1997         {
1998         case RENDERPATH_GL11:
1999         case RENDERPATH_GL13:
2000         case RENDERPATH_GL20:
2001         case RENDERPATH_CGGL:
2002                 CHECKGLERROR
2003
2004                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2005
2006                 //qglLineWidth(width);CHECKGLERROR
2007
2008                 CHECKGLERROR
2009                 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2010                 qglDrawArrays(GL_LINES, 0, numlines*2);
2011                 CHECKGLERROR
2012                 break;
2013         case RENDERPATH_D3D9:
2014                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2015                 break;
2016         case RENDERPATH_D3D10:
2017                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2018                 break;
2019         case RENDERPATH_D3D11:
2020                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2021                 break;
2022         }
2023 }
2024
2025 void DrawQ_SetClipArea(float x, float y, float width, float height)
2026 {
2027         int ix, iy, iw, ih;
2028         _DrawQ_Setup();
2029
2030         // We have to convert the con coords into real coords
2031         // OGL uses top to bottom
2032         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2033         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2034         iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2035         ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2036         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2037
2038         GL_ScissorTest(true);
2039 }
2040
2041 void DrawQ_ResetClipArea(void)
2042 {
2043         _DrawQ_Setup();
2044         GL_ScissorTest(false);
2045 }
2046
2047 void DrawQ_Finish(void)
2048 {
2049         r_refdef.draw2dstage = 0;
2050 }
2051
2052 void DrawQ_RecalcView(void)
2053 {
2054         if(r_refdef.draw2dstage)
2055                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2056 }
2057
2058 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2059 void R_DrawGamma(void)
2060 {
2061         float c[4];
2062         switch(vid.renderpath)
2063         {
2064         case RENDERPATH_GL20:
2065         case RENDERPATH_CGGL:
2066         case RENDERPATH_D3D9:
2067         case RENDERPATH_D3D10:
2068         case RENDERPATH_D3D11:
2069                 if (vid_usinghwgamma || v_glslgamma.integer)
2070                         return;
2071                 break;
2072         case RENDERPATH_GL13:
2073         case RENDERPATH_GL11:
2074                 if (vid_usinghwgamma)
2075                         return;
2076                 break;
2077         }
2078         // all the blends ignore depth
2079 //      R_Mesh_ResetTextureState();
2080         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2081         GL_DepthMask(true);
2082         GL_DepthRange(0, 1);
2083         GL_PolygonOffset(0, 0);
2084         GL_DepthTest(false);
2085         if (v_color_enable.integer)
2086         {
2087                 c[0] = v_color_white_r.value;
2088                 c[1] = v_color_white_g.value;
2089                 c[2] = v_color_white_b.value;
2090         }
2091         else
2092                 c[0] = c[1] = c[2] = v_contrast.value;
2093         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2094         {
2095                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2096                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2097                 {
2098                         GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2099                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2100                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2101                         VectorScale(c, 0.5, c);
2102                 }
2103         }
2104         if (v_color_enable.integer)
2105         {
2106                 c[0] = v_color_black_r.value;
2107                 c[1] = v_color_black_g.value;
2108                 c[2] = v_color_black_b.value;
2109         }
2110         else
2111                 c[0] = c[1] = c[2] = v_brightness.value;
2112         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2113         {
2114                 GL_BlendFunc(GL_ONE, GL_ONE);
2115                 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2116                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2117                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2118         }
2119 }
2120