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