]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_draw.c
try using imagefont from fallbacks as well (.tga and .width)
[divverent/darkplaces.git] / gl_draw.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "wad.h"
24
25 #include "cl_video.h"
26 #include "cl_dyntexture.h"
27
28 #include "ft2.h"
29 #include "ft2_fontdefs.h"
30
31 dp_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_DPrintf("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_DPrintf("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                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
568                 {
569                         if (!fnt->fallbacks[i][0])
570                                 break;
571                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
572                         if(fnt->tex != r_texture_notexture)
573                                 break;
574                 }
575                 if(fnt->tex == r_texture_notexture)
576                 {
577                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
578                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
579                 }
580                 else
581                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
582         }
583         else
584                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
585
586         // unspecified width == 1 (base width)
587         for(i = 1; i < 256; ++i)
588                 fnt->width_of[i] = 1;
589         scale = 1;
590
591         // FIXME load "name.width", if it fails, fill all with 1
592         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
593         {
594                 float extraspacing = 0;
595                 const char *p = widthbuf;
596                 int ch = 0;
597
598                 while(ch < 256)
599                 {
600                         if(!COM_ParseToken_Simple(&p, false, false))
601                                 return;
602
603                         switch(*com_token)
604                         {
605                                 case '0':
606                                 case '1':
607                                 case '2':
608                                 case '3':
609                                 case '4':
610                                 case '5':
611                                 case '6':
612                                 case '7':
613                                 case '8':
614                                 case '9':
615                                 case '+':
616                                 case '-':
617                                 case '.':
618                                         fnt->width_of[ch++] = atof(com_token) + extraspacing;
619                                         break;
620                                 default:
621                                         if(!strcmp(com_token, "extraspacing"))
622                                         {
623                                                 if(!COM_ParseToken_Simple(&p, false, false))
624                                                         return;
625                                                 extraspacing = atof(com_token);
626                                         }
627                                         else if(!strcmp(com_token, "scale"))
628                                         {
629                                                 if(!COM_ParseToken_Simple(&p, false, false))
630                                                         return;
631                                                 scale = atof(com_token);
632                                         }
633                                         else
634                                         {
635                                                 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
636                                                 if(!COM_ParseToken_Simple(&p, false, false))
637                                                         return;
638                                         }
639                                         break;
640                         }
641                 }
642
643                 Mem_Free(widthbuf);
644         }
645
646         maxwidth = fnt->width_of[1];
647         for(i = 2; i < 256; ++i)
648                 maxwidth = max(maxwidth, fnt->width_of[i]);
649         fnt->maxwidth = maxwidth;
650
651         // fix up maxwidth for overlap
652         fnt->maxwidth *= scale;
653         fnt->scale = scale;
654
655         if(fnt == FONT_CONSOLE)
656                 con_linewidth = -1; // rewrap console in next frame
657 }
658
659 static dp_font_t *FindFont(const char *title)
660 {
661         int i;
662         for(i = 0; i < MAX_FONTS; ++i)
663                 if(!strcmp(dp_fonts[i].title, title))
664                         return &dp_fonts[i];
665         return NULL;
666 }
667
668 static float snap_to_pixel_x(float x, float roundUpAt)
669 {
670         float pixelpos = x * vid.width / vid_conwidth.value;
671         int snap = (int) pixelpos;
672         if (pixelpos - snap >= roundUpAt) ++snap;
673         return ((float)snap * vid_conwidth.value / vid.width);
674         /*
675         x = (int)(x * vid.width / vid_conwidth.value);
676         x = (x * vid_conwidth.value / vid.width);
677         return x;
678         */
679 }
680
681 static float snap_to_pixel_y(float y, float roundUpAt)
682 {
683         float pixelpos = y * vid.height / vid_conheight.value;
684         int snap = (int) pixelpos;
685         if (pixelpos - snap > roundUpAt) ++snap;
686         return ((float)snap * vid_conheight.value / vid.height);
687         /*
688         y = (int)(y * vid.height / vid_conheight.value);
689         y = (y * vid_conheight.value / vid.height);
690         return y;
691         */
692 }
693
694 static void LoadFont_f(void)
695 {
696         dp_font_t *f;
697         int i;
698         const char *filelist, *c, *cm;
699         float sz;
700         char mainfont[MAX_QPATH];
701
702         if(Cmd_Argc() < 2)
703         {
704                 Con_Printf("Available font commands:\n");
705                 for(i = 0; i < MAX_FONTS; ++i)
706                         Con_Printf("  loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title);
707                 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
708                            "can specify multiple fonts and faces\n"
709                            "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
710                            "to load face 2 of the font gfx/vera-sans and use face 1\n"
711                            "of gfx/fallback as fallback font.\n"
712                            "You can also specify a list of font sizes to load, like this:\n"
713                            "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
714                            "In many cases, 8 12 16 24 32 should be a good choice.\n"
715                         );
716                 return;
717         }
718         f = FindFont(Cmd_Argv(1));
719         if(f == NULL)
720         {
721                 Con_Printf("font function not found\n");
722                 return;
723         }
724
725         if(Cmd_Argc() < 3)
726                 filelist = "gfx/conchars";
727         else
728                 filelist = Cmd_Argv(2);
729
730         memset(f->fallbacks, 0, sizeof(f->fallbacks));
731         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
732
733         // first font is handled "normally"
734         c = strchr(filelist, ':');
735         cm = strchr(filelist, ',');
736         if(c && (!cm || c < cm))
737                 f->req_face = atoi(c+1);
738         else
739         {
740                 f->req_face = 0;
741                 c = cm;
742         }
743
744         if(!c || (c - filelist) > MAX_QPATH)
745                 strlcpy(mainfont, filelist, sizeof(mainfont));
746         else
747         {
748                 memcpy(mainfont, filelist, c - filelist);
749                 mainfont[c - filelist] = 0;
750         }
751
752         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
753         {
754                 c = strchr(filelist, ',');
755                 if(!c)
756                         break;
757                 filelist = c + 1;
758                 if(!*filelist)
759                         break;
760                 c = strchr(filelist, ':');
761                 cm = strchr(filelist, ',');
762                 if(c && (!cm || c < cm))
763                         f->fallback_faces[i] = atoi(c+1);
764                 else
765                 {
766                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
767                         c = cm;
768                 }
769                 if(!c || (c-filelist) > MAX_QPATH)
770                 {
771                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
772                 }
773                 else
774                 {
775                         memcpy(f->fallbacks[i], filelist, c - filelist);
776                         f->fallbacks[i][c - filelist] = 0;
777                 }
778         }
779
780         // for now: by default load only one size: the default size
781         f->req_sizes[0] = 0;
782         for(i = 1; i < MAX_FONT_SIZES; ++i)
783                 f->req_sizes[i] = -1;
784
785         // for some reason this argc is 3 even when using 2 arguments here, maybe nexuiz screws up
786         if(Cmd_Argc() >= 3)
787         {
788                 for(i = 0; i < Cmd_Argc()-3; ++i)
789                 {
790                         sz = atof(Cmd_Argv(i+3));
791                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
792                                 f->req_sizes[i] = sz;
793                 }
794         }
795         LoadFont(true, mainfont, f);
796 }
797
798 /*
799 ===============
800 Draw_Init
801 ===============
802 */
803 static void gl_draw_start(void)
804 {
805         int i;
806         drawtexturepool = R_AllocTexturePool();
807
808         numcachepics = 0;
809         memset(cachepichash, 0, sizeof(cachepichash));
810
811         font_start();
812
813         for(i = 0; i < MAX_FONTS; ++i)
814                 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
815
816         // draw the loading screen so people have something to see in the newly opened window
817         SCR_UpdateLoadingScreen(true);
818 }
819
820 static void gl_draw_shutdown(void)
821 {
822         font_shutdown();
823
824         R_FreeTexturePool(&drawtexturepool);
825
826         numcachepics = 0;
827         memset(cachepichash, 0, sizeof(cachepichash));
828 }
829
830 static void gl_draw_newmap(void)
831 {
832         font_newmap();
833 }
834
835 void GL_Draw_Init (void)
836 {
837         int i, j;
838         Cvar_RegisterVariable(&r_textshadow);
839         Cvar_RegisterVariable(&r_textbrightness);
840         Cvar_RegisterVariable(&r_textcontrast);
841         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
842         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
843
844         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
845                 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
846         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
847         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
848         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
849         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
850         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
851         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
852         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
853         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
854                 if(!FONT_USER[i].title[0])
855                         dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
856         Font_Init();
857 }
858
859 void _DrawQ_Setup(void)
860 {
861         r_viewport_t viewport;
862         if (r_refdef.draw2dstage)
863                 return;
864         r_refdef.draw2dstage = true;
865         CHECKGLERROR
866         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);
867         R_SetViewport(&viewport);
868         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
869         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
870         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
871         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
872         R_EntityMatrix(&identitymatrix);
873
874         GL_DepthMask(true);
875         GL_DepthRange(0, 1);
876         GL_PolygonOffset(0, 0);
877         GL_DepthTest(false);
878         GL_Color(1,1,1,1);
879         GL_AlphaTest(false);
880         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
881 }
882
883 static void _DrawQ_ProcessDrawFlag(int flags)
884 {
885         _DrawQ_Setup();
886         CHECKGLERROR
887         if(flags == DRAWFLAG_ADDITIVE)
888                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
889         else if(flags == DRAWFLAG_MODULATE)
890                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
891         else if(flags == DRAWFLAG_2XMODULATE)
892                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
893         else if(flags == DRAWFLAG_SCREEN)
894                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
895         else
896                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
897 }
898
899 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
900 {
901         float floats[20];
902
903         _DrawQ_ProcessDrawFlag(flags);
904         GL_Color(red, green, blue, alpha);
905
906         R_Mesh_VertexPointer(floats, 0, 0);
907         R_Mesh_ColorPointer(NULL, 0, 0);
908         R_Mesh_ResetTextureState();
909         if (pic)
910         {
911                 if (width == 0)
912                         width = pic->width;
913                 if (height == 0)
914                         height = pic->height;
915                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
916                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
917
918 #if 1
919                 floats[12] = 0.0f;floats[13] = 0.0f;
920                 floats[14] = 1.0f;floats[15] = 0.0f;
921                 floats[16] = 1.0f;floats[17] = 1.0f;
922                 floats[18] = 0.0f;floats[19] = 1.0f;
923 #else
924       // AK07: lets be texel correct on the corners
925       {
926          float horz_offset = 0.5f / pic->width;
927          float vert_offset = 0.5f / pic->height;
928
929                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
930                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
931                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
932                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
933       }
934 #endif
935         }
936         else
937                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
938
939         floats[2] = floats[5] = floats[8] = floats[11] = 0;
940         floats[0] = floats[9] = x;
941         floats[1] = floats[4] = y;
942         floats[3] = floats[6] = x + width;
943         floats[7] = floats[10] = y + height;
944
945         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
946 }
947
948 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)
949 {
950         float floats[20];
951         float af = DEG2RAD(-angle); // forward
952         float ar = DEG2RAD(-angle + 90); // right
953         float sinaf = sin(af);
954         float cosaf = cos(af);
955         float sinar = sin(ar);
956         float cosar = cos(ar);
957
958         _DrawQ_ProcessDrawFlag(flags);
959         GL_Color(red, green, blue, alpha);
960
961         R_Mesh_VertexPointer(floats, 0, 0);
962         R_Mesh_ColorPointer(NULL, 0, 0);
963         R_Mesh_ResetTextureState();
964         if (pic)
965         {
966                 if (width == 0)
967                         width = pic->width;
968                 if (height == 0)
969                         height = pic->height;
970                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
971                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
972
973                 floats[12] = 0.0f;floats[13] = 0.0f;
974                 floats[14] = 1.0f;floats[15] = 0.0f;
975                 floats[16] = 1.0f;floats[17] = 1.0f;
976                 floats[18] = 0.0f;floats[19] = 1.0f;
977         }
978         else
979                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
980
981         floats[2] = floats[5] = floats[8] = floats[11] = 0;
982
983 // top left
984         floats[0] = x - cosaf*org_x - cosar*org_y;
985         floats[1] = y - sinaf*org_x - sinar*org_y;
986
987 // top right
988         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
989         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
990
991 // bottom right
992         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
993         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
994
995 // bottom left
996         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
997         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
998
999         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1000 }
1001
1002 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1003 {
1004         float floats[12];
1005
1006         _DrawQ_ProcessDrawFlag(flags);
1007         GL_Color(red, green, blue, alpha);
1008
1009         R_Mesh_VertexPointer(floats, 0, 0);
1010         R_Mesh_ColorPointer(NULL, 0, 0);
1011         R_Mesh_ResetTextureState();
1012         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1013
1014         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1015         floats[0] = floats[9] = x;
1016         floats[1] = floats[4] = y;
1017         floats[3] = floats[6] = x + width;
1018         floats[7] = floats[10] = y + height;
1019
1020         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1021 }
1022
1023 /// color tag printing
1024 static const vec4_t string_colors[] =
1025 {
1026         // Quake3 colors
1027         // LordHavoc: why on earth is cyan before magenta in Quake3?
1028         // LordHavoc: note: Doom3 uses white for [0] and [7]
1029         {0.0, 0.0, 0.0, 1.0}, // black
1030         {1.0, 0.0, 0.0, 1.0}, // red
1031         {0.0, 1.0, 0.0, 1.0}, // green
1032         {1.0, 1.0, 0.0, 1.0}, // yellow
1033         {0.0, 0.0, 1.0, 1.0}, // blue
1034         {0.0, 1.0, 1.0, 1.0}, // cyan
1035         {1.0, 0.0, 1.0, 1.0}, // magenta
1036         {1.0, 1.0, 1.0, 1.0}, // white
1037         // [515]'s BX_COLOREDTEXT extension
1038         {1.0, 1.0, 1.0, 0.5}, // half transparent
1039         {0.5, 0.5, 0.5, 1.0}  // half brightness
1040         // Black's color table
1041         //{1.0, 1.0, 1.0, 1.0},
1042         //{1.0, 0.0, 0.0, 1.0},
1043         //{0.0, 1.0, 0.0, 1.0},
1044         //{0.0, 0.0, 1.0, 1.0},
1045         //{1.0, 1.0, 0.0, 1.0},
1046         //{0.0, 1.0, 1.0, 1.0},
1047         //{1.0, 0.0, 1.0, 1.0},
1048         //{0.1, 0.1, 0.1, 1.0}
1049 };
1050
1051 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1052
1053 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1054 {
1055         float C = r_textcontrast.value;
1056         float B = r_textbrightness.value;
1057         if (colorindex & 0x10000) // that bit means RGB color
1058         {
1059                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1060                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1061                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1062                 color[3] = (colorindex & 0xf) / 15.0;
1063         }
1064         else
1065                 Vector4Copy(string_colors[colorindex], color);
1066         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1067         if (shadow)
1068         {
1069                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1070                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1071         }
1072 }
1073
1074 // NOTE: this function always draws exactly one character if maxwidth <= 0
1075 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)
1076 {
1077         const char *text_start = text;
1078         int colorindex = STRING_COLOR_DEFAULT;
1079         size_t i;
1080         float x = 0;
1081         Uchar ch, mapch, nextch;
1082         Uchar prevch = 0; // used for kerning
1083         int tempcolorindex;
1084         float kx;
1085         int map_index = 0;
1086         size_t bytes_left;
1087         ft2_font_map_t *fontmap = NULL;
1088         ft2_font_map_t *map = NULL;
1089         ft2_font_map_t *prevmap = NULL;
1090         ft2_font_t *ft2 = fnt->ft2;
1091         // float ftbase_x;
1092         qboolean snap = true;
1093         qboolean least_one = false;
1094         float dw, dh; // display w/h
1095
1096         if (!h) h = w;
1097         if (!h) {
1098                 w = h = 1;
1099                 snap = false;
1100         }
1101         // do this in the end
1102         w *= fnt->scale;
1103         h *= fnt->scale;
1104
1105         // find the most fitting size:
1106         if (ft2 != NULL)
1107         {
1108                 if (snap)
1109                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1110                 else
1111                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1112                 fontmap = Font_MapForIndex(ft2, map_index);
1113         }
1114         if(snap)
1115         {
1116                 if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
1117                         snap = false; // turn off pixel snapping for better animation
1118                 else
1119                         sw = sh = 1;
1120         }
1121
1122         dw = w * sw;
1123         dh = h * sh;
1124
1125         if (*maxlen < 1)
1126                 *maxlen = 1<<30;
1127
1128         if (!outcolor || *outcolor == -1)
1129                 colorindex = STRING_COLOR_DEFAULT;
1130         else
1131                 colorindex = *outcolor;
1132
1133         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1134         // ftbase_x = snap_to_pixel_x(0);
1135         
1136         if(maxwidth <= 0)
1137         {
1138                 least_one = true;
1139                 maxwidth = -maxwidth;
1140         }
1141
1142         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1143         {
1144                 size_t i0 = i;
1145                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1146                 i = text - text_start;
1147                 if (!ch)
1148                         break;
1149                 if (snap)
1150                         x = snap_to_pixel_x(x, 0.4);
1151                 if (ch == ' ' && !fontmap)
1152                 {
1153                         if(!least_one || i0) // never skip the first character
1154                         if(x + fnt->width_of[(int) ' '] * dw > maxwidth)
1155                         {
1156                                 i = i0;
1157                                 break; // oops, can't draw this
1158                         }
1159                         x += fnt->width_of[(int) ' '] * dw;
1160                         continue;
1161                 }
1162                 // i points to the char after ^
1163                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1164                 {
1165                         ch = *text; // colors are ascii, so no u8_ needed
1166                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1167                         {
1168                                 colorindex = ch - '0';
1169                                 ++text;
1170                                 ++i;
1171                                 continue;
1172                         }
1173                         // i points to the char after ^...
1174                         // i+3 points to 3 in ^x123
1175                         // i+3 == *maxlen would mean that char is missing
1176                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1177                         {
1178                                 // building colorindex...
1179                                 ch = tolower(text[1]);
1180                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1181                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1182                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1183                                 else tempcolorindex = 0;
1184                                 if (tempcolorindex)
1185                                 {
1186                                         ch = tolower(text[2]);
1187                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1188                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1189                                         else tempcolorindex = 0;
1190                                         if (tempcolorindex)
1191                                         {
1192                                                 ch = tolower(text[3]);
1193                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1194                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1195                                                 else tempcolorindex = 0;
1196                                                 if (tempcolorindex)
1197                                                 {
1198                                                         colorindex = tempcolorindex | 0xf;
1199                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1200                                                         i+=4;
1201                                                         text += 4;
1202                                                         continue;
1203                                                 }
1204                                         }
1205                                 }
1206                         }
1207                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1208                         {
1209                                 i++;
1210                                 text++;
1211                         }
1212                         i--;
1213                 }
1214                 ch = nextch;
1215
1216                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1217                 {
1218                         if (ch > 0xE000)
1219                                 ch -= 0xE000;
1220                         if (ch > 0xFF)
1221                                 continue;
1222                         if (fontmap)
1223                                 map = ft2_oldstyle_map;
1224                         prevch = 0;
1225                         if(!least_one || i0) // never skip the first character
1226                         if(x + fnt->width_of[ch] * dw > maxwidth)
1227                         {
1228                                 i = i0;
1229                                 break; // oops, can't draw this
1230                         }
1231                         x += fnt->width_of[ch] * dw;
1232                 } else {
1233                         if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
1234                         {
1235                                 map = FontMap_FindForChar(fontmap, ch);
1236                                 if (!map)
1237                                 {
1238                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1239                                                 break;
1240                                         if (!map)
1241                                                 break;
1242                                 }
1243                         }
1244                         mapch = ch - map->start;
1245                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1246                                 x += kx * dw;
1247                         x += map->glyphs[mapch].advance_x * dw;
1248                         prevmap = map;
1249                         prevch = ch;
1250                 }
1251         }
1252
1253         *maxlen = i;
1254
1255         if (outcolor)
1256                 *outcolor = colorindex;
1257
1258         return x;
1259 }
1260
1261 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)
1262 {
1263         int shadow, colorindex = STRING_COLOR_DEFAULT;
1264         size_t i;
1265         float x = startx, y, s, t, u, v, thisw;
1266         float *av, *at, *ac;
1267         float color[4];
1268         int batchcount;
1269         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1270         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1271         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1272         Uchar ch, mapch, nextch;
1273         Uchar prevch = 0; // used for kerning
1274         int tempcolorindex;
1275         int map_index = 0;
1276         ft2_font_map_t *prevmap = NULL; // the previous map
1277         ft2_font_map_t *map = NULL;     // the currently used map
1278         ft2_font_map_t *fontmap = NULL; // the font map for the size
1279         float ftbase_y;
1280         const char *text_start = text;
1281         float kx, ky;
1282         ft2_font_t *ft2 = fnt->ft2;
1283         qboolean snap = true;
1284         float pix_x, pix_y;
1285         size_t bytes_left;
1286         float dw, dh;
1287
1288         int tw, th;
1289         tw = R_TextureWidth(fnt->tex);
1290         th = R_TextureHeight(fnt->tex);
1291
1292         if (!h) h = w;
1293         if (!h) {
1294                 h = w = 1;
1295                 snap = false;
1296         }
1297
1298         starty -= (fnt->scale - 1) * h * 0.5; // center
1299         w *= fnt->scale;
1300         h *= fnt->scale;
1301
1302         if (ft2 != NULL)
1303         {
1304                 if (snap)
1305                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1306                 else
1307                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1308                 fontmap = Font_MapForIndex(ft2, map_index);
1309         }
1310         if(snap)
1311         {
1312                 if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
1313                         snap = false; // turn off pixel snapping for better animation
1314                 else
1315                         sw = sh = 1;
1316         }
1317
1318         dw = w * sw;
1319         dh = h * sh;
1320
1321         // draw the font at its baseline when using freetype
1322         //ftbase_x = 0;
1323         ftbase_y = dh * (4.5/6.0);
1324
1325         if (maxlen < 1)
1326                 maxlen = 1<<30;
1327
1328         _DrawQ_ProcessDrawFlag(flags);
1329
1330         R_Mesh_ColorPointer(color4f, 0, 0);
1331         R_Mesh_ResetTextureState();
1332         if (!fontmap)
1333                 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1334         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1335         R_Mesh_VertexPointer(vertex3f, 0, 0);
1336         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1337
1338         ac = color4f;
1339         at = texcoord2f;
1340         av = vertex3f;
1341         batchcount = 0;
1342
1343         //ftbase_x = snap_to_pixel_x(ftbase_x);
1344         if(snap)
1345                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1346
1347         pix_x = vid.width / vid_conwidth.value;
1348         pix_y = vid.height / vid_conheight.value;
1349         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1350         {
1351                 text = text_start;
1352
1353                 if (!outcolor || *outcolor == -1)
1354                         colorindex = STRING_COLOR_DEFAULT;
1355                 else
1356                         colorindex = *outcolor;
1357
1358                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1359
1360                 x = startx;
1361                 y = starty;
1362                 /*
1363                 if (shadow)
1364                 {
1365                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1366                         y += r_textshadow.value * vid.height / vid_conheight.value;
1367                 }
1368                 */
1369                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1370                 {
1371                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1372                         i = text - text_start;
1373                         if (!ch)
1374                                 break;
1375                         if (snap)
1376                         {
1377                                 x = snap_to_pixel_x(x, 0.4);
1378                                 y = snap_to_pixel_y(y, 0.4);
1379                         }
1380                         if (ch == ' ' && !fontmap)
1381                         {
1382                                 x += fnt->width_of[(int) ' '] * dw;
1383                                 continue;
1384                         }
1385                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1386                         {
1387                                 ch = *text; // colors are ascii, so no u8_ needed
1388                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1389                                 {
1390                                         colorindex = ch - '0';
1391                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1392                                         ++text;
1393                                         ++i;
1394                                         continue;
1395                                 }
1396                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1397                                 {
1398                                         // building colorindex...
1399                                         ch = tolower(text[1]);
1400                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1401                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1402                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1403                                         else tempcolorindex = 0;
1404                                         if (tempcolorindex)
1405                                         {
1406                                                 ch = tolower(text[2]);
1407                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1408                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1409                                                 else tempcolorindex = 0;
1410                                                 if (tempcolorindex)
1411                                                 {
1412                                                         ch = tolower(text[3]);
1413                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1414                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1415                                                         else tempcolorindex = 0;
1416                                                         if (tempcolorindex)
1417                                                         {
1418                                                                 colorindex = tempcolorindex | 0xf;
1419                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1420                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1421                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1422                                                                 i+=4;
1423                                                                 text+=4;
1424                                                                 continue;
1425                                                         }
1426                                                 }
1427                                         }
1428                                 }
1429                                 else if (ch == STRING_COLOR_TAG)
1430                                 {
1431                                         i++;
1432                                         text++;
1433                                 }
1434                                 i--;
1435                         }
1436                         // get the backup
1437                         ch = nextch;
1438                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1439                         // this way we don't need to rebind fnt->tex for every old-style character
1440                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1441                         if (shadow)
1442                         {
1443                                 x += 1.0/pix_x * r_textshadow.value;
1444                                 y += 1.0/pix_y * r_textshadow.value;
1445                         }
1446                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1447                         {
1448                                 if (ch > 0xE000)
1449                                         ch -= 0xE000;
1450                                 if (ch > 0xFF)
1451                                         continue;
1452                                 if (fontmap)
1453                                 {
1454                                         if (map != ft2_oldstyle_map)
1455                                         {
1456                                                 if (batchcount)
1457                                                 {
1458                                                         // switching from freetype to non-freetype rendering
1459                                                         GL_LockArrays(0, batchcount * 4);
1460                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1461                                                         GL_LockArrays(0, 0);
1462                                                         batchcount = 0;
1463                                                         ac = color4f;
1464                                                         at = texcoord2f;
1465                                                         av = vertex3f;
1466                                                 }
1467                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1468                                                 map = ft2_oldstyle_map;
1469                                         }
1470                                 }
1471                                 prevch = 0;
1472                                 //num = (unsigned char) text[i];
1473                                 //thisw = fnt->width_of[num];
1474                                 thisw = fnt->width_of[ch];
1475                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1476                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1477                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1478                                 u = 0.0625f * thisw - (1.0f / tw);
1479                                 v = 0.0625f - (1.0f / th);
1480                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1481                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1482                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1483                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1484                                 at[ 0] = s              ; at[ 1] = t    ;
1485                                 at[ 2] = s+u    ; at[ 3] = t    ;
1486                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1487                                 at[ 6] = s              ; at[ 7] = t+v  ;
1488                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1489                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1490                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1491                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1492                                 ac += 16;
1493                                 at += 8;
1494                                 av += 12;
1495                                 batchcount++;
1496                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1497                                 {
1498                                         GL_LockArrays(0, batchcount * 4);
1499                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1500                                         GL_LockArrays(0, 0);
1501                                         batchcount = 0;
1502                                         ac = color4f;
1503                                         at = texcoord2f;
1504                                         av = vertex3f;
1505                                 }
1506                                 x += thisw * dw;
1507                         } else {
1508                                 if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
1509                                 {
1510                                         // new charmap - need to render
1511                                         if (batchcount)
1512                                         {
1513                                                 // we need a different character map, render what we currently have:
1514                                                 GL_LockArrays(0, batchcount * 4);
1515                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1516                                                 GL_LockArrays(0, 0);
1517                                                 batchcount = 0;
1518                                                 ac = color4f;
1519                                                 at = texcoord2f;
1520                                                 av = vertex3f;
1521                                         }
1522                                         // find the new map
1523                                         map = FontMap_FindForChar(fontmap, ch);
1524                                         if (!map)
1525                                         {
1526                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1527                                                 {
1528                                                         shadow = -1;
1529                                                         break;
1530                                                 }
1531                                                 if (!map)
1532                                                 {
1533                                                         // this shouldn't happen
1534                                                         shadow = -1;
1535                                                         break;
1536                                                 }
1537                                         }
1538                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1539                                 }
1540
1541                                 mapch = ch - map->start;
1542                                 thisw = map->glyphs[mapch].advance_x;
1543
1544                                 //x += ftbase_x;
1545                                 y += ftbase_y;
1546                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1547                                 {
1548                                         x += kx * dw;
1549                                         y += ky * dh;
1550                                 }
1551                                 else
1552                                         kx = ky = 0;
1553                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1554                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1555                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1556                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1557                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1558                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1559                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1560                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1561                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1562                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1563                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1564                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1565                                 //x -= ftbase_x;
1566                                 y -= ftbase_y;
1567
1568                                 x += thisw * dw;
1569                                 ac += 16;
1570                                 at += 8;
1571                                 av += 12;
1572                                 batchcount++;
1573                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1574                                 {
1575                                         GL_LockArrays(0, batchcount * 4);
1576                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1577                                         GL_LockArrays(0, 0);
1578                                         batchcount = 0;
1579                                         ac = color4f;
1580                                         at = texcoord2f;
1581                                         av = vertex3f;
1582                                 }
1583
1584                                 prevmap = map;
1585                                 prevch = ch;
1586                         }
1587                         if (shadow)
1588                         {
1589                                 x -= 1.0/pix_x * r_textshadow.value;
1590                                 y -= 1.0/pix_y * r_textshadow.value;
1591                         }
1592                 }
1593         }
1594         if (batchcount > 0)
1595         {
1596                 GL_LockArrays(0, batchcount * 4);
1597                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1598                 GL_LockArrays(0, 0);
1599         }
1600
1601         if (outcolor)
1602                 *outcolor = colorindex;
1603
1604         // note: this relies on the proper text (not shadow) being drawn last
1605         return x;
1606 }
1607
1608 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)
1609 {
1610         return DrawQ_String_Font_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1611 }
1612
1613 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)
1614 {
1615         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1616 }
1617
1618 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)
1619 {
1620         return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size_Scale(text, w, h, 1, 1, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth);
1621 }
1622
1623 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1624 {
1625         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1626 }
1627
1628 float DrawQ_TextWidth_Font_Size(const char *text, float w, float h, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1629 {
1630         return DrawQ_TextWidth_Font_UntilWidth_Size(text, w, h, &maxlen, ignorecolorcodes, fnt, 1000000000);
1631 }
1632
1633 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1634 {
1635         return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1636 }
1637
1638 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)
1639 {
1640         return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, w, h, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1641 }
1642
1643 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1644 {
1645         return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, 0, 0, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth);
1646 }
1647
1648 #if 0
1649 // not used
1650 // no ^xrgb management
1651 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1652 {
1653         int color, numchars = 0;
1654         char *outputend2c = output2c + maxoutchars - 2;
1655         if (!outcolor || *outcolor == -1)
1656                 color = STRING_COLOR_DEFAULT;
1657         else
1658                 color = *outcolor;
1659         if (!maxreadchars)
1660                 maxreadchars = 1<<30;
1661         textend = text + maxreadchars;
1662         while (text != textend && *text)
1663         {
1664                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1665                 {
1666                         if (text[1] == STRING_COLOR_TAG)
1667                                 text++;
1668                         else if (text[1] >= '0' && text[1] <= '9')
1669                         {
1670                                 color = text[1] - '0';
1671                                 text += 2;
1672                                 continue;
1673                         }
1674                 }
1675                 if (output2c >= outputend2c)
1676                         break;
1677                 *output2c++ = *text++;
1678                 *output2c++ = color;
1679                 numchars++;
1680         }
1681         output2c[0] = output2c[1] = 0;
1682         if (outcolor)
1683                 *outcolor = color;
1684         return numchars;
1685 }
1686 #endif
1687
1688 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)
1689 {
1690         float floats[36];
1691
1692         _DrawQ_ProcessDrawFlag(flags);
1693
1694         R_Mesh_VertexPointer(floats, 0, 0);
1695         R_Mesh_ColorPointer(floats + 20, 0, 0);
1696         R_Mesh_ResetTextureState();
1697         if (pic)
1698         {
1699                 if (width == 0)
1700                         width = pic->width;
1701                 if (height == 0)
1702                         height = pic->height;
1703                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1704                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1705                 floats[12] = s1;floats[13] = t1;
1706                 floats[14] = s2;floats[15] = t2;
1707                 floats[16] = s4;floats[17] = t4;
1708                 floats[18] = s3;floats[19] = t3;
1709         }
1710         else
1711                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1712
1713         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1714         floats[0] = floats[9] = x;
1715         floats[1] = floats[4] = y;
1716         floats[3] = floats[6] = x + width;
1717         floats[7] = floats[10] = y + height;
1718         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1719         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1720         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1721         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1722
1723         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1724 }
1725
1726 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1727 {
1728         _DrawQ_ProcessDrawFlag(flags);
1729
1730         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1731         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1732         R_Mesh_ResetTextureState();
1733         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1734         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1735
1736         GL_LockArrays(0, mesh->num_vertices);
1737         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1738         GL_LockArrays(0, 0);
1739 }
1740
1741 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1742 {
1743         int num;
1744
1745         _DrawQ_ProcessDrawFlag(flags);
1746
1747         GL_Color(1,1,1,1);
1748         CHECKGLERROR
1749         qglBegin(GL_LINE_LOOP);
1750         for (num = 0;num < mesh->num_vertices;num++)
1751         {
1752                 if (mesh->data_color4f)
1753                         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]);
1754                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1755         }
1756         qglEnd();
1757         CHECKGLERROR
1758 }
1759
1760 //[515]: this is old, delete
1761 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1762 {
1763         _DrawQ_ProcessDrawFlag(flags);
1764
1765         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1766
1767         CHECKGLERROR
1768         //qglLineWidth(width);CHECKGLERROR
1769
1770         GL_Color(r,g,b,alpha);
1771         CHECKGLERROR
1772         qglBegin(GL_LINES);
1773         qglVertex2f(x1, y1);
1774         qglVertex2f(x2, y2);
1775         qglEnd();
1776         CHECKGLERROR
1777 }
1778
1779 void DrawQ_SetClipArea(float x, float y, float width, float height)
1780 {
1781         int ix, iy, iw, ih;
1782         _DrawQ_Setup();
1783
1784         // We have to convert the con coords into real coords
1785         // OGL uses top to bottom
1786         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1787         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1788         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1789         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1790         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1791
1792         GL_ScissorTest(true);
1793 }
1794
1795 void DrawQ_ResetClipArea(void)
1796 {
1797         _DrawQ_Setup();
1798         GL_ScissorTest(false);
1799 }
1800
1801 void DrawQ_Finish(void)
1802 {
1803         r_refdef.draw2dstage = false;
1804 }
1805
1806 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1807 void R_DrawGamma(void)
1808 {
1809         float c[4];
1810         switch(vid.renderpath)
1811         {
1812         case RENDERPATH_GL20:
1813         case RENDERPATH_CGGL:
1814                 if (vid_usinghwgamma || v_glslgamma.integer)
1815                         return;
1816                 break;
1817         case RENDERPATH_GL13:
1818         case RENDERPATH_GL11:
1819                 if (vid_usinghwgamma)
1820                         return;
1821                 break;
1822         }
1823         // all the blends ignore depth
1824         R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1825         R_Mesh_ColorPointer(NULL, 0, 0);
1826         R_Mesh_ResetTextureState();
1827         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1828         GL_DepthMask(true);
1829         GL_DepthRange(0, 1);
1830         GL_PolygonOffset(0, 0);
1831         GL_DepthTest(false);
1832         if (v_color_enable.integer)
1833         {
1834                 c[0] = v_color_white_r.value;
1835                 c[1] = v_color_white_g.value;
1836                 c[2] = v_color_white_b.value;
1837         }
1838         else
1839                 c[0] = c[1] = c[2] = v_contrast.value;
1840         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1841         {
1842                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1843                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1844                 {
1845                         GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1846                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1847                         VectorScale(c, 0.5, c);
1848                 }
1849         }
1850         if (v_color_enable.integer)
1851         {
1852                 c[0] = v_color_black_r.value;
1853                 c[1] = v_color_black_g.value;
1854                 c[2] = v_color_black_b.value;
1855         }
1856         else
1857                 c[0] = c[1] = c[2] = v_brightness.value;
1858         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1859         {
1860                 GL_BlendFunc(GL_ONE, GL_ONE);
1861                 GL_Color(c[0], c[1], c[2], 1);
1862                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1863         }
1864 }
1865