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