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