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