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