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