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