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