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