new r_stereo modes (horiz and vert split)
[divverent/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;
725
726         // find font
727         for(i = 0; i < dp_fonts.maxsize; ++i)
728                 if(!strcmp(dp_fonts.f[i].title, title))
729                         return &dp_fonts.f[i];
730         // if not found - try allocate
731         if (allocate_new)
732         {
733                 // find any font with empty title
734                 for(i = 0; i < dp_fonts.maxsize; ++i)
735                 {
736                         if(!strcmp(dp_fonts.f[i].title, ""))
737                         {
738                                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
739                                 return &dp_fonts.f[i];
740                         }
741                 }
742                 // if no any 'free' fonts - expand buffer
743                 i = dp_fonts.maxsize;
744                 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
745                 if (developer_font.integer)
746                         Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
747                 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
748                 // register a font in first expanded slot
749                 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
750                 return &dp_fonts.f[i];
751         }
752         return NULL;
753 }
754
755 static float snap_to_pixel_x(float x, float roundUpAt)
756 {
757         float pixelpos = x * vid.width / vid_conwidth.value;
758         int snap = (int) pixelpos;
759         if (pixelpos - snap >= roundUpAt) ++snap;
760         return ((float)snap * vid_conwidth.value / vid.width);
761         /*
762         x = (int)(x * vid.width / vid_conwidth.value);
763         x = (x * vid_conwidth.value / vid.width);
764         return x;
765         */
766 }
767
768 static float snap_to_pixel_y(float y, float roundUpAt)
769 {
770         float pixelpos = y * vid.height / vid_conheight.value;
771         int snap = (int) pixelpos;
772         if (pixelpos - snap > roundUpAt) ++snap;
773         return ((float)snap * vid_conheight.value / vid.height);
774         /*
775         y = (int)(y * vid.height / vid_conheight.value);
776         y = (y * vid_conheight.value / vid.height);
777         return y;
778         */
779 }
780
781 static void LoadFont_f(void)
782 {
783         dp_font_t *f;
784         int i, sizes;
785         const char *filelist, *c, *cm;
786         float sz, scale, voffset;
787         char mainfont[MAX_QPATH];
788
789         if(Cmd_Argc() < 2)
790         {
791                 Con_Printf("Available font commands:\n");
792                 for(i = 0; i < dp_fonts.maxsize; ++i)
793                         if (dp_fonts.f[i].title[0])
794                                 Con_Printf("  loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
795                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
796                            "can specify multiple fonts and faces\n"
797                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
798                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
799                            "of gfx/fallback as fallback font.\n"
800                            "You can also specify a list of font sizes to load, like this:\n"
801                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
802                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
803                            "custom switches:\n"
804                            " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
805                            " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
806                         );
807                 return;
808         }
809         f = FindFont(Cmd_Argv(1), true);
810         if(f == NULL)
811         {
812                 Con_Printf("font function not found\n");
813                 return;
814         }
815
816         if(Cmd_Argc() < 3)
817                 filelist = "gfx/conchars";
818         else
819                 filelist = Cmd_Argv(2);
820
821         memset(f->fallbacks, 0, sizeof(f->fallbacks));
822         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
823
824         // first font is handled "normally"
825         c = strchr(filelist, ':');
826         cm = strchr(filelist, ',');
827         if(c && (!cm || c < cm))
828                 f->req_face = atoi(c+1);
829         else
830         {
831                 f->req_face = 0;
832                 c = cm;
833         }
834
835         if(!c || (c - filelist) > MAX_QPATH)
836                 strlcpy(mainfont, filelist, sizeof(mainfont));
837         else
838         {
839                 memcpy(mainfont, filelist, c - filelist);
840                 mainfont[c - filelist] = 0;
841         }
842
843         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
844         {
845                 c = strchr(filelist, ',');
846                 if(!c)
847                         break;
848                 filelist = c + 1;
849                 if(!*filelist)
850                         break;
851                 c = strchr(filelist, ':');
852                 cm = strchr(filelist, ',');
853                 if(c && (!cm || c < cm))
854                         f->fallback_faces[i] = atoi(c+1);
855                 else
856                 {
857                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
858                         c = cm;
859                 }
860                 if(!c || (c-filelist) > MAX_QPATH)
861                 {
862                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
863                 }
864                 else
865                 {
866                         memcpy(f->fallbacks[i], filelist, c - filelist);
867                         f->fallbacks[i][c - filelist] = 0;
868                 }
869         }
870
871         // for now: by default load only one size: the default size
872         f->req_sizes[0] = 0;
873         for(i = 1; i < MAX_FONT_SIZES; ++i)
874                 f->req_sizes[i] = -1;
875
876         scale = 1;
877         voffset = 0;
878         if(Cmd_Argc() >= 4)
879         {
880                 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
881                 {
882                         // special switches
883                         if (!strcmp(Cmd_Argv(i), "scale"))
884                         {
885                                 i++;
886                                 if (i < Cmd_Argc())
887                                         scale = atof(Cmd_Argv(i));
888                                 continue;
889                         }
890                         if (!strcmp(Cmd_Argv(i), "voffset"))
891                         {
892                                 i++;
893                                 if (i < Cmd_Argc())
894                                         voffset = atof(Cmd_Argv(i));
895                                 continue;
896                         }
897
898                         if (sizes == -1)
899                                 continue; // no slot for other sizes
900
901                         // parse one of sizes
902                         sz = atof(Cmd_Argv(i));
903                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
904                         {
905                                 // search for duplicated sizes
906                                 int j;
907                                 for (j=0; j<sizes; j++)
908                                         if (f->req_sizes[j] == sz)
909                                                 break;
910                                 if (j != sizes)
911                                         continue; // sz already in req_sizes, don't add it again
912
913                                 if (sizes == MAX_FONT_SIZES)
914                                 {
915                                         Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
916                                         sizes = -1;
917                                         continue;
918                                 }
919                                 f->req_sizes[sizes] = sz;
920                                 sizes++;
921                         }
922                 }
923         }
924
925         LoadFont(true, mainfont, f, scale, voffset);
926 }
927
928 /*
929 ===============
930 Draw_Init
931 ===============
932 */
933 static void gl_draw_start(void)
934 {
935         int i;
936         drawtexturepool = R_AllocTexturePool();
937
938         numcachepics = 0;
939         memset(cachepichash, 0, sizeof(cachepichash));
940
941         font_start();
942
943         // load default font textures
944         for(i = 0; i < dp_fonts.maxsize; ++i)
945                 if (dp_fonts.f[i].title[0])
946                         LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
947
948         // draw the loading screen so people have something to see in the newly opened window
949         SCR_UpdateLoadingScreen(true);
950 }
951
952 static void gl_draw_shutdown(void)
953 {
954         font_shutdown();
955
956         R_FreeTexturePool(&drawtexturepool);
957
958         numcachepics = 0;
959         memset(cachepichash, 0, sizeof(cachepichash));
960 }
961
962 static void gl_draw_newmap(void)
963 {
964         font_newmap();
965 }
966
967 void GL_Draw_Init (void)
968 {
969         int i, j;
970
971         Cvar_RegisterVariable(&r_font_postprocess_blur);
972         Cvar_RegisterVariable(&r_font_postprocess_outline);
973         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
974         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
975         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
976         Cvar_RegisterVariable(&r_font_hinting);
977         Cvar_RegisterVariable(&r_font_antialias);
978         Cvar_RegisterVariable(&r_textshadow);
979         Cvar_RegisterVariable(&r_textbrightness);
980         Cvar_RegisterVariable(&r_textcontrast);
981
982         // allocate fonts storage
983         fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
984         dp_fonts.maxsize = MAX_FONTS;
985         dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
986         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
987
988         // assign starting font names
989         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
990         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
991         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
992         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
993         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
994         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
995         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
996         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
997         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
998         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
999                 if(!FONT_USER(i)->title[0])
1000                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1001
1002         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1003         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1004 }
1005
1006 static void _DrawQ_Setup(void)
1007 {
1008         r_viewport_t viewport;
1009         if (r_refdef.draw2dstage == 1)
1010                 return;
1011         r_refdef.draw2dstage = 1;
1012         CHECKGLERROR
1013         R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
1014         R_SetViewport(&viewport);
1015         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1016         GL_DepthFunc(GL_LEQUAL);
1017         GL_PolygonOffset(0,0);
1018         GL_CullFace(GL_NONE);
1019         R_EntityMatrix(&identitymatrix);
1020
1021         GL_DepthRange(0, 1);
1022         GL_PolygonOffset(0, 0);
1023         GL_DepthTest(false);
1024         GL_Color(1,1,1,1);
1025         GL_AlphaTest(false);
1026 }
1027
1028 qboolean r_draw2d_force = false;
1029 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1030 {
1031         _DrawQ_Setup();
1032         CHECKGLERROR
1033         if(!r_draw2d.integer && !r_draw2d_force)
1034                 return;
1035         DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1036 }
1037 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1038 {
1039         if(flags == DRAWFLAG_ADDITIVE)
1040         {
1041                 GL_DepthMask(false);
1042                 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1043         }
1044         else if(flags == DRAWFLAG_MODULATE)
1045         {
1046                 GL_DepthMask(false);
1047                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1048         }
1049         else if(flags == DRAWFLAG_2XMODULATE)
1050         {
1051                 GL_DepthMask(false);
1052                 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1053         }
1054         else if(flags == DRAWFLAG_SCREEN)
1055         {
1056                 GL_DepthMask(false);
1057                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1058         }
1059         else if(alpha)
1060         {
1061                 GL_DepthMask(false);
1062                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1063         }
1064         else
1065         {
1066                 GL_DepthMask(true);
1067                 GL_BlendFunc(GL_ONE, GL_ZERO);
1068         }
1069 }
1070
1071 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1072 {
1073         float floats[36];
1074
1075         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1076         if(!r_draw2d.integer && !r_draw2d_force)
1077                 return;
1078
1079         R_Mesh_ResetTextureState();
1080         floats[12] = 0.0f;floats[13] = 0.0f;
1081         floats[14] = 1.0f;floats[15] = 0.0f;
1082         floats[16] = 1.0f;floats[17] = 1.0f;
1083         floats[18] = 0.0f;floats[19] = 1.0f;
1084         floats[20] = floats[24] = floats[28] = floats[32] = red;
1085         floats[21] = floats[25] = floats[29] = floats[33] = green;
1086         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1087         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1088         if (pic)
1089         {
1090                 if (width == 0)
1091                         width = pic->width;
1092                 if (height == 0)
1093                         height = pic->height;
1094                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1095
1096 #if 0
1097       // AK07: lets be texel correct on the corners
1098       {
1099          float horz_offset = 0.5f / pic->width;
1100          float vert_offset = 0.5f / pic->height;
1101
1102                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1103                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1104                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1105                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1106       }
1107 #endif
1108         }
1109         else
1110                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1111
1112         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1113         floats[0] = floats[9] = x;
1114         floats[1] = floats[4] = y;
1115         floats[3] = floats[6] = x + width;
1116         floats[7] = floats[10] = y + height;
1117
1118         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1119         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1120 }
1121
1122 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
1123 {
1124         float floats[36];
1125         float af = DEG2RAD(-angle); // forward
1126         float ar = DEG2RAD(-angle + 90); // right
1127         float sinaf = sin(af);
1128         float cosaf = cos(af);
1129         float sinar = sin(ar);
1130         float cosar = cos(ar);
1131
1132         _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1133         if(!r_draw2d.integer && !r_draw2d_force)
1134                 return;
1135
1136         R_Mesh_ResetTextureState();
1137         if (pic)
1138         {
1139                 if (width == 0)
1140                         width = pic->width;
1141                 if (height == 0)
1142                         height = pic->height;
1143                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1144         }
1145         else
1146                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1147
1148         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1149
1150 // top left
1151         floats[0] = x - cosaf*org_x - cosar*org_y;
1152         floats[1] = y - sinaf*org_x - sinar*org_y;
1153
1154 // top right
1155         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1156         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1157
1158 // bottom right
1159         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1160         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1161
1162 // bottom left
1163         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1164         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1165
1166         floats[12] = 0.0f;floats[13] = 0.0f;
1167         floats[14] = 1.0f;floats[15] = 0.0f;
1168         floats[16] = 1.0f;floats[17] = 1.0f;
1169         floats[18] = 0.0f;floats[19] = 1.0f;
1170         floats[20] = floats[24] = floats[28] = floats[32] = red;
1171         floats[21] = floats[25] = floats[29] = floats[33] = green;
1172         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1173         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1174
1175         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1176         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1177 }
1178
1179 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1180 {
1181         float floats[36];
1182
1183         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1184         if(!r_draw2d.integer && !r_draw2d_force)
1185                 return;
1186
1187         R_Mesh_ResetTextureState();
1188         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1189
1190         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1191         floats[0] = floats[9] = x;
1192         floats[1] = floats[4] = y;
1193         floats[3] = floats[6] = x + width;
1194         floats[7] = floats[10] = y + height;
1195         floats[12] = 0.0f;floats[13] = 0.0f;
1196         floats[14] = 1.0f;floats[15] = 0.0f;
1197         floats[16] = 1.0f;floats[17] = 1.0f;
1198         floats[18] = 0.0f;floats[19] = 1.0f;
1199         floats[20] = floats[24] = floats[28] = floats[32] = red;
1200         floats[21] = floats[25] = floats[29] = floats[33] = green;
1201         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1202         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1203
1204         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1205         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1206 }
1207
1208 /// color tag printing
1209 static const vec4_t string_colors[] =
1210 {
1211         // Quake3 colors
1212         // LordHavoc: why on earth is cyan before magenta in Quake3?
1213         // LordHavoc: note: Doom3 uses white for [0] and [7]
1214         {0.0, 0.0, 0.0, 1.0}, // black
1215         {1.0, 0.0, 0.0, 1.0}, // red
1216         {0.0, 1.0, 0.0, 1.0}, // green
1217         {1.0, 1.0, 0.0, 1.0}, // yellow
1218         {0.0, 0.0, 1.0, 1.0}, // blue
1219         {0.0, 1.0, 1.0, 1.0}, // cyan
1220         {1.0, 0.0, 1.0, 1.0}, // magenta
1221         {1.0, 1.0, 1.0, 1.0}, // white
1222         // [515]'s BX_COLOREDTEXT extension
1223         {1.0, 1.0, 1.0, 0.5}, // half transparent
1224         {0.5, 0.5, 0.5, 1.0}  // half brightness
1225         // Black's color table
1226         //{1.0, 1.0, 1.0, 1.0},
1227         //{1.0, 0.0, 0.0, 1.0},
1228         //{0.0, 1.0, 0.0, 1.0},
1229         //{0.0, 0.0, 1.0, 1.0},
1230         //{1.0, 1.0, 0.0, 1.0},
1231         //{0.0, 1.0, 1.0, 1.0},
1232         //{1.0, 0.0, 1.0, 1.0},
1233         //{0.1, 0.1, 0.1, 1.0}
1234 };
1235
1236 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1237
1238 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1239 {
1240         float C = r_textcontrast.value;
1241         float B = r_textbrightness.value;
1242         if (colorindex & 0x10000) // that bit means RGB color
1243         {
1244                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1245                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1246                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1247                 color[3] = (colorindex & 0xf) / 15.0;
1248         }
1249         else
1250                 Vector4Copy(string_colors[colorindex], color);
1251         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1252         if (shadow)
1253         {
1254                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1255                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1256         }
1257 }
1258
1259 // NOTE: this function always draws exactly one character if maxwidth <= 0
1260 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)
1261 {
1262         const char *text_start = text;
1263         int colorindex = STRING_COLOR_DEFAULT;
1264         size_t i;
1265         float x = 0;
1266         Uchar ch, mapch, nextch;
1267         Uchar prevch = 0; // used for kerning
1268         int tempcolorindex;
1269         float kx;
1270         int map_index = 0;
1271         size_t bytes_left;
1272         ft2_font_map_t *fontmap = NULL;
1273         ft2_font_map_t *map = NULL;
1274         //ft2_font_map_t *prevmap = NULL;
1275         ft2_font_t *ft2 = fnt->ft2;
1276         // float ftbase_x;
1277         qboolean snap = true;
1278         qboolean least_one = false;
1279         float dw; // display w
1280         //float dh; // display h
1281         const float *width_of;
1282
1283         if (!h) h = w;
1284         if (!h) {
1285                 w = h = 1;
1286                 snap = false;
1287         }
1288         // do this in the end
1289         w *= fnt->settings.scale;
1290         h *= fnt->settings.scale;
1291
1292         // find the most fitting size:
1293         if (ft2 != NULL)
1294         {
1295                 if (snap)
1296                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1297                 else
1298                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1299                 fontmap = Font_MapForIndex(ft2, map_index);
1300         }
1301
1302         dw = w * sw;
1303         //dh = h * sh;
1304
1305         if (*maxlen < 1)
1306                 *maxlen = 1<<30;
1307
1308         if (!outcolor || *outcolor == -1)
1309                 colorindex = STRING_COLOR_DEFAULT;
1310         else
1311                 colorindex = *outcolor;
1312
1313         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1314         // ftbase_x = snap_to_pixel_x(0);
1315         
1316         if(maxwidth <= 0)
1317         {
1318                 least_one = true;
1319                 maxwidth = -maxwidth;
1320         }
1321
1322         //if (snap)
1323         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1324
1325         if (fontmap)
1326                 width_of = fontmap->width_of;
1327         else
1328                 width_of = fnt->width_of;
1329
1330         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1331         {
1332                 size_t i0 = i;
1333                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1334                 i = text - text_start;
1335                 if (!ch)
1336                         break;
1337                 if (ch == ' ' && !fontmap)
1338                 {
1339                         if(!least_one || i0) // never skip the first character
1340                         if(x + width_of[(int) ' '] * dw > maxwidth)
1341                         {
1342                                 i = i0;
1343                                 break; // oops, can't draw this
1344                         }
1345                         x += width_of[(int) ' '] * dw;
1346                         continue;
1347                 }
1348                 // i points to the char after ^
1349                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1350                 {
1351                         ch = *text; // colors are ascii, so no u8_ needed
1352                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1353                         {
1354                                 colorindex = ch - '0';
1355                                 ++text;
1356                                 ++i;
1357                                 continue;
1358                         }
1359                         // i points to the char after ^...
1360                         // i+3 points to 3 in ^x123
1361                         // i+3 == *maxlen would mean that char is missing
1362                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1363                         {
1364                                 // building colorindex...
1365                                 ch = tolower(text[1]);
1366                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1367                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1368                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1369                                 else tempcolorindex = 0;
1370                                 if (tempcolorindex)
1371                                 {
1372                                         ch = tolower(text[2]);
1373                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1374                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1375                                         else tempcolorindex = 0;
1376                                         if (tempcolorindex)
1377                                         {
1378                                                 ch = tolower(text[3]);
1379                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1380                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1381                                                 else tempcolorindex = 0;
1382                                                 if (tempcolorindex)
1383                                                 {
1384                                                         colorindex = tempcolorindex | 0xf;
1385                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1386                                                         i+=4;
1387                                                         text += 4;
1388                                                         continue;
1389                                                 }
1390                                         }
1391                                 }
1392                         }
1393                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1394                         {
1395                                 i++;
1396                                 text++;
1397                         }
1398                         i--;
1399                 }
1400                 ch = nextch;
1401
1402                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1403                 {
1404                         if (ch > 0xE000)
1405                                 ch -= 0xE000;
1406                         if (ch > 0xFF)
1407                                 continue;
1408                         if (fontmap)
1409                                 map = ft2_oldstyle_map;
1410                         prevch = 0;
1411                         if(!least_one || i0) // never skip the first character
1412                         if(x + width_of[ch] * dw > maxwidth)
1413                         {
1414                                 i = i0;
1415                                 break; // oops, can't draw this
1416                         }
1417                         x += width_of[ch] * dw;
1418                 } else {
1419                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1420                         {
1421                                 map = FontMap_FindForChar(fontmap, ch);
1422                                 if (!map)
1423                                 {
1424                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1425                                                 break;
1426                                         if (!map)
1427                                                 break;
1428                                 }
1429                         }
1430                         mapch = ch - map->start;
1431                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1432                                 x += kx * dw;
1433                         x += map->glyphs[mapch].advance_x * dw;
1434                         //prevmap = map;
1435                         prevch = ch;
1436                 }
1437         }
1438
1439         *maxlen = i;
1440
1441         if (outcolor)
1442                 *outcolor = colorindex;
1443
1444         return x;
1445 }
1446
1447 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)
1448 {
1449         int shadow, colorindex = STRING_COLOR_DEFAULT;
1450         size_t i;
1451         float x = startx, y, s, t, u, v, thisw;
1452         float *av, *at, *ac;
1453         float color[4];
1454         int batchcount;
1455         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1456         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1457         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1458         Uchar ch, mapch, nextch;
1459         Uchar prevch = 0; // used for kerning
1460         int tempcolorindex;
1461         int map_index = 0;
1462         //ft2_font_map_t *prevmap = NULL; // the previous map
1463         ft2_font_map_t *map = NULL;     // the currently used map
1464         ft2_font_map_t *fontmap = NULL; // the font map for the size
1465         float ftbase_y;
1466         const char *text_start = text;
1467         float kx, ky;
1468         ft2_font_t *ft2 = fnt->ft2;
1469         qboolean snap = true;
1470         float pix_x, pix_y;
1471         size_t bytes_left;
1472         float dw, dh;
1473         const float *width_of;
1474
1475         int tw, th;
1476         tw = R_TextureWidth(fnt->tex);
1477         th = R_TextureHeight(fnt->tex);
1478
1479         if (!h) h = w;
1480         if (!h) {
1481                 h = w = 1;
1482                 snap = false;
1483         }
1484
1485         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1486         w *= fnt->settings.scale;
1487         h *= fnt->settings.scale;
1488
1489         if (ft2 != NULL)
1490         {
1491                 if (snap)
1492                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1493                 else
1494                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1495                 fontmap = Font_MapForIndex(ft2, map_index);
1496         }
1497
1498         dw = w * sw;
1499         dh = h * sh;
1500
1501         // draw the font at its baseline when using freetype
1502         //ftbase_x = 0;
1503         ftbase_y = dh * (4.5/6.0);
1504
1505         if (maxlen < 1)
1506                 maxlen = 1<<30;
1507
1508         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1509         if(!r_draw2d.integer && !r_draw2d_force)
1510                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1511
1512         R_Mesh_ResetTextureState();
1513         if (!fontmap)
1514                 R_Mesh_TexBind(0, fnt->tex);
1515         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1516
1517         ac = color4f;
1518         at = texcoord2f;
1519         av = vertex3f;
1520         batchcount = 0;
1521
1522         //ftbase_x = snap_to_pixel_x(ftbase_x);
1523         if(snap)
1524         {
1525                 startx = snap_to_pixel_x(startx, 0.4);
1526                 starty = snap_to_pixel_y(starty, 0.4);
1527                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1528         }
1529
1530         pix_x = vid.width / vid_conwidth.value;
1531         pix_y = vid.height / vid_conheight.value;
1532
1533         if (fontmap)
1534                 width_of = fontmap->width_of;
1535         else
1536                 width_of = fnt->width_of;
1537
1538         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1539         {
1540                 prevch = 0;
1541                 text = text_start;
1542
1543                 if (!outcolor || *outcolor == -1)
1544                         colorindex = STRING_COLOR_DEFAULT;
1545                 else
1546                         colorindex = *outcolor;
1547
1548                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1549
1550                 x = startx;
1551                 y = starty;
1552                 /*
1553                 if (shadow)
1554                 {
1555                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1556                         y += r_textshadow.value * vid.height / vid_conheight.value;
1557                 }
1558                 */
1559                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1560                 {
1561                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1562                         i = text - text_start;
1563                         if (!ch)
1564                                 break;
1565                         if (ch == ' ' && !fontmap)
1566                         {
1567                                 x += width_of[(int) ' '] * dw;
1568                                 continue;
1569                         }
1570                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1571                         {
1572                                 ch = *text; // colors are ascii, so no u8_ needed
1573                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1574                                 {
1575                                         colorindex = ch - '0';
1576                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1577                                         ++text;
1578                                         ++i;
1579                                         continue;
1580                                 }
1581                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1582                                 {
1583                                         // building colorindex...
1584                                         ch = tolower(text[1]);
1585                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1586                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1587                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1588                                         else tempcolorindex = 0;
1589                                         if (tempcolorindex)
1590                                         {
1591                                                 ch = tolower(text[2]);
1592                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1593                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1594                                                 else tempcolorindex = 0;
1595                                                 if (tempcolorindex)
1596                                                 {
1597                                                         ch = tolower(text[3]);
1598                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1599                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1600                                                         else tempcolorindex = 0;
1601                                                         if (tempcolorindex)
1602                                                         {
1603                                                                 colorindex = tempcolorindex | 0xf;
1604                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1605                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1606                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1607                                                                 i+=4;
1608                                                                 text+=4;
1609                                                                 continue;
1610                                                         }
1611                                                 }
1612                                         }
1613                                 }
1614                                 else if (ch == STRING_COLOR_TAG)
1615                                 {
1616                                         i++;
1617                                         text++;
1618                                 }
1619                                 i--;
1620                         }
1621                         // get the backup
1622                         ch = nextch;
1623                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1624                         // this way we don't need to rebind fnt->tex for every old-style character
1625                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1626                         if (shadow)
1627                         {
1628                                 x += 1.0/pix_x * r_textshadow.value;
1629                                 y += 1.0/pix_y * r_textshadow.value;
1630                         }
1631                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1632                         {
1633                                 if (ch > 0xE000)
1634                                         ch -= 0xE000;
1635                                 if (ch > 0xFF)
1636                                         continue;
1637                                 if (fontmap)
1638                                 {
1639                                         if (map != ft2_oldstyle_map)
1640                                         {
1641                                                 if (batchcount)
1642                                                 {
1643                                                         // switching from freetype to non-freetype rendering
1644                                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1645                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1646                                                         batchcount = 0;
1647                                                         ac = color4f;
1648                                                         at = texcoord2f;
1649                                                         av = vertex3f;
1650                                                 }
1651                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1652                                                 map = ft2_oldstyle_map;
1653                                         }
1654                                 }
1655                                 prevch = 0;
1656                                 //num = (unsigned char) text[i];
1657                                 //thisw = fnt->width_of[num];
1658                                 thisw = fnt->width_of[ch];
1659                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1660                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1661                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1662                                 u = 0.0625f * thisw - (1.0f / tw);
1663                                 v = 0.0625f - (1.0f / th);
1664                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1665                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1666                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1667                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1668                                 at[ 0] = s              ; at[ 1] = t    ;
1669                                 at[ 2] = s+u    ; at[ 3] = t    ;
1670                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1671                                 at[ 6] = s              ; at[ 7] = t+v  ;
1672                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1673                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1674                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1675                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1676                                 ac += 16;
1677                                 at += 8;
1678                                 av += 12;
1679                                 batchcount++;
1680                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1681                                 {
1682                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1683                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1684                                         batchcount = 0;
1685                                         ac = color4f;
1686                                         at = texcoord2f;
1687                                         av = vertex3f;
1688                                 }
1689                                 x += width_of[ch] * dw;
1690                         } else {
1691                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1692                                 {
1693                                         // new charmap - need to render
1694                                         if (batchcount)
1695                                         {
1696                                                 // we need a different character map, render what we currently have:
1697                                                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1698                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1699                                                 batchcount = 0;
1700                                                 ac = color4f;
1701                                                 at = texcoord2f;
1702                                                 av = vertex3f;
1703                                         }
1704                                         // find the new map
1705                                         map = FontMap_FindForChar(fontmap, ch);
1706                                         if (!map)
1707                                         {
1708                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1709                                                 {
1710                                                         shadow = -1;
1711                                                         break;
1712                                                 }
1713                                                 if (!map)
1714                                                 {
1715                                                         // this shouldn't happen
1716                                                         shadow = -1;
1717                                                         break;
1718                                                 }
1719                                         }
1720                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1721                                 }
1722
1723                                 mapch = ch - map->start;
1724                                 thisw = map->glyphs[mapch].advance_x;
1725
1726                                 //x += ftbase_x;
1727                                 y += ftbase_y;
1728                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1729                                 {
1730                                         x += kx * dw;
1731                                         y += ky * dh;
1732                                 }
1733                                 else
1734                                         kx = ky = 0;
1735                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1736                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1737                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1738                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1739                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1740                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1741                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1742                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1743                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1744                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1745                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1746                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1747                                 //x -= ftbase_x;
1748                                 y -= ftbase_y;
1749
1750                                 x += thisw * dw;
1751                                 ac += 16;
1752                                 at += 8;
1753                                 av += 12;
1754                                 batchcount++;
1755                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1756                                 {
1757                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1758                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1759                                         batchcount = 0;
1760                                         ac = color4f;
1761                                         at = texcoord2f;
1762                                         av = vertex3f;
1763                                 }
1764
1765                                 //prevmap = map;
1766                                 prevch = ch;
1767                         }
1768                         if (shadow)
1769                         {
1770                                 x -= 1.0/pix_x * r_textshadow.value;
1771                                 y -= 1.0/pix_y * r_textshadow.value;
1772                         }
1773                 }
1774         }
1775         if (batchcount > 0)
1776         {
1777                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1778                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1779         }
1780
1781         if (outcolor)
1782                 *outcolor = colorindex;
1783
1784         // note: this relies on the proper text (not shadow) being drawn last
1785         return x;
1786 }
1787
1788 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)
1789 {
1790         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1791 }
1792
1793 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)
1794 {
1795         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1796 }
1797
1798 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1799 {
1800         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1801 }
1802
1803 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1804 {
1805         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1806 }
1807
1808 #if 0
1809 // not used
1810 // no ^xrgb management
1811 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1812 {
1813         int color, numchars = 0;
1814         char *outputend2c = output2c + maxoutchars - 2;
1815         if (!outcolor || *outcolor == -1)
1816                 color = STRING_COLOR_DEFAULT;
1817         else
1818                 color = *outcolor;
1819         if (!maxreadchars)
1820                 maxreadchars = 1<<30;
1821         textend = text + maxreadchars;
1822         while (text != textend && *text)
1823         {
1824                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1825                 {
1826                         if (text[1] == STRING_COLOR_TAG)
1827                                 text++;
1828                         else if (text[1] >= '0' && text[1] <= '9')
1829                         {
1830                                 color = text[1] - '0';
1831                                 text += 2;
1832                                 continue;
1833                         }
1834                 }
1835                 if (output2c >= outputend2c)
1836                         break;
1837                 *output2c++ = *text++;
1838                 *output2c++ = color;
1839                 numchars++;
1840         }
1841         output2c[0] = output2c[1] = 0;
1842         if (outcolor)
1843                 *outcolor = color;
1844         return numchars;
1845 }
1846 #endif
1847
1848 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)
1849 {
1850         float floats[36];
1851
1852         _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1853         if(!r_draw2d.integer && !r_draw2d_force)
1854                 return;
1855
1856         R_Mesh_ResetTextureState();
1857         if (pic)
1858         {
1859                 if (width == 0)
1860                         width = pic->width;
1861                 if (height == 0)
1862                         height = pic->height;
1863                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1864         }
1865         else
1866                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1867
1868         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1869         floats[0] = floats[9] = x;
1870         floats[1] = floats[4] = y;
1871         floats[3] = floats[6] = x + width;
1872         floats[7] = floats[10] = y + height;
1873         floats[12] = s1;floats[13] = t1;
1874         floats[14] = s2;floats[15] = t2;
1875         floats[16] = s4;floats[17] = t4;
1876         floats[18] = s3;floats[19] = t3;
1877         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1878         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1879         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1880         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1881
1882         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1883         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1884 }
1885
1886 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1887 {
1888         _DrawQ_Setup();
1889         CHECKGLERROR
1890         if(!r_draw2d.integer && !r_draw2d_force)
1891                 return;
1892         DrawQ_ProcessDrawFlag(flags, hasalpha);
1893
1894         R_Mesh_ResetTextureState();
1895         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1896
1897         R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1898         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1899 }
1900
1901 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1902 {
1903         int num;
1904
1905         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1906         if(!r_draw2d.integer && !r_draw2d_force)
1907                 return;
1908
1909         GL_Color(1,1,1,1);
1910         switch(vid.renderpath)
1911         {
1912         case RENDERPATH_GL11:
1913         case RENDERPATH_GL13:
1914         case RENDERPATH_GL20:
1915         case RENDERPATH_CGGL:
1916                 CHECKGLERROR
1917                 qglBegin(GL_LINE_LOOP);
1918                 for (num = 0;num < mesh->num_vertices;num++)
1919                 {
1920                         if (mesh->data_color4f)
1921                                 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]);
1922                         qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1923                 }
1924                 qglEnd();
1925                 CHECKGLERROR
1926                 break;
1927         case RENDERPATH_D3D9:
1928                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1929                 break;
1930         case RENDERPATH_D3D10:
1931                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1932                 break;
1933         case RENDERPATH_D3D11:
1934                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1935                 break;
1936         }
1937 }
1938
1939 //[515]: this is old, delete
1940 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1941 {
1942         _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1943         if(!r_draw2d.integer && !r_draw2d_force)
1944                 return;
1945
1946         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1947
1948         switch(vid.renderpath)
1949         {
1950         case RENDERPATH_GL11:
1951         case RENDERPATH_GL13:
1952         case RENDERPATH_GL20:
1953         case RENDERPATH_CGGL:
1954                 CHECKGLERROR
1955
1956                 //qglLineWidth(width);CHECKGLERROR
1957
1958                 GL_Color(r,g,b,alpha);
1959                 CHECKGLERROR
1960                 qglBegin(GL_LINES);
1961                 qglVertex2f(x1, y1);
1962                 qglVertex2f(x2, y2);
1963                 qglEnd();
1964                 CHECKGLERROR
1965                 break;
1966         case RENDERPATH_D3D9:
1967                 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1968                 break;
1969         case RENDERPATH_D3D10:
1970                 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1971                 break;
1972         case RENDERPATH_D3D11:
1973                 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1974                 break;
1975         }
1976 }
1977
1978 void DrawQ_SetClipArea(float x, float y, float width, float height)
1979 {
1980         int ix, iy, iw, ih;
1981         _DrawQ_Setup();
1982
1983         // We have to convert the con coords into real coords
1984         // OGL uses top to bottom
1985         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1986         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1987         iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1988         ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1989         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1990
1991         GL_ScissorTest(true);
1992 }
1993
1994 void DrawQ_ResetClipArea(void)
1995 {
1996         _DrawQ_Setup();
1997         GL_ScissorTest(false);
1998 }
1999
2000 void DrawQ_Finish(void)
2001 {
2002         r_refdef.draw2dstage = 0;
2003 }
2004
2005 void DrawQ_RecalcView(void)
2006 {
2007         if(r_refdef.draw2dstage)
2008                 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2009 }
2010
2011 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2012 void R_DrawGamma(void)
2013 {
2014         float c[4];
2015         switch(vid.renderpath)
2016         {
2017         case RENDERPATH_GL20:
2018         case RENDERPATH_CGGL:
2019         case RENDERPATH_D3D9:
2020         case RENDERPATH_D3D10:
2021         case RENDERPATH_D3D11:
2022                 if (vid_usinghwgamma || v_glslgamma.integer)
2023                         return;
2024                 break;
2025         case RENDERPATH_GL13:
2026         case RENDERPATH_GL11:
2027                 if (vid_usinghwgamma)
2028                         return;
2029                 break;
2030         }
2031         // all the blends ignore depth
2032         R_Mesh_ResetTextureState();
2033         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2034         GL_DepthMask(true);
2035         GL_DepthRange(0, 1);
2036         GL_PolygonOffset(0, 0);
2037         GL_DepthTest(false);
2038         if (v_color_enable.integer)
2039         {
2040                 c[0] = v_color_white_r.value;
2041                 c[1] = v_color_white_g.value;
2042                 c[2] = v_color_white_b.value;
2043         }
2044         else
2045                 c[0] = c[1] = c[2] = v_contrast.value;
2046         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2047         {
2048                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2049                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2050                 {
2051                         GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2052                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2053                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2054                         VectorScale(c, 0.5, c);
2055                 }
2056         }
2057         if (v_color_enable.integer)
2058         {
2059                 c[0] = v_color_black_r.value;
2060                 c[1] = v_color_black_g.value;
2061                 c[2] = v_color_black_b.value;
2062         }
2063         else
2064                 c[0] = c[1] = c[2] = v_brightness.value;
2065         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2066         {
2067                 GL_BlendFunc(GL_ONE, GL_ONE);
2068                 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2069                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2070                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2071         }
2072 }
2073