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