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