]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
35f8267f8cc2d5ebd8ec02efcb217f4c300b208d
[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 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                                                         GL_LockArrays(0, batchcount * 4);
1498                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1499                                                         GL_LockArrays(0, 0);
1500                                                         batchcount = 0;
1501                                                         ac = color4f;
1502                                                         at = texcoord2f;
1503                                                         av = vertex3f;
1504                                                 }
1505                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1506                                                 map = ft2_oldstyle_map;
1507                                         }
1508                                 }
1509                                 prevch = 0;
1510                                 //num = (unsigned char) text[i];
1511                                 //thisw = fnt->width_of[num];
1512                                 thisw = fnt->width_of[ch];
1513                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1514                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1515                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1516                                 u = 0.0625f * thisw - (1.0f / tw);
1517                                 v = 0.0625f - (1.0f / th);
1518                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1519                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1520                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1521                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1522                                 at[ 0] = s              ; at[ 1] = t    ;
1523                                 at[ 2] = s+u    ; at[ 3] = t    ;
1524                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1525                                 at[ 6] = s              ; at[ 7] = t+v  ;
1526                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1527                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1528                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1529                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1530                                 ac += 16;
1531                                 at += 8;
1532                                 av += 12;
1533                                 batchcount++;
1534                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1535                                 {
1536                                         GL_LockArrays(0, batchcount * 4);
1537                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1538                                         GL_LockArrays(0, 0);
1539                                         batchcount = 0;
1540                                         ac = color4f;
1541                                         at = texcoord2f;
1542                                         av = vertex3f;
1543                                 }
1544                                 x += width_of[ch] * dw;
1545                         } else {
1546                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1547                                 {
1548                                         // new charmap - need to render
1549                                         if (batchcount)
1550                                         {
1551                                                 // we need a different character map, render what we currently have:
1552                                                 GL_LockArrays(0, batchcount * 4);
1553                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1554                                                 GL_LockArrays(0, 0);
1555                                                 batchcount = 0;
1556                                                 ac = color4f;
1557                                                 at = texcoord2f;
1558                                                 av = vertex3f;
1559                                         }
1560                                         // find the new map
1561                                         map = FontMap_FindForChar(fontmap, ch);
1562                                         if (!map)
1563                                         {
1564                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1565                                                 {
1566                                                         shadow = -1;
1567                                                         break;
1568                                                 }
1569                                                 if (!map)
1570                                                 {
1571                                                         // this shouldn't happen
1572                                                         shadow = -1;
1573                                                         break;
1574                                                 }
1575                                         }
1576                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1577                                 }
1578
1579                                 mapch = ch - map->start;
1580                                 thisw = map->glyphs[mapch].advance_x;
1581
1582                                 //x += ftbase_x;
1583                                 y += ftbase_y;
1584                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1585                                 {
1586                                         x += kx * dw;
1587                                         y += ky * dh;
1588                                 }
1589                                 else
1590                                         kx = ky = 0;
1591                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1592                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1593                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1594                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1595                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1596                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1597                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1598                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1599                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1600                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1601                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1602                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1603                                 //x -= ftbase_x;
1604                                 y -= ftbase_y;
1605
1606                                 x += thisw * dw;
1607                                 ac += 16;
1608                                 at += 8;
1609                                 av += 12;
1610                                 batchcount++;
1611                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1612                                 {
1613                                         GL_LockArrays(0, batchcount * 4);
1614                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1615                                         GL_LockArrays(0, 0);
1616                                         batchcount = 0;
1617                                         ac = color4f;
1618                                         at = texcoord2f;
1619                                         av = vertex3f;
1620                                 }
1621
1622                                 prevmap = map;
1623                                 prevch = ch;
1624                         }
1625                         if (shadow)
1626                         {
1627                                 x -= 1.0/pix_x * r_textshadow.value;
1628                                 y -= 1.0/pix_y * r_textshadow.value;
1629                         }
1630                 }
1631         }
1632         if (batchcount > 0)
1633         {
1634                 GL_LockArrays(0, batchcount * 4);
1635                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1636                 GL_LockArrays(0, 0);
1637         }
1638
1639         if (outcolor)
1640                 *outcolor = colorindex;
1641
1642         // note: this relies on the proper text (not shadow) being drawn last
1643         return x;
1644 }
1645
1646 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)
1647 {
1648         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1649 }
1650
1651 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)
1652 {
1653         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1654 }
1655
1656 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1657 {
1658         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1659 }
1660
1661 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1662 {
1663         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1664 }
1665
1666 #if 0
1667 // not used
1668 // no ^xrgb management
1669 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1670 {
1671         int color, numchars = 0;
1672         char *outputend2c = output2c + maxoutchars - 2;
1673         if (!outcolor || *outcolor == -1)
1674                 color = STRING_COLOR_DEFAULT;
1675         else
1676                 color = *outcolor;
1677         if (!maxreadchars)
1678                 maxreadchars = 1<<30;
1679         textend = text + maxreadchars;
1680         while (text != textend && *text)
1681         {
1682                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1683                 {
1684                         if (text[1] == STRING_COLOR_TAG)
1685                                 text++;
1686                         else if (text[1] >= '0' && text[1] <= '9')
1687                         {
1688                                 color = text[1] - '0';
1689                                 text += 2;
1690                                 continue;
1691                         }
1692                 }
1693                 if (output2c >= outputend2c)
1694                         break;
1695                 *output2c++ = *text++;
1696                 *output2c++ = color;
1697                 numchars++;
1698         }
1699         output2c[0] = output2c[1] = 0;
1700         if (outcolor)
1701                 *outcolor = color;
1702         return numchars;
1703 }
1704 #endif
1705
1706 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)
1707 {
1708         float floats[36];
1709
1710         _DrawQ_ProcessDrawFlag(flags);
1711
1712         R_Mesh_VertexPointer(floats, 0, 0);
1713         R_Mesh_ColorPointer(floats + 20, 0, 0);
1714         R_Mesh_ResetTextureState();
1715         if (pic)
1716         {
1717                 if (width == 0)
1718                         width = pic->width;
1719                 if (height == 0)
1720                         height = pic->height;
1721                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1722                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1723                 floats[12] = s1;floats[13] = t1;
1724                 floats[14] = s2;floats[15] = t2;
1725                 floats[16] = s4;floats[17] = t4;
1726                 floats[18] = s3;floats[19] = t3;
1727         }
1728         else
1729                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1730
1731         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1732         floats[0] = floats[9] = x;
1733         floats[1] = floats[4] = y;
1734         floats[3] = floats[6] = x + width;
1735         floats[7] = floats[10] = y + height;
1736         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1737         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1738         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1739         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1740
1741         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1742 }
1743
1744 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1745 {
1746         _DrawQ_ProcessDrawFlag(flags);
1747
1748         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1749         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1750         R_Mesh_ResetTextureState();
1751         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1752         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1753
1754         GL_LockArrays(0, mesh->num_vertices);
1755         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1756         GL_LockArrays(0, 0);
1757 }
1758
1759 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1760 {
1761         int num;
1762
1763         _DrawQ_ProcessDrawFlag(flags);
1764
1765         GL_Color(1,1,1,1);
1766         CHECKGLERROR
1767         qglBegin(GL_LINE_LOOP);
1768         for (num = 0;num < mesh->num_vertices;num++)
1769         {
1770                 if (mesh->data_color4f)
1771                         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]);
1772                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1773         }
1774         qglEnd();
1775         CHECKGLERROR
1776 }
1777
1778 //[515]: this is old, delete
1779 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1780 {
1781         _DrawQ_ProcessDrawFlag(flags);
1782
1783         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1784
1785         CHECKGLERROR
1786         //qglLineWidth(width);CHECKGLERROR
1787
1788         GL_Color(r,g,b,alpha);
1789         CHECKGLERROR
1790         qglBegin(GL_LINES);
1791         qglVertex2f(x1, y1);
1792         qglVertex2f(x2, y2);
1793         qglEnd();
1794         CHECKGLERROR
1795 }
1796
1797 void DrawQ_SetClipArea(float x, float y, float width, float height)
1798 {
1799         int ix, iy, iw, ih;
1800         _DrawQ_Setup();
1801
1802         // We have to convert the con coords into real coords
1803         // OGL uses top to bottom
1804         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1805         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1806         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1807         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1808         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1809
1810         GL_ScissorTest(true);
1811 }
1812
1813 void DrawQ_ResetClipArea(void)
1814 {
1815         _DrawQ_Setup();
1816         GL_ScissorTest(false);
1817 }
1818
1819 void DrawQ_Finish(void)
1820 {
1821         r_refdef.draw2dstage = false;
1822 }
1823
1824 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1825 void R_DrawGamma(void)
1826 {
1827         float c[4];
1828         switch(vid.renderpath)
1829         {
1830         case RENDERPATH_GL20:
1831         case RENDERPATH_CGGL:
1832                 if (vid_usinghwgamma || v_glslgamma.integer)
1833                         return;
1834                 break;
1835         case RENDERPATH_GL13:
1836         case RENDERPATH_GL11:
1837                 if (vid_usinghwgamma)
1838                         return;
1839                 break;
1840         }
1841         // all the blends ignore depth
1842         R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1843         R_Mesh_ColorPointer(NULL, 0, 0);
1844         R_Mesh_ResetTextureState();
1845         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1846         GL_DepthMask(true);
1847         GL_DepthRange(0, 1);
1848         GL_PolygonOffset(0, 0);
1849         GL_DepthTest(false);
1850         if (v_color_enable.integer)
1851         {
1852                 c[0] = v_color_white_r.value;
1853                 c[1] = v_color_white_g.value;
1854                 c[2] = v_color_white_b.value;
1855         }
1856         else
1857                 c[0] = c[1] = c[2] = v_contrast.value;
1858         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1859         {
1860                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1861                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1862                 {
1863                         GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1864                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1865                         VectorScale(c, 0.5, c);
1866                 }
1867         }
1868         if (v_color_enable.integer)
1869         {
1870                 c[0] = v_color_black_r.value;
1871                 c[1] = v_color_black_g.value;
1872                 c[2] = v_color_black_b.value;
1873         }
1874         else
1875                 c[0] = c[1] = c[2] = v_brightness.value;
1876         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1877         {
1878                 GL_BlendFunc(GL_ONE, GL_ONE);
1879                 GL_Color(c[0], c[1], c[2], 1);
1880                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1881         }
1882 }
1883