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