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