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