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