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