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