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