]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
fix font loading bug causing E0xx glyphs to get zero size
[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, NULL);
363         if (pixels == NULL && !strncmp(path, "gfx/", 4))
364                 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, 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);
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);
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 = 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 = 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         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
974         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
975         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
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[20];
1009
1010         _DrawQ_ProcessDrawFlag(flags);
1011         if(!r_draw2d.integer && !r_draw2d_force)
1012                 return;
1013
1014         GL_Color(red, green, blue, alpha);
1015
1016         R_Mesh_VertexPointer(floats, 0, 0);
1017         R_Mesh_ColorPointer(NULL, 0, 0);
1018         R_Mesh_ResetTextureState();
1019         if (pic)
1020         {
1021                 if (width == 0)
1022                         width = pic->width;
1023                 if (height == 0)
1024                         height = pic->height;
1025                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1026                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1027
1028 #if 1
1029                 floats[12] = 0.0f;floats[13] = 0.0f;
1030                 floats[14] = 1.0f;floats[15] = 0.0f;
1031                 floats[16] = 1.0f;floats[17] = 1.0f;
1032                 floats[18] = 0.0f;floats[19] = 1.0f;
1033 #else
1034       // AK07: lets be texel correct on the corners
1035       {
1036          float horz_offset = 0.5f / pic->width;
1037          float vert_offset = 0.5f / pic->height;
1038
1039                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1040                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1041                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1042                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1043       }
1044 #endif
1045         }
1046         else
1047                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1048
1049         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1050         floats[0] = floats[9] = x;
1051         floats[1] = floats[4] = y;
1052         floats[3] = floats[6] = x + width;
1053         floats[7] = floats[10] = y + height;
1054
1055         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1056 }
1057
1058 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)
1059 {
1060         float floats[20];
1061         float af = DEG2RAD(-angle); // forward
1062         float ar = DEG2RAD(-angle + 90); // right
1063         float sinaf = sin(af);
1064         float cosaf = cos(af);
1065         float sinar = sin(ar);
1066         float cosar = cos(ar);
1067
1068         _DrawQ_ProcessDrawFlag(flags);
1069         if(!r_draw2d.integer && !r_draw2d_force)
1070                 return;
1071
1072         GL_Color(red, green, blue, alpha);
1073
1074         R_Mesh_VertexPointer(floats, 0, 0);
1075         R_Mesh_ColorPointer(NULL, 0, 0);
1076         R_Mesh_ResetTextureState();
1077         if (pic)
1078         {
1079                 if (width == 0)
1080                         width = pic->width;
1081                 if (height == 0)
1082                         height = pic->height;
1083                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1084                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1085
1086                 floats[12] = 0.0f;floats[13] = 0.0f;
1087                 floats[14] = 1.0f;floats[15] = 0.0f;
1088                 floats[16] = 1.0f;floats[17] = 1.0f;
1089                 floats[18] = 0.0f;floats[19] = 1.0f;
1090         }
1091         else
1092                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1093
1094         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1095
1096 // top left
1097         floats[0] = x - cosaf*org_x - cosar*org_y;
1098         floats[1] = y - sinaf*org_x - sinar*org_y;
1099
1100 // top right
1101         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1102         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1103
1104 // bottom right
1105         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1106         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1107
1108 // bottom left
1109         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1110         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1111
1112         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1113 }
1114
1115 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1116 {
1117         float floats[12];
1118
1119         _DrawQ_ProcessDrawFlag(flags);
1120         if(!r_draw2d.integer && !r_draw2d_force)
1121                 return;
1122
1123         GL_Color(red, green, blue, alpha);
1124
1125         R_Mesh_VertexPointer(floats, 0, 0);
1126         R_Mesh_ColorPointer(NULL, 0, 0);
1127         R_Mesh_ResetTextureState();
1128         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1129
1130         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1131         floats[0] = floats[9] = x;
1132         floats[1] = floats[4] = y;
1133         floats[3] = floats[6] = x + width;
1134         floats[7] = floats[10] = y + height;
1135
1136         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1137 }
1138
1139 /// color tag printing
1140 static const vec4_t string_colors[] =
1141 {
1142         // Quake3 colors
1143         // LordHavoc: why on earth is cyan before magenta in Quake3?
1144         // LordHavoc: note: Doom3 uses white for [0] and [7]
1145         {0.0, 0.0, 0.0, 1.0}, // black
1146         {1.0, 0.0, 0.0, 1.0}, // red
1147         {0.0, 1.0, 0.0, 1.0}, // green
1148         {1.0, 1.0, 0.0, 1.0}, // yellow
1149         {0.0, 0.0, 1.0, 1.0}, // blue
1150         {0.0, 1.0, 1.0, 1.0}, // cyan
1151         {1.0, 0.0, 1.0, 1.0}, // magenta
1152         {1.0, 1.0, 1.0, 1.0}, // white
1153         // [515]'s BX_COLOREDTEXT extension
1154         {1.0, 1.0, 1.0, 0.5}, // half transparent
1155         {0.5, 0.5, 0.5, 1.0}  // half brightness
1156         // Black's color table
1157         //{1.0, 1.0, 1.0, 1.0},
1158         //{1.0, 0.0, 0.0, 1.0},
1159         //{0.0, 1.0, 0.0, 1.0},
1160         //{0.0, 0.0, 1.0, 1.0},
1161         //{1.0, 1.0, 0.0, 1.0},
1162         //{0.0, 1.0, 1.0, 1.0},
1163         //{1.0, 0.0, 1.0, 1.0},
1164         //{0.1, 0.1, 0.1, 1.0}
1165 };
1166
1167 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1168
1169 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1170 {
1171         float C = r_textcontrast.value;
1172         float B = r_textbrightness.value;
1173         if (colorindex & 0x10000) // that bit means RGB color
1174         {
1175                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1176                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1177                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1178                 color[3] = (colorindex & 0xf) / 15.0;
1179         }
1180         else
1181                 Vector4Copy(string_colors[colorindex], color);
1182         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1183         if (shadow)
1184         {
1185                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1186                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1187         }
1188 }
1189
1190 // NOTE: this function always draws exactly one character if maxwidth <= 0
1191 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)
1192 {
1193         const char *text_start = text;
1194         int colorindex = STRING_COLOR_DEFAULT;
1195         size_t i;
1196         float x = 0;
1197         Uchar ch, mapch, nextch;
1198         Uchar prevch = 0; // used for kerning
1199         int tempcolorindex;
1200         float kx;
1201         int map_index = 0;
1202         size_t bytes_left;
1203         ft2_font_map_t *fontmap = NULL;
1204         ft2_font_map_t *map = NULL;
1205         //ft2_font_map_t *prevmap = NULL;
1206         ft2_font_t *ft2 = fnt->ft2;
1207         // float ftbase_x;
1208         qboolean snap = true;
1209         qboolean least_one = false;
1210         float dw; // display w
1211         //float dh; // display h
1212         const float *width_of;
1213
1214         if (!h) h = w;
1215         if (!h) {
1216                 w = h = 1;
1217                 snap = false;
1218         }
1219         // do this in the end
1220         w *= fnt->settings.scale;
1221         h *= fnt->settings.scale;
1222
1223         // find the most fitting size:
1224         if (ft2 != NULL)
1225         {
1226                 if (snap)
1227                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1228                 else
1229                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1230                 fontmap = Font_MapForIndex(ft2, map_index);
1231         }
1232
1233         dw = w * sw;
1234         //dh = h * sh;
1235
1236         if (*maxlen < 1)
1237                 *maxlen = 1<<30;
1238
1239         if (!outcolor || *outcolor == -1)
1240                 colorindex = STRING_COLOR_DEFAULT;
1241         else
1242                 colorindex = *outcolor;
1243
1244         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1245         // ftbase_x = snap_to_pixel_x(0);
1246         
1247         if(maxwidth <= 0)
1248         {
1249                 least_one = true;
1250                 maxwidth = -maxwidth;
1251         }
1252
1253         //if (snap)
1254         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1255
1256         if (fontmap)
1257                 width_of = fontmap->width_of;
1258         else
1259                 width_of = fnt->width_of;
1260
1261         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1262         {
1263                 size_t i0 = i;
1264                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1265                 i = text - text_start;
1266                 if (!ch)
1267                         break;
1268                 if (ch == ' ' && !fontmap)
1269                 {
1270                         if(!least_one || i0) // never skip the first character
1271                         if(x + width_of[(int) ' '] * dw > maxwidth)
1272                         {
1273                                 i = i0;
1274                                 break; // oops, can't draw this
1275                         }
1276                         x += width_of[(int) ' '] * dw;
1277                         continue;
1278                 }
1279                 // i points to the char after ^
1280                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1281                 {
1282                         ch = *text; // colors are ascii, so no u8_ needed
1283                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1284                         {
1285                                 colorindex = ch - '0';
1286                                 ++text;
1287                                 ++i;
1288                                 continue;
1289                         }
1290                         // i points to the char after ^...
1291                         // i+3 points to 3 in ^x123
1292                         // i+3 == *maxlen would mean that char is missing
1293                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1294                         {
1295                                 // building colorindex...
1296                                 ch = tolower(text[1]);
1297                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1298                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1299                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1300                                 else tempcolorindex = 0;
1301                                 if (tempcolorindex)
1302                                 {
1303                                         ch = tolower(text[2]);
1304                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1305                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1306                                         else tempcolorindex = 0;
1307                                         if (tempcolorindex)
1308                                         {
1309                                                 ch = tolower(text[3]);
1310                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1311                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1312                                                 else tempcolorindex = 0;
1313                                                 if (tempcolorindex)
1314                                                 {
1315                                                         colorindex = tempcolorindex | 0xf;
1316                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1317                                                         i+=4;
1318                                                         text += 4;
1319                                                         continue;
1320                                                 }
1321                                         }
1322                                 }
1323                         }
1324                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1325                         {
1326                                 i++;
1327                                 text++;
1328                         }
1329                         i--;
1330                 }
1331                 ch = nextch;
1332
1333                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1334                 {
1335                         if (ch > 0xE000)
1336                                 ch -= 0xE000;
1337                         if (ch > 0xFF)
1338                                 continue;
1339                         if (fontmap)
1340                                 map = ft2_oldstyle_map;
1341                         prevch = 0;
1342                         if(!least_one || i0) // never skip the first character
1343                         if(x + width_of[ch] * dw > maxwidth)
1344                         {
1345                                 i = i0;
1346                                 break; // oops, can't draw this
1347                         }
1348                         x += width_of[ch] * dw;
1349                 } else {
1350                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1351                         {
1352                                 map = FontMap_FindForChar(fontmap, ch);
1353                                 if (!map)
1354                                 {
1355                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1356                                                 break;
1357                                         if (!map)
1358                                                 break;
1359                                 }
1360                         }
1361                         mapch = ch - map->start;
1362                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1363                                 x += kx * dw;
1364                         x += map->glyphs[mapch].advance_x * dw;
1365                         //prevmap = map;
1366                         prevch = ch;
1367                 }
1368         }
1369
1370         *maxlen = i;
1371
1372         if (outcolor)
1373                 *outcolor = colorindex;
1374
1375         return x;
1376 }
1377
1378 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)
1379 {
1380         int shadow, colorindex = STRING_COLOR_DEFAULT;
1381         size_t i;
1382         float x = startx, y, s, t, u, v, thisw;
1383         float *av, *at, *ac;
1384         float color[4];
1385         int batchcount;
1386         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1387         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1388         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1389         Uchar ch, mapch, nextch;
1390         Uchar prevch = 0; // used for kerning
1391         int tempcolorindex;
1392         int map_index = 0;
1393         //ft2_font_map_t *prevmap = NULL; // the previous map
1394         ft2_font_map_t *map = NULL;     // the currently used map
1395         ft2_font_map_t *fontmap = NULL; // the font map for the size
1396         float ftbase_y;
1397         const char *text_start = text;
1398         float kx, ky;
1399         ft2_font_t *ft2 = fnt->ft2;
1400         qboolean snap = true;
1401         float pix_x, pix_y;
1402         size_t bytes_left;
1403         float dw, dh;
1404         const float *width_of;
1405
1406         int tw, th;
1407         tw = R_TextureWidth(fnt->tex);
1408         th = R_TextureHeight(fnt->tex);
1409
1410         if (!h) h = w;
1411         if (!h) {
1412                 h = w = 1;
1413                 snap = false;
1414         }
1415
1416         starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1417         w *= fnt->settings.scale;
1418         h *= fnt->settings.scale;
1419
1420         if (ft2 != NULL)
1421         {
1422                 if (snap)
1423                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1424                 else
1425                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1426                 fontmap = Font_MapForIndex(ft2, map_index);
1427         }
1428
1429         dw = w * sw;
1430         dh = h * sh;
1431
1432         // draw the font at its baseline when using freetype
1433         //ftbase_x = 0;
1434         ftbase_y = dh * (4.5/6.0);
1435
1436         if (maxlen < 1)
1437                 maxlen = 1<<30;
1438
1439         _DrawQ_ProcessDrawFlag(flags);
1440         if(!r_draw2d.integer && !r_draw2d_force)
1441                 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1442
1443         R_Mesh_ColorPointer(color4f, 0, 0);
1444         R_Mesh_ResetTextureState();
1445         if (!fontmap)
1446                 R_Mesh_TexBind(0, fnt->tex);
1447         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1448         R_Mesh_VertexPointer(vertex3f, 0, 0);
1449         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1450
1451         ac = color4f;
1452         at = texcoord2f;
1453         av = vertex3f;
1454         batchcount = 0;
1455
1456         //ftbase_x = snap_to_pixel_x(ftbase_x);
1457         if(snap)
1458         {
1459                 startx = snap_to_pixel_x(startx, 0.4);
1460                 starty = snap_to_pixel_y(starty, 0.4);
1461                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1462         }
1463
1464         pix_x = vid.width / vid_conwidth.value;
1465         pix_y = vid.height / vid_conheight.value;
1466
1467         if (fontmap)
1468                 width_of = fontmap->width_of;
1469         else
1470                 width_of = fnt->width_of;
1471
1472         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1473         {
1474                 prevch = 0;
1475                 text = text_start;
1476
1477                 if (!outcolor || *outcolor == -1)
1478                         colorindex = STRING_COLOR_DEFAULT;
1479                 else
1480                         colorindex = *outcolor;
1481
1482                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1483
1484                 x = startx;
1485                 y = starty;
1486                 /*
1487                 if (shadow)
1488                 {
1489                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1490                         y += r_textshadow.value * vid.height / vid_conheight.value;
1491                 }
1492                 */
1493                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1494                 {
1495                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1496                         i = text - text_start;
1497                         if (!ch)
1498                                 break;
1499                         if (ch == ' ' && !fontmap)
1500                         {
1501                                 x += width_of[(int) ' '] * dw;
1502                                 continue;
1503                         }
1504                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1505                         {
1506                                 ch = *text; // colors are ascii, so no u8_ needed
1507                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1508                                 {
1509                                         colorindex = ch - '0';
1510                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1511                                         ++text;
1512                                         ++i;
1513                                         continue;
1514                                 }
1515                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1516                                 {
1517                                         // building colorindex...
1518                                         ch = tolower(text[1]);
1519                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1520                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1521                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1522                                         else tempcolorindex = 0;
1523                                         if (tempcolorindex)
1524                                         {
1525                                                 ch = tolower(text[2]);
1526                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1527                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1528                                                 else tempcolorindex = 0;
1529                                                 if (tempcolorindex)
1530                                                 {
1531                                                         ch = tolower(text[3]);
1532                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1533                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1534                                                         else tempcolorindex = 0;
1535                                                         if (tempcolorindex)
1536                                                         {
1537                                                                 colorindex = tempcolorindex | 0xf;
1538                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1539                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1540                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1541                                                                 i+=4;
1542                                                                 text+=4;
1543                                                                 continue;
1544                                                         }
1545                                                 }
1546                                         }
1547                                 }
1548                                 else if (ch == STRING_COLOR_TAG)
1549                                 {
1550                                         i++;
1551                                         text++;
1552                                 }
1553                                 i--;
1554                         }
1555                         // get the backup
1556                         ch = nextch;
1557                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1558                         // this way we don't need to rebind fnt->tex for every old-style character
1559                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1560                         if (shadow)
1561                         {
1562                                 x += 1.0/pix_x * r_textshadow.value;
1563                                 y += 1.0/pix_y * r_textshadow.value;
1564                         }
1565                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1566                         {
1567                                 if (ch > 0xE000)
1568                                         ch -= 0xE000;
1569                                 if (ch > 0xFF)
1570                                         continue;
1571                                 if (fontmap)
1572                                 {
1573                                         if (map != ft2_oldstyle_map)
1574                                         {
1575                                                 if (batchcount)
1576                                                 {
1577                                                         // switching from freetype to non-freetype rendering
1578                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1579                                                         batchcount = 0;
1580                                                         ac = color4f;
1581                                                         at = texcoord2f;
1582                                                         av = vertex3f;
1583                                                 }
1584                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1585                                                 map = ft2_oldstyle_map;
1586                                         }
1587                                 }
1588                                 prevch = 0;
1589                                 //num = (unsigned char) text[i];
1590                                 //thisw = fnt->width_of[num];
1591                                 thisw = fnt->width_of[ch];
1592                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1593                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1594                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1595                                 u = 0.0625f * thisw - (1.0f / tw);
1596                                 v = 0.0625f - (1.0f / th);
1597                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1598                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1599                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1600                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1601                                 at[ 0] = s              ; at[ 1] = t    ;
1602                                 at[ 2] = s+u    ; at[ 3] = t    ;
1603                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1604                                 at[ 6] = s              ; at[ 7] = t+v  ;
1605                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1606                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1607                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1608                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1609                                 ac += 16;
1610                                 at += 8;
1611                                 av += 12;
1612                                 batchcount++;
1613                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1614                                 {
1615                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1616                                         batchcount = 0;
1617                                         ac = color4f;
1618                                         at = texcoord2f;
1619                                         av = vertex3f;
1620                                 }
1621                                 x += width_of[ch] * dw;
1622                         } else {
1623                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1624                                 {
1625                                         // new charmap - need to render
1626                                         if (batchcount)
1627                                         {
1628                                                 // we need a different character map, render what we currently have:
1629                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1630                                                 batchcount = 0;
1631                                                 ac = color4f;
1632                                                 at = texcoord2f;
1633                                                 av = vertex3f;
1634                                         }
1635                                         // find the new map
1636                                         map = FontMap_FindForChar(fontmap, ch);
1637                                         if (!map)
1638                                         {
1639                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1640                                                 {
1641                                                         shadow = -1;
1642                                                         break;
1643                                                 }
1644                                                 if (!map)
1645                                                 {
1646                                                         // this shouldn't happen
1647                                                         shadow = -1;
1648                                                         break;
1649                                                 }
1650                                         }
1651                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1652                                 }
1653
1654                                 mapch = ch - map->start;
1655                                 thisw = map->glyphs[mapch].advance_x;
1656
1657                                 //x += ftbase_x;
1658                                 y += ftbase_y;
1659                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1660                                 {
1661                                         x += kx * dw;
1662                                         y += ky * dh;
1663                                 }
1664                                 else
1665                                         kx = ky = 0;
1666                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1667                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1668                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1669                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1670                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1671                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1672                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1673                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1674                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1675                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1676                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1677                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1678                                 //x -= ftbase_x;
1679                                 y -= ftbase_y;
1680
1681                                 x += thisw * dw;
1682                                 ac += 16;
1683                                 at += 8;
1684                                 av += 12;
1685                                 batchcount++;
1686                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1687                                 {
1688                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1689                                         batchcount = 0;
1690                                         ac = color4f;
1691                                         at = texcoord2f;
1692                                         av = vertex3f;
1693                                 }
1694
1695                                 //prevmap = map;
1696                                 prevch = ch;
1697                         }
1698                         if (shadow)
1699                         {
1700                                 x -= 1.0/pix_x * r_textshadow.value;
1701                                 y -= 1.0/pix_y * r_textshadow.value;
1702                         }
1703                 }
1704         }
1705         if (batchcount > 0)
1706                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1707
1708         if (outcolor)
1709                 *outcolor = colorindex;
1710
1711         // note: this relies on the proper text (not shadow) being drawn last
1712         return x;
1713 }
1714
1715 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)
1716 {
1717         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1718 }
1719
1720 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)
1721 {
1722         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1723 }
1724
1725 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1726 {
1727         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1728 }
1729
1730 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1731 {
1732         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1733 }
1734
1735 #if 0
1736 // not used
1737 // no ^xrgb management
1738 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1739 {
1740         int color, numchars = 0;
1741         char *outputend2c = output2c + maxoutchars - 2;
1742         if (!outcolor || *outcolor == -1)
1743                 color = STRING_COLOR_DEFAULT;
1744         else
1745                 color = *outcolor;
1746         if (!maxreadchars)
1747                 maxreadchars = 1<<30;
1748         textend = text + maxreadchars;
1749         while (text != textend && *text)
1750         {
1751                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1752                 {
1753                         if (text[1] == STRING_COLOR_TAG)
1754                                 text++;
1755                         else if (text[1] >= '0' && text[1] <= '9')
1756                         {
1757                                 color = text[1] - '0';
1758                                 text += 2;
1759                                 continue;
1760                         }
1761                 }
1762                 if (output2c >= outputend2c)
1763                         break;
1764                 *output2c++ = *text++;
1765                 *output2c++ = color;
1766                 numchars++;
1767         }
1768         output2c[0] = output2c[1] = 0;
1769         if (outcolor)
1770                 *outcolor = color;
1771         return numchars;
1772 }
1773 #endif
1774
1775 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)
1776 {
1777         float floats[36];
1778
1779         _DrawQ_ProcessDrawFlag(flags);
1780         if(!r_draw2d.integer && !r_draw2d_force)
1781                 return;
1782
1783         R_Mesh_VertexPointer(floats, 0, 0);
1784         R_Mesh_ColorPointer(floats + 20, 0, 0);
1785         R_Mesh_ResetTextureState();
1786         if (pic)
1787         {
1788                 if (width == 0)
1789                         width = pic->width;
1790                 if (height == 0)
1791                         height = pic->height;
1792                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1793                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1794                 floats[12] = s1;floats[13] = t1;
1795                 floats[14] = s2;floats[15] = t2;
1796                 floats[16] = s4;floats[17] = t4;
1797                 floats[18] = s3;floats[19] = t3;
1798         }
1799         else
1800                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1801
1802         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1803         floats[0] = floats[9] = x;
1804         floats[1] = floats[4] = y;
1805         floats[3] = floats[6] = x + width;
1806         floats[7] = floats[10] = y + height;
1807         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1808         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1809         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1810         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1811
1812         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1813 }
1814
1815 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1816 {
1817         _DrawQ_ProcessDrawFlag(flags);
1818         if(!r_draw2d.integer && !r_draw2d_force)
1819                 return;
1820
1821         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1822         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1823         R_Mesh_ResetTextureState();
1824         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1825         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1826
1827         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1828 }
1829
1830 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1831 {
1832         int num;
1833
1834         _DrawQ_ProcessDrawFlag(flags);
1835         if(!r_draw2d.integer && !r_draw2d_force)
1836                 return;
1837
1838         GL_Color(1,1,1,1);
1839         CHECKGLERROR
1840         qglBegin(GL_LINE_LOOP);
1841         for (num = 0;num < mesh->num_vertices;num++)
1842         {
1843                 if (mesh->data_color4f)
1844                         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]);
1845                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1846         }
1847         qglEnd();
1848         CHECKGLERROR
1849 }
1850
1851 //[515]: this is old, delete
1852 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1853 {
1854         _DrawQ_ProcessDrawFlag(flags);
1855         if(!r_draw2d.integer && !r_draw2d_force)
1856                 return;
1857
1858         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1859
1860         CHECKGLERROR
1861         //qglLineWidth(width);CHECKGLERROR
1862
1863         GL_Color(r,g,b,alpha);
1864         CHECKGLERROR
1865         qglBegin(GL_LINES);
1866         qglVertex2f(x1, y1);
1867         qglVertex2f(x2, y2);
1868         qglEnd();
1869         CHECKGLERROR
1870 }
1871
1872 void DrawQ_SetClipArea(float x, float y, float width, float height)
1873 {
1874         int ix, iy, iw, ih;
1875         _DrawQ_Setup();
1876
1877         // We have to convert the con coords into real coords
1878         // OGL uses top to bottom
1879         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1880         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1881         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1882         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1883         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1884
1885         GL_ScissorTest(true);
1886 }
1887
1888 void DrawQ_ResetClipArea(void)
1889 {
1890         _DrawQ_Setup();
1891         GL_ScissorTest(false);
1892 }
1893
1894 void DrawQ_Finish(void)
1895 {
1896         r_refdef.draw2dstage = false;
1897 }
1898
1899 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1900 void R_DrawGamma(void)
1901 {
1902         float c[4];
1903         switch(vid.renderpath)
1904         {
1905         case RENDERPATH_GL20:
1906         case RENDERPATH_CGGL:
1907                 if (vid_usinghwgamma || v_glslgamma.integer)
1908                         return;
1909                 break;
1910         case RENDERPATH_GL13:
1911         case RENDERPATH_GL11:
1912                 if (vid_usinghwgamma)
1913                         return;
1914                 break;
1915         }
1916         // all the blends ignore depth
1917         R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1918         R_Mesh_ColorPointer(NULL, 0, 0);
1919         R_Mesh_ResetTextureState();
1920         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1921         GL_DepthMask(true);
1922         GL_DepthRange(0, 1);
1923         GL_PolygonOffset(0, 0);
1924         GL_DepthTest(false);
1925         if (v_color_enable.integer)
1926         {
1927                 c[0] = v_color_white_r.value;
1928                 c[1] = v_color_white_g.value;
1929                 c[2] = v_color_white_b.value;
1930         }
1931         else
1932                 c[0] = c[1] = c[2] = v_contrast.value;
1933         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1934         {
1935                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1936                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1937                 {
1938                         GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1939                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1940                         VectorScale(c, 0.5, c);
1941                 }
1942         }
1943         if (v_color_enable.integer)
1944         {
1945                 c[0] = v_color_black_r.value;
1946                 c[1] = v_color_black_g.value;
1947                 c[2] = v_color_black_b.value;
1948         }
1949         else
1950                 c[0] = c[1] = c[2] = v_brightness.value;
1951         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1952         {
1953                 GL_BlendFunc(GL_ONE, GL_ONE);
1954                 GL_Color(c[0], c[1], c[2], 1);
1955                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1956         }
1957 }
1958