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