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