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