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