]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
add DeviceLost and DeviceRestored functions to R_Modules system
[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, NULL, NULL);
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 qboolean r_draw2d_force = false;
987 static void _DrawQ_ProcessDrawFlag(int flags)
988 {
989         _DrawQ_Setup();
990         CHECKGLERROR
991         if(!r_draw2d.integer && !r_draw2d_force)
992                 return;
993         if(flags == DRAWFLAG_ADDITIVE)
994                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
995         else if(flags == DRAWFLAG_MODULATE)
996                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
997         else if(flags == DRAWFLAG_2XMODULATE)
998                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
999         else if(flags == DRAWFLAG_SCREEN)
1000                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
1001         else
1002                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1003 }
1004
1005 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1006 {
1007         float floats[20];
1008
1009         _DrawQ_ProcessDrawFlag(flags);
1010         if(!r_draw2d.integer && !r_draw2d_force)
1011                 return;
1012
1013         GL_Color(red, green, blue, alpha);
1014
1015         R_Mesh_VertexPointer(floats, 0, 0);
1016         R_Mesh_ColorPointer(NULL, 0, 0);
1017         R_Mesh_ResetTextureState();
1018         if (pic)
1019         {
1020                 if (width == 0)
1021                         width = pic->width;
1022                 if (height == 0)
1023                         height = pic->height;
1024                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1025                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1026
1027 #if 1
1028                 floats[12] = 0.0f;floats[13] = 0.0f;
1029                 floats[14] = 1.0f;floats[15] = 0.0f;
1030                 floats[16] = 1.0f;floats[17] = 1.0f;
1031                 floats[18] = 0.0f;floats[19] = 1.0f;
1032 #else
1033       // AK07: lets be texel correct on the corners
1034       {
1035          float horz_offset = 0.5f / pic->width;
1036          float vert_offset = 0.5f / pic->height;
1037
1038                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1039                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1040                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1041                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1042       }
1043 #endif
1044         }
1045         else
1046                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1047
1048         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1049         floats[0] = floats[9] = x;
1050         floats[1] = floats[4] = y;
1051         floats[3] = floats[6] = x + width;
1052         floats[7] = floats[10] = y + height;
1053
1054         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1055 }
1056
1057 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)
1058 {
1059         float floats[20];
1060         float af = DEG2RAD(-angle); // forward
1061         float ar = DEG2RAD(-angle + 90); // right
1062         float sinaf = sin(af);
1063         float cosaf = cos(af);
1064         float sinar = sin(ar);
1065         float cosar = cos(ar);
1066
1067         _DrawQ_ProcessDrawFlag(flags);
1068         if(!r_draw2d.integer && !r_draw2d_force)
1069                 return;
1070
1071         GL_Color(red, green, blue, alpha);
1072
1073         R_Mesh_VertexPointer(floats, 0, 0);
1074         R_Mesh_ColorPointer(NULL, 0, 0);
1075         R_Mesh_ResetTextureState();
1076         if (pic)
1077         {
1078                 if (width == 0)
1079                         width = pic->width;
1080                 if (height == 0)
1081                         height = pic->height;
1082                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1083                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1084
1085                 floats[12] = 0.0f;floats[13] = 0.0f;
1086                 floats[14] = 1.0f;floats[15] = 0.0f;
1087                 floats[16] = 1.0f;floats[17] = 1.0f;
1088                 floats[18] = 0.0f;floats[19] = 1.0f;
1089         }
1090         else
1091                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1092
1093         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1094
1095 // top left
1096         floats[0] = x - cosaf*org_x - cosar*org_y;
1097         floats[1] = y - sinaf*org_x - sinar*org_y;
1098
1099 // top right
1100         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1101         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1102
1103 // bottom right
1104         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1105         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1106
1107 // bottom left
1108         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1109         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1110
1111         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1112 }
1113
1114 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1115 {
1116         float floats[12];
1117
1118         _DrawQ_ProcessDrawFlag(flags);
1119         if(!r_draw2d.integer && !r_draw2d_force)
1120                 return;
1121
1122         GL_Color(red, green, blue, alpha);
1123
1124         R_Mesh_VertexPointer(floats, 0, 0);
1125         R_Mesh_ColorPointer(NULL, 0, 0);
1126         R_Mesh_ResetTextureState();
1127         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1128
1129         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1130         floats[0] = floats[9] = x;
1131         floats[1] = floats[4] = y;
1132         floats[3] = floats[6] = x + width;
1133         floats[7] = floats[10] = y + height;
1134
1135         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1136 }
1137
1138 /// color tag printing
1139 static const vec4_t string_colors[] =
1140 {
1141         // Quake3 colors
1142         // LordHavoc: why on earth is cyan before magenta in Quake3?
1143         // LordHavoc: note: Doom3 uses white for [0] and [7]
1144         {0.0, 0.0, 0.0, 1.0}, // black
1145         {1.0, 0.0, 0.0, 1.0}, // red
1146         {0.0, 1.0, 0.0, 1.0}, // green
1147         {1.0, 1.0, 0.0, 1.0}, // yellow
1148         {0.0, 0.0, 1.0, 1.0}, // blue
1149         {0.0, 1.0, 1.0, 1.0}, // cyan
1150         {1.0, 0.0, 1.0, 1.0}, // magenta
1151         {1.0, 1.0, 1.0, 1.0}, // white
1152         // [515]'s BX_COLOREDTEXT extension
1153         {1.0, 1.0, 1.0, 0.5}, // half transparent
1154         {0.5, 0.5, 0.5, 1.0}  // half brightness
1155         // Black's color table
1156         //{1.0, 1.0, 1.0, 1.0},
1157         //{1.0, 0.0, 0.0, 1.0},
1158         //{0.0, 1.0, 0.0, 1.0},
1159         //{0.0, 0.0, 1.0, 1.0},
1160         //{1.0, 1.0, 0.0, 1.0},
1161         //{0.0, 1.0, 1.0, 1.0},
1162         //{1.0, 0.0, 1.0, 1.0},
1163         //{0.1, 0.1, 0.1, 1.0}
1164 };
1165
1166 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1167
1168 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1169 {
1170         float C = r_textcontrast.value;
1171         float B = r_textbrightness.value;
1172         if (colorindex & 0x10000) // that bit means RGB color
1173         {
1174                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1175                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1176                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1177                 color[3] = (colorindex & 0xf) / 15.0;
1178         }
1179         else
1180                 Vector4Copy(string_colors[colorindex], color);
1181         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1182         if (shadow)
1183         {
1184                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1185                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1186         }
1187 }
1188
1189 // NOTE: this function always draws exactly one character if maxwidth <= 0
1190 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)
1191 {
1192         const char *text_start = text;
1193         int colorindex = STRING_COLOR_DEFAULT;
1194         size_t i;
1195         float x = 0;
1196         Uchar ch, mapch, nextch;
1197         Uchar prevch = 0; // used for kerning
1198         int tempcolorindex;
1199         float kx;
1200         int map_index = 0;
1201         size_t bytes_left;
1202         ft2_font_map_t *fontmap = NULL;
1203         ft2_font_map_t *map = NULL;
1204         //ft2_font_map_t *prevmap = NULL;
1205         ft2_font_t *ft2 = fnt->ft2;
1206         // float ftbase_x;
1207         qboolean snap = true;
1208         qboolean least_one = false;
1209         float dw; // display w
1210         //float dh; // display h
1211         const float *width_of;
1212
1213         if (!h) h = w;
1214         if (!h) {
1215                 w = h = 1;
1216                 snap = false;
1217         }
1218         // do this in the end
1219         w *= fnt->settings.scale;
1220         h *= fnt->settings.scale;
1221
1222         // find the most fitting size:
1223         if (ft2 != NULL)
1224         {
1225                 if (snap)
1226                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1227                 else
1228                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1229                 fontmap = Font_MapForIndex(ft2, map_index);
1230         }
1231
1232         dw = w * sw;
1233         //dh = h * sh;
1234
1235         if (*maxlen < 1)
1236                 *maxlen = 1<<30;
1237
1238         if (!outcolor || *outcolor == -1)
1239                 colorindex = STRING_COLOR_DEFAULT;
1240         else
1241                 colorindex = *outcolor;
1242
1243         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1244         // ftbase_x = snap_to_pixel_x(0);
1245         
1246         if(maxwidth <= 0)
1247         {
1248                 least_one = true;
1249                 maxwidth = -maxwidth;
1250         }
1251
1252         //if (snap)
1253         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1254
1255         if (fontmap)
1256                 width_of = fontmap->width_of;
1257         else
1258                 width_of = fnt->width_of;
1259
1260         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1261         {
1262                 size_t i0 = i;
1263                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1264                 i = text - text_start;
1265                 if (!ch)
1266                         break;
1267                 if (ch == ' ' && !fontmap)
1268                 {
1269                         if(!least_one || i0) // never skip the first character
1270                         if(x + width_of[(int) ' '] * dw > maxwidth)
1271                         {
1272                                 i = i0;
1273                                 break; // oops, can't draw this
1274                         }
1275                         x += width_of[(int) ' '] * dw;
1276                         continue;
1277                 }
1278                 // i points to the char after ^
1279                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1280                 {
1281                         ch = *text; // colors are ascii, so no u8_ needed
1282                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1283                         {
1284                                 colorindex = ch - '0';
1285                                 ++text;
1286                                 ++i;
1287                                 continue;
1288                         }
1289                         // i points to the char after ^...
1290                         // i+3 points to 3 in ^x123
1291                         // i+3 == *maxlen would mean that char is missing
1292                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1293                         {
1294                                 // building colorindex...
1295                                 ch = tolower(text[1]);
1296                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1297                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1298                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1299                                 else tempcolorindex = 0;
1300                                 if (tempcolorindex)
1301                                 {
1302                                         ch = tolower(text[2]);
1303                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1304                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1305                                         else tempcolorindex = 0;
1306                                         if (tempcolorindex)
1307                                         {
1308                                                 ch = tolower(text[3]);
1309                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1310                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1311                                                 else tempcolorindex = 0;
1312                                                 if (tempcolorindex)
1313                                                 {
1314                                                         colorindex = tempcolorindex | 0xf;
1315                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1316                                                         i+=4;
1317                                                         text += 4;
1318                                                         continue;
1319                                                 }
1320                                         }
1321                                 }
1322                         }
1323                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1324                         {
1325                                 i++;
1326                                 text++;
1327                         }
1328                         i--;
1329                 }
1330                 ch = nextch;
1331
1332                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1333                 {
1334                         if (ch > 0xE000)
1335                                 ch -= 0xE000;
1336                         if (ch > 0xFF)
1337                                 continue;
1338                         if (fontmap)
1339                                 map = ft2_oldstyle_map;
1340                         prevch = 0;
1341                         if(!least_one || i0) // never skip the first character
1342                         if(x + width_of[ch] * dw > maxwidth)
1343                         {
1344                                 i = i0;
1345                                 break; // oops, can't draw this
1346                         }
1347                         x += width_of[ch] * dw;
1348                 } else {
1349                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1350                         {
1351                                 map = FontMap_FindForChar(fontmap, ch);
1352                                 if (!map)
1353                                 {
1354                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1355                                                 break;
1356                                         if (!map)
1357                                                 break;
1358                                 }
1359                         }
1360                         mapch = ch - map->start;
1361                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1362                                 x += kx * dw;
1363                         x += map->glyphs[mapch].advance_x * dw;
1364                         //prevmap = map;
1365                         prevch = ch;
1366                 }
1367         }
1368
1369         *maxlen = i;
1370
1371         if (outcolor)
1372                 *outcolor = colorindex;
1373
1374         return x;
1375 }
1376
1377 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)
1378 {
1379         int shadow, colorindex = STRING_COLOR_DEFAULT;
1380         size_t i;
1381         float x = startx, y, s, t, u, v, thisw;
1382         float *av, *at, *ac;
1383         float color[4];
1384         int batchcount;
1385         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1386         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1387         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1388         Uchar ch, mapch, nextch;
1389         Uchar prevch = 0; // used for kerning
1390         int tempcolorindex;
1391         int map_index = 0;
1392         //ft2_font_map_t *prevmap = NULL; // the previous map
1393         ft2_font_map_t *map = NULL;     // the currently used map
1394         ft2_font_map_t *fontmap = NULL; // the font map for the size
1395         float ftbase_y;
1396         const char *text_start = text;
1397         float kx, ky;
1398         ft2_font_t *ft2 = fnt->ft2;
1399         qboolean snap = true;
1400         float pix_x, pix_y;
1401         size_t bytes_left;
1402         float dw, dh;
1403         const float *width_of;
1404
1405         int tw, th;
1406         tw = R_TextureWidth(fnt->tex);
1407         th = R_TextureHeight(fnt->tex);
1408
1409         if (!h) h = w;
1410         if (!h) {
1411                 h = w = 1;
1412                 snap = false;
1413         }
1414
1415         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1416         w *= fnt->settings.scale;
1417         h *= fnt->settings.scale;
1418
1419         if (ft2 != NULL)
1420         {
1421                 if (snap)
1422                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1423                 else
1424                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1425                 fontmap = Font_MapForIndex(ft2, map_index);
1426         }
1427
1428         dw = w * sw;
1429         dh = h * sh;
1430
1431         // draw the font at its baseline when using freetype
1432         //ftbase_x = 0;
1433         ftbase_y = dh * (4.5/6.0);
1434
1435         if (maxlen < 1)
1436                 maxlen = 1<<30;
1437
1438         _DrawQ_ProcessDrawFlag(flags);
1439         if(!r_draw2d.integer && !r_draw2d_force)
1440                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1441
1442         R_Mesh_ColorPointer(color4f, 0, 0);
1443         R_Mesh_ResetTextureState();
1444         if (!fontmap)
1445                 R_Mesh_TexBind(0, fnt->tex);
1446         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1447         R_Mesh_VertexPointer(vertex3f, 0, 0);
1448         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1449
1450         ac = color4f;
1451         at = texcoord2f;
1452         av = vertex3f;
1453         batchcount = 0;
1454
1455         //ftbase_x = snap_to_pixel_x(ftbase_x);
1456         if(snap)
1457         {
1458                 startx = snap_to_pixel_x(startx, 0.4);
1459                 starty = snap_to_pixel_y(starty, 0.4);
1460                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1461         }
1462
1463         pix_x = vid.width / vid_conwidth.value;
1464         pix_y = vid.height / vid_conheight.value;
1465
1466         if (fontmap)
1467                 width_of = fontmap->width_of;
1468         else
1469                 width_of = fnt->width_of;
1470
1471         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1472         {
1473                 prevch = 0;
1474                 text = text_start;
1475
1476                 if (!outcolor || *outcolor == -1)
1477                         colorindex = STRING_COLOR_DEFAULT;
1478                 else
1479                         colorindex = *outcolor;
1480
1481                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1482
1483                 x = startx;
1484                 y = starty;
1485                 /*
1486                 if (shadow)
1487                 {
1488                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1489                         y += r_textshadow.value * vid.height / vid_conheight.value;
1490                 }
1491                 */
1492                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1493                 {
1494                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1495                         i = text - text_start;
1496                         if (!ch)
1497                                 break;
1498                         if (ch == ' ' && !fontmap)
1499                         {
1500                                 x += width_of[(int) ' '] * dw;
1501                                 continue;
1502                         }
1503                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1504                         {
1505                                 ch = *text; // colors are ascii, so no u8_ needed
1506                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1507                                 {
1508                                         colorindex = ch - '0';
1509                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1510                                         ++text;
1511                                         ++i;
1512                                         continue;
1513                                 }
1514                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1515                                 {
1516                                         // building colorindex...
1517                                         ch = tolower(text[1]);
1518                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1519                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1520                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1521                                         else tempcolorindex = 0;
1522                                         if (tempcolorindex)
1523                                         {
1524                                                 ch = tolower(text[2]);
1525                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1526                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1527                                                 else tempcolorindex = 0;
1528                                                 if (tempcolorindex)
1529                                                 {
1530                                                         ch = tolower(text[3]);
1531                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1532                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1533                                                         else tempcolorindex = 0;
1534                                                         if (tempcolorindex)
1535                                                         {
1536                                                                 colorindex = tempcolorindex | 0xf;
1537                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1538                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1539                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1540                                                                 i+=4;
1541                                                                 text+=4;
1542                                                                 continue;
1543                                                         }
1544                                                 }
1545                                         }
1546                                 }
1547                                 else if (ch == STRING_COLOR_TAG)
1548                                 {
1549                                         i++;
1550                                         text++;
1551                                 }
1552                                 i--;
1553                         }
1554                         // get the backup
1555                         ch = nextch;
1556                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1557                         // this way we don't need to rebind fnt->tex for every old-style character
1558                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1559                         if (shadow)
1560                         {
1561                                 x += 1.0/pix_x * r_textshadow.value;
1562                                 y += 1.0/pix_y * r_textshadow.value;
1563                         }
1564                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1565                         {
1566                                 if (ch > 0xE000)
1567                                         ch -= 0xE000;
1568                                 if (ch > 0xFF)
1569                                         continue;
1570                                 if (fontmap)
1571                                 {
1572                                         if (map != ft2_oldstyle_map)
1573                                         {
1574                                                 if (batchcount)
1575                                                 {
1576                                                         // switching from freetype to non-freetype rendering
1577                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1578                                                         batchcount = 0;
1579                                                         ac = color4f;
1580                                                         at = texcoord2f;
1581                                                         av = vertex3f;
1582                                                 }
1583                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1584                                                 map = ft2_oldstyle_map;
1585                                         }
1586                                 }
1587                                 prevch = 0;
1588                                 //num = (unsigned char) text[i];
1589                                 //thisw = fnt->width_of[num];
1590                                 thisw = fnt->width_of[ch];
1591                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1592                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1593                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1594                                 u = 0.0625f * thisw - (1.0f / tw);
1595                                 v = 0.0625f - (1.0f / th);
1596                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1597                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1598                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1599                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1600                                 at[ 0] = s              ; at[ 1] = t    ;
1601                                 at[ 2] = s+u    ; at[ 3] = t    ;
1602                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1603                                 at[ 6] = s              ; at[ 7] = t+v  ;
1604                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1605                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1606                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1607                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1608                                 ac += 16;
1609                                 at += 8;
1610                                 av += 12;
1611                                 batchcount++;
1612                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1613                                 {
1614                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1615                                         batchcount = 0;
1616                                         ac = color4f;
1617                                         at = texcoord2f;
1618                                         av = vertex3f;
1619                                 }
1620                                 x += width_of[ch] * dw;
1621                         } else {
1622                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1623                                 {
1624                                         // new charmap - need to render
1625                                         if (batchcount)
1626                                         {
1627                                                 // we need a different character map, render what we currently have:
1628                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1629                                                 batchcount = 0;
1630                                                 ac = color4f;
1631                                                 at = texcoord2f;
1632                                                 av = vertex3f;
1633                                         }
1634                                         // find the new map
1635                                         map = FontMap_FindForChar(fontmap, ch);
1636                                         if (!map)
1637                                         {
1638                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1639                                                 {
1640                                                         shadow = -1;
1641                                                         break;
1642                                                 }
1643                                                 if (!map)
1644                                                 {
1645                                                         // this shouldn't happen
1646                                                         shadow = -1;
1647                                                         break;
1648                                                 }
1649                                         }
1650                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1651                                 }
1652
1653                                 mapch = ch - map->start;
1654                                 thisw = map->glyphs[mapch].advance_x;
1655
1656                                 //x += ftbase_x;
1657                                 y += ftbase_y;
1658                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1659                                 {
1660                                         x += kx * dw;
1661                                         y += ky * dh;
1662                                 }
1663                                 else
1664                                         kx = ky = 0;
1665                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1666                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1667                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1668                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1669                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1670                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1671                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1672                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1673                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1674                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1675                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1676                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1677                                 //x -= ftbase_x;
1678                                 y -= ftbase_y;
1679
1680                                 x += thisw * dw;
1681                                 ac += 16;
1682                                 at += 8;
1683                                 av += 12;
1684                                 batchcount++;
1685                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1686                                 {
1687                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1688                                         batchcount = 0;
1689                                         ac = color4f;
1690                                         at = texcoord2f;
1691                                         av = vertex3f;
1692                                 }
1693
1694                                 //prevmap = map;
1695                                 prevch = ch;
1696                         }
1697                         if (shadow)
1698                         {
1699                                 x -= 1.0/pix_x * r_textshadow.value;
1700                                 y -= 1.0/pix_y * r_textshadow.value;
1701                         }
1702                 }
1703         }
1704         if (batchcount > 0)
1705                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1706
1707         if (outcolor)
1708                 *outcolor = colorindex;
1709
1710         // note: this relies on the proper text (not shadow) being drawn last
1711         return x;
1712 }
1713
1714 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)
1715 {
1716         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1717 }
1718
1719 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)
1720 {
1721         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1722 }
1723
1724 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1725 {
1726         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1727 }
1728
1729 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1730 {
1731         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1732 }
1733
1734 #if 0
1735 // not used
1736 // no ^xrgb management
1737 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1738 {
1739         int color, numchars = 0;
1740         char *outputend2c = output2c + maxoutchars - 2;
1741         if (!outcolor || *outcolor == -1)
1742                 color = STRING_COLOR_DEFAULT;
1743         else
1744                 color = *outcolor;
1745         if (!maxreadchars)
1746                 maxreadchars = 1<<30;
1747         textend = text + maxreadchars;
1748         while (text != textend && *text)
1749         {
1750                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1751                 {
1752                         if (text[1] == STRING_COLOR_TAG)
1753                                 text++;
1754                         else if (text[1] >= '0' && text[1] <= '9')
1755                         {
1756                                 color = text[1] - '0';
1757                                 text += 2;
1758                                 continue;
1759                         }
1760                 }
1761                 if (output2c >= outputend2c)
1762                         break;
1763                 *output2c++ = *text++;
1764                 *output2c++ = color;
1765                 numchars++;
1766         }
1767         output2c[0] = output2c[1] = 0;
1768         if (outcolor)
1769                 *outcolor = color;
1770         return numchars;
1771 }
1772 #endif
1773
1774 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)
1775 {
1776         float floats[36];
1777
1778         _DrawQ_ProcessDrawFlag(flags);
1779         if(!r_draw2d.integer && !r_draw2d_force)
1780                 return;
1781
1782         R_Mesh_VertexPointer(floats, 0, 0);
1783         R_Mesh_ColorPointer(floats + 20, 0, 0);
1784         R_Mesh_ResetTextureState();
1785         if (pic)
1786         {
1787                 if (width == 0)
1788                         width = pic->width;
1789                 if (height == 0)
1790                         height = pic->height;
1791                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1792                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1793                 floats[12] = s1;floats[13] = t1;
1794                 floats[14] = s2;floats[15] = t2;
1795                 floats[16] = s4;floats[17] = t4;
1796                 floats[18] = s3;floats[19] = t3;
1797         }
1798         else
1799                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1800
1801         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1802         floats[0] = floats[9] = x;
1803         floats[1] = floats[4] = y;
1804         floats[3] = floats[6] = x + width;
1805         floats[7] = floats[10] = y + height;
1806         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1807         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1808         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1809         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1810
1811         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1812 }
1813
1814 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1815 {
1816         _DrawQ_ProcessDrawFlag(flags);
1817         if(!r_draw2d.integer && !r_draw2d_force)
1818                 return;
1819
1820         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1821         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1822         R_Mesh_ResetTextureState();
1823         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1824         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1825
1826         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1827 }
1828
1829 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1830 {
1831         int num;
1832
1833         _DrawQ_ProcessDrawFlag(flags);
1834         if(!r_draw2d.integer && !r_draw2d_force)
1835                 return;
1836
1837         GL_Color(1,1,1,1);
1838         CHECKGLERROR
1839         qglBegin(GL_LINE_LOOP);
1840         for (num = 0;num < mesh->num_vertices;num++)
1841         {
1842                 if (mesh->data_color4f)
1843                         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]);
1844                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1845         }
1846         qglEnd();
1847         CHECKGLERROR
1848 }
1849
1850 //[515]: this is old, delete
1851 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1852 {
1853         _DrawQ_ProcessDrawFlag(flags);
1854         if(!r_draw2d.integer && !r_draw2d_force)
1855                 return;
1856
1857         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1858
1859         CHECKGLERROR
1860         //qglLineWidth(width);CHECKGLERROR
1861
1862         GL_Color(r,g,b,alpha);
1863         CHECKGLERROR
1864         qglBegin(GL_LINES);
1865         qglVertex2f(x1, y1);
1866         qglVertex2f(x2, y2);
1867         qglEnd();
1868         CHECKGLERROR
1869 }
1870
1871 void DrawQ_SetClipArea(float x, float y, float width, float height)
1872 {
1873         int ix, iy, iw, ih;
1874         _DrawQ_Setup();
1875
1876         // We have to convert the con coords into real coords
1877         // OGL uses top to bottom
1878         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1879         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1880         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1881         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1882         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1883
1884         GL_ScissorTest(true);
1885 }
1886
1887 void DrawQ_ResetClipArea(void)
1888 {
1889         _DrawQ_Setup();
1890         GL_ScissorTest(false);
1891 }
1892
1893 void DrawQ_Finish(void)
1894 {
1895         r_refdef.draw2dstage = false;
1896 }
1897
1898 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1899 void R_DrawGamma(void)
1900 {
1901         float c[4];
1902         switch(vid.renderpath)
1903         {
1904         case RENDERPATH_GL20:
1905         case RENDERPATH_CGGL:
1906                 if (vid_usinghwgamma || v_glslgamma.integer)
1907                         return;
1908                 break;
1909         case RENDERPATH_GL13:
1910         case RENDERPATH_GL11:
1911                 if (vid_usinghwgamma)
1912                         return;
1913                 break;
1914         }
1915         // all the blends ignore depth
1916         R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1917         R_Mesh_ColorPointer(NULL, 0, 0);
1918         R_Mesh_ResetTextureState();
1919         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1920         GL_DepthMask(true);
1921         GL_DepthRange(0, 1);
1922         GL_PolygonOffset(0, 0);
1923         GL_DepthTest(false);
1924         if (v_color_enable.integer)
1925         {
1926                 c[0] = v_color_white_r.value;
1927                 c[1] = v_color_white_g.value;
1928                 c[2] = v_color_white_b.value;
1929         }
1930         else
1931                 c[0] = c[1] = c[2] = v_contrast.value;
1932         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1933         {
1934                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1935                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1936                 {
1937                         GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1938                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1939                         VectorScale(c, 0.5, c);
1940                 }
1941         }
1942         if (v_color_enable.integer)
1943         {
1944                 c[0] = v_color_black_r.value;
1945                 c[1] = v_color_black_g.value;
1946                 c[2] = v_color_black_b.value;
1947         }
1948         else
1949                 c[0] = c[1] = c[2] = v_brightness.value;
1950         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1951         {
1952                 GL_BlendFunc(GL_ONE, GL_ONE);
1953                 GL_Color(c[0], c[1], c[2], 1);
1954                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1955         }
1956 }
1957