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