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