]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_draw.c
formed DP_GFX_FONTS/DP_GFX_FONTS_FREETYPE/DP_UTF8 extensions, add loadfont()/findfont...
[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
33 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)"};
34 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)"};
35 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)"};
36
37 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
38 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
39 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
40 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
42 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
43 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
44
45 extern cvar_t v_glslgamma;
46
47 //=============================================================================
48 /* Support Routines */
49
50 #define FONT_FILESIZE 13468
51 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
52 static cachepic_t cachepics[MAX_CACHED_PICS];
53 static int numcachepics;
54
55 static rtexturepool_t *drawtexturepool;
56
57 static const unsigned char concharimage[FONT_FILESIZE] =
58 {
59 #include "lhfont.h"
60 };
61
62 static rtexture_t *draw_generateconchars(void)
63 {
64         int i;
65         unsigned char *data;
66         double random;
67         rtexture_t *tex;
68
69         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
70 // Gold numbers
71         for (i = 0;i < 8192;i++)
72         {
73                 random = lhrandom (0.0,1.0);
74                 data[i*4+3] = data[i*4+0];
75                 data[i*4+2] = 83 + (unsigned char)(random * 64);
76                 data[i*4+1] = 71 + (unsigned char)(random * 32);
77                 data[i*4+0] = 23 + (unsigned char)(random * 16);
78         }
79 // White chars
80         for (i = 8192;i < 32768;i++)
81         {
82                 random = lhrandom (0.0,1.0);
83                 data[i*4+3] = data[i*4+0];
84                 data[i*4+2] = 95 + (unsigned char)(random * 64);
85                 data[i*4+1] = 95 + (unsigned char)(random * 64);
86                 data[i*4+0] = 95 + (unsigned char)(random * 64);
87         }
88 // Gold numbers
89         for (i = 32768;i < 40960;i++)
90         {
91                 random = lhrandom (0.0,1.0);
92                 data[i*4+3] = data[i*4+0];
93                 data[i*4+2] = 83 + (unsigned char)(random * 64);
94                 data[i*4+1] = 71 + (unsigned char)(random * 32);
95                 data[i*4+0] = 23 + (unsigned char)(random * 16);
96         }
97 // Red chars
98         for (i = 40960;i < 65536;i++)
99         {
100                 random = lhrandom (0.0,1.0);
101                 data[i*4+3] = data[i*4+0];
102                 data[i*4+2] = 96 + (unsigned char)(random * 64);
103                 data[i*4+1] = 43 + (unsigned char)(random * 32);
104                 data[i*4+0] = 27 + (unsigned char)(random * 32);
105         }
106
107 #if 0
108         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
109 #endif
110
111         tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL);
112         Mem_Free(data);
113         return tex;
114 }
115
116 static rtexture_t *draw_generateditherpattern(void)
117 {
118         int x, y;
119         unsigned char pixels[8][8];
120         for (y = 0;y < 8;y++)
121                 for (x = 0;x < 8;x++)
122                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
123         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent);
124 }
125
126 typedef struct embeddedpic_s
127 {
128         const char *name;
129         int width;
130         int height;
131         const char *pixels;
132 }
133 embeddedpic_t;
134
135 static const embeddedpic_t embeddedpics[] =
136 {
137         {
138         "gfx/prydoncursor001", 16, 16,
139         "477777774......."
140         "77.....6........"
141         "7.....6........."
142         "7....6.........."
143         "7.....6........."
144         "7..6...6........"
145         "7.6.6...6......."
146         "76...6...6......"
147         "4.....6.6......."
148         ".......6........"
149         "................"
150         "................"
151         "................"
152         "................"
153         "................"
154         "................"
155         },
156         {
157         "ui/mousepointer", 16, 16,
158         "477777774......."
159         "77.....6........"
160         "7.....6........."
161         "7....6.........."
162         "7.....6........."
163         "7..6...6........"
164         "7.6.6...6......."
165         "76...6...6......"
166         "4.....6.6......."
167         ".......6........"
168         "................"
169         "................"
170         "................"
171         "................"
172         "................"
173         "................"
174         },
175         {
176         "gfx/crosshair1", 16, 16,
177         "................"
178         "................"
179         "................"
180         "...33......33..."
181         "...355....553..."
182         "....577..775...."
183         ".....77..77....."
184         "................"
185         "................"
186         ".....77..77....."
187         "....577..775...."
188         "...355....553..."
189         "...33......33..."
190         "................"
191         "................"
192         "................"
193         },
194         {
195         "gfx/crosshair2", 16, 16,
196         "................"
197         "................"
198         "................"
199         "...3........3..."
200         "....5......5...."
201         ".....7....7....."
202         "......7..7......"
203         "................"
204         "................"
205         "......7..7......"
206         ".....7....7....."
207         "....5......5...."
208         "...3........3..."
209         "................"
210         "................"
211         "................"
212         },
213         {
214         "gfx/crosshair3", 16, 16,
215         "................"
216         ".......77......."
217         ".......77......."
218         "................"
219         "................"
220         ".......44......."
221         ".......44......."
222         ".77..44..44..77."
223         ".77..44..44..77."
224         ".......44......."
225         ".......44......."
226         "................"
227         "................"
228         ".......77......."
229         ".......77......."
230         "................"
231         },
232         {
233         "gfx/crosshair4", 16, 16,
234         "................"
235         "................"
236         "................"
237         "................"
238         "................"
239         "................"
240         "................"
241         "................"
242         "........7777777."
243         "........752....."
244         "........72......"
245         "........7......."
246         "........7......."
247         "........7......."
248         "........7......."
249         "................"
250         },
251         {
252         "gfx/crosshair5", 8, 8,
253         "........"
254         "........"
255         "....7..."
256         "........"
257         "..7.7.7."
258         "........"
259         "....7..."
260         "........"
261         },
262         {
263         "gfx/crosshair6", 2, 2,
264         "77"
265         "77"
266         },
267         {
268         "gfx/crosshair7", 16, 16,
269         "................"
270         ".3............3."
271         "..5...2332...5.."
272         "...7.3....3.7..."
273         "....7......7...."
274         "...3.7....7.3..."
275         "..2...7..7...2.."
276         "..3..........3.."
277         "..3..........3.."
278         "..2...7..7...2.."
279         "...3.7....7.3..."
280         "....7......7...."
281         "...7.3....3.7..."
282         "..5...2332...5.."
283         ".3............3."
284         "................"
285         },
286         {NULL, 0, 0, NULL}
287 };
288
289 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
290 {
291         const embeddedpic_t *p;
292         for (p = embeddedpics;p->name;p++)
293                 if (!strcmp(name, p->name))
294                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic);
295         if (!strcmp(name, "gfx/conchars"))
296                 return draw_generateconchars();
297         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
298                 return draw_generateditherpattern();
299         if (!quiet)
300                 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
301         return r_texture_notexture;
302 }
303
304
305 /*
306 ================
307 Draw_CachePic
308 ================
309 */
310 // FIXME: move this to client somehow
311 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
312 {
313         int crc, hashkey;
314         unsigned char *pixels;
315         cachepic_t *pic;
316         fs_offset_t lmpsize;
317         unsigned char *lmpdata;
318         char lmpname[MAX_QPATH];
319
320         // check whether the picture has already been cached
321         crc = CRC_Block((unsigned char *)path, strlen(path));
322         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
323         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
324                 if (!strcmp (path, pic->name))
325                         return pic;
326
327         if (numcachepics == MAX_CACHED_PICS)
328         {
329                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
330                 // FIXME: support NULL in callers?
331                 return cachepics; // return the first one
332         }
333         pic = cachepics + (numcachepics++);
334         strlcpy (pic->name, path, sizeof(pic->name));
335         // link into list
336         pic->chain = cachepichash[hashkey];
337         cachepichash[hashkey] = pic;
338
339         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
340         pic->tex = CL_GetDynTexture( path );
341         // if so, set the width/height, too
342         if( pic->tex ) {
343                 pic->width = R_TextureWidth(pic->tex);
344                 pic->height = R_TextureHeight(pic->tex);
345                 // we're done now (early-out)
346                 return pic;
347         }
348
349         pic->texflags = TEXF_ALPHA;
350         if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
351                 pic->texflags |= TEXF_CLAMP;
352         if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
353                 pic->texflags |= TEXF_COMPRESS;
354
355         pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
356
357         // load a high quality image from disk if possible
358         pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer);
359         if (pixels == NULL && !strncmp(path, "gfx/", 4))
360                 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer);
361         if (pixels)
362         {
363                 pic->width = image_width;
364                 pic->height = image_height;
365                 if (!pic->autoload)
366                         pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
367         }
368         else
369         {
370                 pic->autoload = false;
371                 // never compress the fallback images
372                 pic->texflags &= ~TEXF_COMPRESS;
373         }
374
375         // now read the low quality version (wad or lmp file), and take the pic
376         // size from that even if we don't upload the texture, this way the pics
377         // show up the right size in the menu even if they were replaced with
378         // higher or lower resolution versions
379         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
380         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
381         {
382                 if (developer_loading.integer)
383                         Con_Printf("loading lump \"%s\"\n", path);
384
385                 if (lmpsize >= 9)
386                 {
387                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
388                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
389                         // if no high quality replacement image was found, upload the original low quality texture
390                         if (!pixels)
391                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
392                 }
393                 Mem_Free(lmpdata);
394         }
395         else if ((lmpdata = W_GetLumpName (path + 4)))
396         {
397                 if (developer_loading.integer)
398                         Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
399
400                 if (!strcmp(path, "gfx/conchars"))
401                 {
402                         // conchars is a raw image and with color 0 as transparent instead of 255
403                         pic->width = 128;
404                         pic->height = 128;
405                         // if no high quality replacement image was found, upload the original low quality texture
406                         if (!pixels)
407                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
408                 }
409                 else
410                 {
411                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
412                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
413                         // if no high quality replacement image was found, upload the original low quality texture
414                         if (!pixels)
415                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
416                 }
417         }
418
419         if (pixels)
420         {
421                 Mem_Free(pixels);
422                 pixels = NULL;
423         }
424         else if (pic->tex == NULL)
425         {
426                 // if it's not found on disk, generate an image
427                 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
428                 pic->width = R_TextureWidth(pic->tex);
429                 pic->height = R_TextureHeight(pic->tex);
430         }
431
432         return pic;
433 }
434
435 cachepic_t *Draw_CachePic (const char *path)
436 {
437         return Draw_CachePic_Flags (path, 0);
438 }
439
440 int draw_frame = 1;
441
442 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
443 {
444         if (pic->autoload && !pic->tex)
445         {
446                 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
447                 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
448                         pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
449                 if (pic->tex == NULL)
450                         pic->tex = draw_generatepic(pic->name, true);
451         }
452         pic->lastusedframe = draw_frame;
453         return pic->tex;
454 }
455
456 void Draw_Frame(void)
457 {
458         int i;
459         cachepic_t *pic;
460         static double nextpurgetime;
461         if (nextpurgetime > realtime)
462                 return;
463         nextpurgetime = realtime + 0.05;
464         for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
465         {
466                 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
467                 {
468                         R_FreeTexture(pic->tex);
469                         pic->tex = NULL;
470                 }
471         }
472         draw_frame++;
473 }
474
475 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
476 {
477         int crc, hashkey;
478         cachepic_t *pic;
479
480         crc = CRC_Block((unsigned char *)picname, strlen(picname));
481         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
482         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
483                 if (!strcmp (picname, pic->name))
484                         break;
485
486         if (pic)
487         {
488                 if (pic->tex && pic->width == width && pic->height == height)
489                 {
490                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
491                         return pic;
492                 }
493         }
494         else
495         {
496                 if (pic == NULL)
497                 {
498                         if (numcachepics == MAX_CACHED_PICS)
499                         {
500                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
501                                 // FIXME: support NULL in callers?
502                                 return cachepics; // return the first one
503                         }
504                         pic = cachepics + (numcachepics++);
505                         strlcpy (pic->name, picname, sizeof(pic->name));
506                         // link into list
507                         pic->chain = cachepichash[hashkey];
508                         cachepichash[hashkey] = pic;
509                 }
510         }
511
512         pic->width = width;
513         pic->height = height;
514         if (pic->tex)
515                 R_FreeTexture(pic->tex);
516         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
517         return pic;
518 }
519
520 void Draw_FreePic(const char *picname)
521 {
522         int crc;
523         int hashkey;
524         cachepic_t *pic;
525         // this doesn't really free the pic, but does free it's texture
526         crc = CRC_Block((unsigned char *)picname, strlen(picname));
527         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
528         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
529         {
530                 if (!strcmp (picname, pic->name) && pic->tex)
531                 {
532                         R_FreeTexture(pic->tex);
533                         pic->tex = NULL;
534                         pic->width = 0;
535                         pic->height = 0;
536                         return;
537                 }
538         }
539 }
540
541 static float snap_to_pixel_x(float x, float roundUpAt);
542 extern int con_linewidth; // to force rewrapping
543 void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
544 {
545         int i;
546         float maxwidth, scale;
547         char widthfile[MAX_QPATH];
548         char *widthbuf;
549         fs_offset_t widthbufsize;
550
551         if(override || !fnt->texpath[0])
552         {
553                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
554
555                 // load the cvars when the font is FIRST loaded
556                 fnt->settings.antialias = r_font_antialias.integer;
557                 fnt->settings.hinting = r_font_hinting.integer;
558                 fnt->settings.outline = r_font_postprocess_outline.value;
559                 fnt->settings.blur = r_font_postprocess_blur.value;
560                 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
561                 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
562                 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
563         }
564
565         if(drawtexturepool == NULL)
566                 return; // before gl_draw_start, so will be loaded later
567
568         if(fnt->ft2)
569         {
570                 // clear freetype font
571                 Font_UnloadFont(fnt->ft2);
572                 Mem_Free(fnt->ft2);
573                 fnt->ft2 = NULL;
574         }
575
576         if(fnt->req_face != -1)
577         {
578                 if(!Font_LoadFont(fnt->texpath, fnt))
579                         Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
580         }
581
582         fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
583         if(fnt->tex == r_texture_notexture)
584         {
585                 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
586                 {
587                         if (!fnt->fallbacks[i][0])
588                                 break;
589                         fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
590                         if(fnt->tex != r_texture_notexture)
591                                 break;
592                 }
593                 if(fnt->tex == r_texture_notexture)
594                 {
595                         fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
596                         strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
597                 }
598                 else
599                         dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
600         }
601         else
602                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
603
604         // unspecified width == 1 (base width)
605         for(i = 1; i < 256; ++i)
606                 fnt->width_of[i] = 1;
607         scale = 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
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(tempmempool, 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;
753         const char *filelist, *c, *cm;
754         float sz;
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[...] [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                         );
772                 return;
773         }
774         f = FindFont(Cmd_Argv(1), true);
775         if(f == NULL)
776         {
777                 Con_Printf("font function not found\n");
778                 return;
779         }
780
781         if(Cmd_Argc() < 3)
782                 filelist = "gfx/conchars";
783         else
784                 filelist = Cmd_Argv(2);
785
786         memset(f->fallbacks, 0, sizeof(f->fallbacks));
787         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
788
789         // first font is handled "normally"
790         c = strchr(filelist, ':');
791         cm = strchr(filelist, ',');
792         if(c && (!cm || c < cm))
793                 f->req_face = atoi(c+1);
794         else
795         {
796                 f->req_face = 0;
797                 c = cm;
798         }
799
800         if(!c || (c - filelist) > MAX_QPATH)
801                 strlcpy(mainfont, filelist, sizeof(mainfont));
802         else
803         {
804                 memcpy(mainfont, filelist, c - filelist);
805                 mainfont[c - filelist] = 0;
806         }
807
808         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
809         {
810                 c = strchr(filelist, ',');
811                 if(!c)
812                         break;
813                 filelist = c + 1;
814                 if(!*filelist)
815                         break;
816                 c = strchr(filelist, ':');
817                 cm = strchr(filelist, ',');
818                 if(c && (!cm || c < cm))
819                         f->fallback_faces[i] = atoi(c+1);
820                 else
821                 {
822                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
823                         c = cm;
824                 }
825                 if(!c || (c-filelist) > MAX_QPATH)
826                 {
827                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
828                 }
829                 else
830                 {
831                         memcpy(f->fallbacks[i], filelist, c - filelist);
832                         f->fallbacks[i][c - filelist] = 0;
833                 }
834         }
835
836         // for now: by default load only one size: the default size
837         f->req_sizes[0] = 0;
838         for(i = 1; i < MAX_FONT_SIZES; ++i)
839                 f->req_sizes[i] = -1;
840
841         if(Cmd_Argc() >= 3)
842         {
843                 for(i = 0; i < Cmd_Argc()-3; ++i)
844                 {
845                         sz = atof(Cmd_Argv(i+3));
846                         if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
847                                 f->req_sizes[i] = sz;
848                 }
849         }
850
851         LoadFont(true, mainfont, f);
852 }
853
854 /*
855 ===============
856 Draw_Init
857 ===============
858 */
859 static void gl_draw_start(void)
860 {
861         int i;
862         drawtexturepool = R_AllocTexturePool();
863
864         numcachepics = 0;
865         memset(cachepichash, 0, sizeof(cachepichash));
866
867         font_start();
868
869         // load default font textures
870         for(i = 0; i < dp_fonts.maxsize; ++i)
871                 if (dp_fonts.f[i].title[0])
872                         LoadFont(false, va("gfx/font_%s", &dp_fonts.f[i].title), &dp_fonts.f[i]);
873
874         // draw the loading screen so people have something to see in the newly opened window
875         SCR_UpdateLoadingScreen(true);
876 }
877
878 static void gl_draw_shutdown(void)
879 {
880         font_shutdown();
881
882         R_FreeTexturePool(&drawtexturepool);
883
884         numcachepics = 0;
885         memset(cachepichash, 0, sizeof(cachepichash));
886 }
887
888 static void gl_draw_newmap(void)
889 {
890         font_newmap();
891 }
892
893 void GL_Draw_Init (void)
894 {
895         int i, j;
896
897         Cvar_RegisterVariable(&r_font_postprocess_blur);
898         Cvar_RegisterVariable(&r_font_postprocess_outline);
899         Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
900         Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
901         Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
902         Cvar_RegisterVariable(&r_font_hinting);
903         Cvar_RegisterVariable(&r_font_antialias);
904         Cvar_RegisterVariable(&r_textshadow);
905         Cvar_RegisterVariable(&r_textbrightness);
906         Cvar_RegisterVariable(&r_textcontrast);
907
908         // allocate fonts storage
909         dp_fonts.maxsize = MAX_FONTS;
910         dp_fonts.f = Mem_Alloc(tempmempool, sizeof(dp_font_t) * dp_fonts.maxsize);
911         memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
912
913         // assign starting font names
914         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
915         strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
916         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
917         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
918         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
919         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
920         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
921         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
922         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
923         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
924                 if(!FONT_USER(i)->title[0])
925                         dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
926
927         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
928         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
929 }
930
931 void _DrawQ_Setup(void)
932 {
933         r_viewport_t viewport;
934         if (r_refdef.draw2dstage)
935                 return;
936         r_refdef.draw2dstage = true;
937         CHECKGLERROR
938         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);
939         R_SetViewport(&viewport);
940         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
941         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
942         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
943         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
944         R_EntityMatrix(&identitymatrix);
945
946         GL_DepthMask(true);
947         GL_DepthRange(0, 1);
948         GL_PolygonOffset(0, 0);
949         GL_DepthTest(false);
950         GL_Color(1,1,1,1);
951         GL_AlphaTest(false);
952         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
953 }
954
955 static void _DrawQ_ProcessDrawFlag(int flags)
956 {
957         _DrawQ_Setup();
958         CHECKGLERROR
959         if(flags == DRAWFLAG_ADDITIVE)
960                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
961         else if(flags == DRAWFLAG_MODULATE)
962                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
963         else if(flags == DRAWFLAG_2XMODULATE)
964                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
965         else if(flags == DRAWFLAG_SCREEN)
966                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
967         else
968                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
969 }
970
971 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
972 {
973         float floats[36];
974
975         _DrawQ_ProcessDrawFlag(flags);
976
977         R_Mesh_ResetTextureState();
978         floats[12] = 0.0f;floats[13] = 0.0f;
979         floats[14] = 1.0f;floats[15] = 0.0f;
980         floats[16] = 1.0f;floats[17] = 1.0f;
981         floats[18] = 0.0f;floats[19] = 1.0f;
982         floats[20] = floats[24] = floats[28] = floats[32] = red;
983         floats[21] = floats[25] = floats[29] = floats[33] = green;
984         floats[22] = floats[26] = floats[30] = floats[34] = blue;
985         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
986         if (pic)
987         {
988                 if (width == 0)
989                         width = pic->width;
990                 if (height == 0)
991                         height = pic->height;
992                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
993
994 #if 0
995       // AK07: lets be texel correct on the corners
996       {
997          float horz_offset = 0.5f / pic->width;
998          float vert_offset = 0.5f / pic->height;
999
1000                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1001                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1002                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1003                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1004       }
1005 #endif
1006         }
1007         else
1008                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1009
1010         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1011         floats[0] = floats[9] = x;
1012         floats[1] = floats[4] = y;
1013         floats[3] = floats[6] = x + width;
1014         floats[7] = floats[10] = y + height;
1015
1016         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1017         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1018 }
1019
1020 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)
1021 {
1022         float floats[36];
1023         float af = DEG2RAD(-angle); // forward
1024         float ar = DEG2RAD(-angle + 90); // right
1025         float sinaf = sin(af);
1026         float cosaf = cos(af);
1027         float sinar = sin(ar);
1028         float cosar = cos(ar);
1029
1030         _DrawQ_ProcessDrawFlag(flags);
1031
1032         R_Mesh_ResetTextureState();
1033         if (pic)
1034         {
1035                 if (width == 0)
1036                         width = pic->width;
1037                 if (height == 0)
1038                         height = pic->height;
1039                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1040         }
1041         else
1042                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1043
1044         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1045
1046 // top left
1047         floats[0] = x - cosaf*org_x - cosar*org_y;
1048         floats[1] = y - sinaf*org_x - sinar*org_y;
1049
1050 // top right
1051         floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1052         floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1053
1054 // bottom right
1055         floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1056         floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1057
1058 // bottom left
1059         floats[9]  = x - cosaf*org_x + cosar*(height-org_y);
1060         floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1061
1062         floats[12] = 0.0f;floats[13] = 0.0f;
1063         floats[14] = 1.0f;floats[15] = 0.0f;
1064         floats[16] = 1.0f;floats[17] = 1.0f;
1065         floats[18] = 0.0f;floats[19] = 1.0f;
1066         floats[20] = floats[24] = floats[28] = floats[32] = red;
1067         floats[21] = floats[25] = floats[29] = floats[33] = green;
1068         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1069         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1070
1071         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1072         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1073 }
1074
1075 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1076 {
1077         float floats[36];
1078
1079         _DrawQ_ProcessDrawFlag(flags);
1080
1081         R_Mesh_ResetTextureState();
1082         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1083
1084         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1085         floats[0] = floats[9] = x;
1086         floats[1] = floats[4] = y;
1087         floats[3] = floats[6] = x + width;
1088         floats[7] = floats[10] = y + height;
1089         floats[12] = 0.0f;floats[13] = 0.0f;
1090         floats[14] = 1.0f;floats[15] = 0.0f;
1091         floats[16] = 1.0f;floats[17] = 1.0f;
1092         floats[18] = 0.0f;floats[19] = 1.0f;
1093         floats[20] = floats[24] = floats[28] = floats[32] = red;
1094         floats[21] = floats[25] = floats[29] = floats[33] = green;
1095         floats[22] = floats[26] = floats[30] = floats[34] = blue;
1096         floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1097
1098         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1099         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1100 }
1101
1102 /// color tag printing
1103 static const vec4_t string_colors[] =
1104 {
1105         // Quake3 colors
1106         // LordHavoc: why on earth is cyan before magenta in Quake3?
1107         // LordHavoc: note: Doom3 uses white for [0] and [7]
1108         {0.0, 0.0, 0.0, 1.0}, // black
1109         {1.0, 0.0, 0.0, 1.0}, // red
1110         {0.0, 1.0, 0.0, 1.0}, // green
1111         {1.0, 1.0, 0.0, 1.0}, // yellow
1112         {0.0, 0.0, 1.0, 1.0}, // blue
1113         {0.0, 1.0, 1.0, 1.0}, // cyan
1114         {1.0, 0.0, 1.0, 1.0}, // magenta
1115         {1.0, 1.0, 1.0, 1.0}, // white
1116         // [515]'s BX_COLOREDTEXT extension
1117         {1.0, 1.0, 1.0, 0.5}, // half transparent
1118         {0.5, 0.5, 0.5, 1.0}  // half brightness
1119         // Black's color table
1120         //{1.0, 1.0, 1.0, 1.0},
1121         //{1.0, 0.0, 0.0, 1.0},
1122         //{0.0, 1.0, 0.0, 1.0},
1123         //{0.0, 0.0, 1.0, 1.0},
1124         //{1.0, 1.0, 0.0, 1.0},
1125         //{0.0, 1.0, 1.0, 1.0},
1126         //{1.0, 0.0, 1.0, 1.0},
1127         //{0.1, 0.1, 0.1, 1.0}
1128 };
1129
1130 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
1131
1132 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1133 {
1134         float C = r_textcontrast.value;
1135         float B = r_textbrightness.value;
1136         if (colorindex & 0x10000) // that bit means RGB color
1137         {
1138                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1139                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1140                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1141                 color[3] = (colorindex & 0xf) / 15.0;
1142         }
1143         else
1144                 Vector4Copy(string_colors[colorindex], color);
1145         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1146         if (shadow)
1147         {
1148                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1149                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1150         }
1151 }
1152
1153 // NOTE: this function always draws exactly one character if maxwidth <= 0
1154 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)
1155 {
1156         const char *text_start = text;
1157         int colorindex = STRING_COLOR_DEFAULT;
1158         size_t i;
1159         float x = 0;
1160         Uchar ch, mapch, nextch;
1161         Uchar prevch = 0; // used for kerning
1162         int tempcolorindex;
1163         float kx;
1164         int map_index = 0;
1165         size_t bytes_left;
1166         ft2_font_map_t *fontmap = NULL;
1167         ft2_font_map_t *map = NULL;
1168         //ft2_font_map_t *prevmap = NULL;
1169         ft2_font_t *ft2 = fnt->ft2;
1170         // float ftbase_x;
1171         qboolean snap = true;
1172         qboolean least_one = false;
1173         float dw; // display w
1174         //float dh; // display h
1175         const float *width_of;
1176
1177         if (!h) h = w;
1178         if (!h) {
1179                 w = h = 1;
1180                 snap = false;
1181         }
1182         // do this in the end
1183         w *= fnt->scale;
1184         h *= fnt->scale;
1185
1186         // find the most fitting size:
1187         if (ft2 != NULL)
1188         {
1189                 if (snap)
1190                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1191                 else
1192                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1193                 fontmap = Font_MapForIndex(ft2, map_index);
1194         }
1195
1196         dw = w * sw;
1197         //dh = h * sh;
1198
1199         if (*maxlen < 1)
1200                 *maxlen = 1<<30;
1201
1202         if (!outcolor || *outcolor == -1)
1203                 colorindex = STRING_COLOR_DEFAULT;
1204         else
1205                 colorindex = *outcolor;
1206
1207         // maxwidth /= fnt->scale; // w and h are multiplied by it already
1208         // ftbase_x = snap_to_pixel_x(0);
1209         
1210         if(maxwidth <= 0)
1211         {
1212                 least_one = true;
1213                 maxwidth = -maxwidth;
1214         }
1215
1216         //if (snap)
1217         //      x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1218
1219         if (fontmap)
1220                 width_of = fontmap->width_of;
1221         else
1222                 width_of = fnt->width_of;
1223
1224         for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1225         {
1226                 size_t i0 = i;
1227                 nextch = ch = u8_getnchar(text, &text, bytes_left);
1228                 i = text - text_start;
1229                 if (!ch)
1230                         break;
1231                 if (ch == ' ' && !fontmap)
1232                 {
1233                         if(!least_one || i0) // never skip the first character
1234                         if(x + width_of[(int) ' '] * dw > maxwidth)
1235                         {
1236                                 i = i0;
1237                                 break; // oops, can't draw this
1238                         }
1239                         x += width_of[(int) ' '] * dw;
1240                         continue;
1241                 }
1242                 // i points to the char after ^
1243                 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1244                 {
1245                         ch = *text; // colors are ascii, so no u8_ needed
1246                         if (ch <= '9' && ch >= '0') // ^[0-9] found
1247                         {
1248                                 colorindex = ch - '0';
1249                                 ++text;
1250                                 ++i;
1251                                 continue;
1252                         }
1253                         // i points to the char after ^...
1254                         // i+3 points to 3 in ^x123
1255                         // i+3 == *maxlen would mean that char is missing
1256                         else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1257                         {
1258                                 // building colorindex...
1259                                 ch = tolower(text[1]);
1260                                 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1261                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1262                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1263                                 else tempcolorindex = 0;
1264                                 if (tempcolorindex)
1265                                 {
1266                                         ch = tolower(text[2]);
1267                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1268                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1269                                         else tempcolorindex = 0;
1270                                         if (tempcolorindex)
1271                                         {
1272                                                 ch = tolower(text[3]);
1273                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1274                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1275                                                 else tempcolorindex = 0;
1276                                                 if (tempcolorindex)
1277                                                 {
1278                                                         colorindex = tempcolorindex | 0xf;
1279                                                         // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1280                                                         i+=4;
1281                                                         text += 4;
1282                                                         continue;
1283                                                 }
1284                                         }
1285                                 }
1286                         }
1287                         else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1288                         {
1289                                 i++;
1290                                 text++;
1291                         }
1292                         i--;
1293                 }
1294                 ch = nextch;
1295
1296                 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1297                 {
1298                         if (ch > 0xE000)
1299                                 ch -= 0xE000;
1300                         if (ch > 0xFF)
1301                                 continue;
1302                         if (fontmap)
1303                                 map = ft2_oldstyle_map;
1304                         prevch = 0;
1305                         if(!least_one || i0) // never skip the first character
1306                         if(x + width_of[ch] * dw > maxwidth)
1307                         {
1308                                 i = i0;
1309                                 break; // oops, can't draw this
1310                         }
1311                         x += width_of[ch] * dw;
1312                 } else {
1313                         if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1314                         {
1315                                 map = FontMap_FindForChar(fontmap, ch);
1316                                 if (!map)
1317                                 {
1318                                         if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1319                                                 break;
1320                                         if (!map)
1321                                                 break;
1322                                 }
1323                         }
1324                         mapch = ch - map->start;
1325                         if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1326                                 x += kx * dw;
1327                         x += map->glyphs[mapch].advance_x * dw;
1328                         //prevmap = map;
1329                         prevch = ch;
1330                 }
1331         }
1332
1333         *maxlen = i;
1334
1335         if (outcolor)
1336                 *outcolor = colorindex;
1337
1338         return x;
1339 }
1340
1341 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)
1342 {
1343         int shadow, colorindex = STRING_COLOR_DEFAULT;
1344         size_t i;
1345         float x = startx, y, s, t, u, v, thisw;
1346         float *av, *at, *ac;
1347         float color[4];
1348         int batchcount;
1349         static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1350         static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1351         static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1352         Uchar ch, mapch, nextch;
1353         Uchar prevch = 0; // used for kerning
1354         int tempcolorindex;
1355         int map_index = 0;
1356         //ft2_font_map_t *prevmap = NULL; // the previous map
1357         ft2_font_map_t *map = NULL;     // the currently used map
1358         ft2_font_map_t *fontmap = NULL; // the font map for the size
1359         float ftbase_y;
1360         const char *text_start = text;
1361         float kx, ky;
1362         ft2_font_t *ft2 = fnt->ft2;
1363         qboolean snap = true;
1364         float pix_x, pix_y;
1365         size_t bytes_left;
1366         float dw, dh;
1367         const float *width_of;
1368
1369         int tw, th;
1370         tw = R_TextureWidth(fnt->tex);
1371         th = R_TextureHeight(fnt->tex);
1372
1373         if (!h) h = w;
1374         if (!h) {
1375                 h = w = 1;
1376                 snap = false;
1377         }
1378
1379         starty -= (fnt->scale - 1) * h * 0.5; // center
1380         w *= fnt->scale;
1381         h *= fnt->scale;
1382
1383         if (ft2 != NULL)
1384         {
1385                 if (snap)
1386                         map_index = Font_IndexForSize(ft2, h, &w, &h);
1387                 else
1388                         map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1389                 fontmap = Font_MapForIndex(ft2, map_index);
1390         }
1391
1392         dw = w * sw;
1393         dh = h * sh;
1394
1395         // draw the font at its baseline when using freetype
1396         //ftbase_x = 0;
1397         ftbase_y = dh * (4.5/6.0);
1398
1399         if (maxlen < 1)
1400                 maxlen = 1<<30;
1401
1402         _DrawQ_ProcessDrawFlag(flags);
1403
1404         R_Mesh_ResetTextureState();
1405         if (!fontmap)
1406                 R_Mesh_TexBind(0, fnt->tex);
1407         R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1408
1409         ac = color4f;
1410         at = texcoord2f;
1411         av = vertex3f;
1412         batchcount = 0;
1413
1414         //ftbase_x = snap_to_pixel_x(ftbase_x);
1415         if(snap)
1416         {
1417                 startx = snap_to_pixel_x(startx, 0.4);
1418                 starty = snap_to_pixel_y(starty, 0.4);
1419                 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1420         }
1421
1422         pix_x = vid.width / vid_conwidth.value;
1423         pix_y = vid.height / vid_conheight.value;
1424
1425         if (fontmap)
1426                 width_of = fontmap->width_of;
1427         else
1428                 width_of = fnt->width_of;
1429
1430         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1431         {
1432                 prevch = 0;
1433                 text = text_start;
1434
1435                 if (!outcolor || *outcolor == -1)
1436                         colorindex = STRING_COLOR_DEFAULT;
1437                 else
1438                         colorindex = *outcolor;
1439
1440                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1441
1442                 x = startx;
1443                 y = starty;
1444                 /*
1445                 if (shadow)
1446                 {
1447                         x += r_textshadow.value * vid.width / vid_conwidth.value;
1448                         y += r_textshadow.value * vid.height / vid_conheight.value;
1449                 }
1450                 */
1451                 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1452                 {
1453                         nextch = ch = u8_getnchar(text, &text, bytes_left);
1454                         i = text - text_start;
1455                         if (!ch)
1456                                 break;
1457                         if (ch == ' ' && !fontmap)
1458                         {
1459                                 x += width_of[(int) ' '] * dw;
1460                                 continue;
1461                         }
1462                         if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1463                         {
1464                                 ch = *text; // colors are ascii, so no u8_ needed
1465                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
1466                                 {
1467                                         colorindex = ch - '0';
1468                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1469                                         ++text;
1470                                         ++i;
1471                                         continue;
1472                                 }
1473                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1474                                 {
1475                                         // building colorindex...
1476                                         ch = tolower(text[1]);
1477                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1478                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1479                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1480                                         else tempcolorindex = 0;
1481                                         if (tempcolorindex)
1482                                         {
1483                                                 ch = tolower(text[2]);
1484                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1485                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1486                                                 else tempcolorindex = 0;
1487                                                 if (tempcolorindex)
1488                                                 {
1489                                                         ch = tolower(text[3]);
1490                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1491                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1492                                                         else tempcolorindex = 0;
1493                                                         if (tempcolorindex)
1494                                                         {
1495                                                                 colorindex = tempcolorindex | 0xf;
1496                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1497                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1498                                                                 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1499                                                                 i+=4;
1500                                                                 text+=4;
1501                                                                 continue;
1502                                                         }
1503                                                 }
1504                                         }
1505                                 }
1506                                 else if (ch == STRING_COLOR_TAG)
1507                                 {
1508                                         i++;
1509                                         text++;
1510                                 }
1511                                 i--;
1512                         }
1513                         // get the backup
1514                         ch = nextch;
1515                         // using a value of -1 for the oldstyle map because NULL means uninitialized...
1516                         // this way we don't need to rebind fnt->tex for every old-style character
1517                         // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1518                         if (shadow)
1519                         {
1520                                 x += 1.0/pix_x * r_textshadow.value;
1521                                 y += 1.0/pix_y * r_textshadow.value;
1522                         }
1523                         if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1524                         {
1525                                 if (ch > 0xE000)
1526                                         ch -= 0xE000;
1527                                 if (ch > 0xFF)
1528                                         continue;
1529                                 if (fontmap)
1530                                 {
1531                                         if (map != ft2_oldstyle_map)
1532                                         {
1533                                                 if (batchcount)
1534                                                 {
1535                                                         // switching from freetype to non-freetype rendering
1536                                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1537                                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1538                                                         batchcount = 0;
1539                                                         ac = color4f;
1540                                                         at = texcoord2f;
1541                                                         av = vertex3f;
1542                                                 }
1543                                                 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1544                                                 map = ft2_oldstyle_map;
1545                                         }
1546                                 }
1547                                 prevch = 0;
1548                                 //num = (unsigned char) text[i];
1549                                 //thisw = fnt->width_of[num];
1550                                 thisw = fnt->width_of[ch];
1551                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1552                                 s = (ch & 15)*0.0625f + (0.5f / tw);
1553                                 t = (ch >> 4)*0.0625f + (0.5f / th);
1554                                 u = 0.0625f * thisw - (1.0f / tw);
1555                                 v = 0.0625f - (1.0f / th);
1556                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1557                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1558                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1559                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1560                                 at[ 0] = s              ; at[ 1] = t    ;
1561                                 at[ 2] = s+u    ; at[ 3] = t    ;
1562                                 at[ 4] = s+u    ; at[ 5] = t+v  ;
1563                                 at[ 6] = s              ; at[ 7] = t+v  ;
1564                                 av[ 0] = x                      ; av[ 1] = y    ; av[ 2] = 10;
1565                                 av[ 3] = x+dw*thisw     ; av[ 4] = y    ; av[ 5] = 10;
1566                                 av[ 6] = x+dw*thisw     ; av[ 7] = y+dh ; av[ 8] = 10;
1567                                 av[ 9] = x                      ; av[10] = y+dh ; av[11] = 10;
1568                                 ac += 16;
1569                                 at += 8;
1570                                 av += 12;
1571                                 batchcount++;
1572                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1573                                 {
1574                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1575                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1576                                         batchcount = 0;
1577                                         ac = color4f;
1578                                         at = texcoord2f;
1579                                         av = vertex3f;
1580                                 }
1581                                 x += width_of[ch] * dw;
1582                         } else {
1583                                 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1584                                 {
1585                                         // new charmap - need to render
1586                                         if (batchcount)
1587                                         {
1588                                                 // we need a different character map, render what we currently have:
1589                                                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1590                                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1591                                                 batchcount = 0;
1592                                                 ac = color4f;
1593                                                 at = texcoord2f;
1594                                                 av = vertex3f;
1595                                         }
1596                                         // find the new map
1597                                         map = FontMap_FindForChar(fontmap, ch);
1598                                         if (!map)
1599                                         {
1600                                                 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1601                                                 {
1602                                                         shadow = -1;
1603                                                         break;
1604                                                 }
1605                                                 if (!map)
1606                                                 {
1607                                                         // this shouldn't happen
1608                                                         shadow = -1;
1609                                                         break;
1610                                                 }
1611                                         }
1612                                         R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1613                                 }
1614
1615                                 mapch = ch - map->start;
1616                                 thisw = map->glyphs[mapch].advance_x;
1617
1618                                 //x += ftbase_x;
1619                                 y += ftbase_y;
1620                                 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1621                                 {
1622                                         x += kx * dw;
1623                                         y += ky * dh;
1624                                 }
1625                                 else
1626                                         kx = ky = 0;
1627                                 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1628                                 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1629                                 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1630                                 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1631                                 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1632                                 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1633                                 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1634                                 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1635                                 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1636                                 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1637                                 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1638                                 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1639                                 //x -= ftbase_x;
1640                                 y -= ftbase_y;
1641
1642                                 x += thisw * dw;
1643                                 ac += 16;
1644                                 at += 8;
1645                                 av += 12;
1646                                 batchcount++;
1647                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1648                                 {
1649                                         R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1650                                         R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1651                                         batchcount = 0;
1652                                         ac = color4f;
1653                                         at = texcoord2f;
1654                                         av = vertex3f;
1655                                 }
1656
1657                                 //prevmap = map;
1658                                 prevch = ch;
1659                         }
1660                         if (shadow)
1661                         {
1662                                 x -= 1.0/pix_x * r_textshadow.value;
1663                                 y -= 1.0/pix_y * r_textshadow.value;
1664                         }
1665                 }
1666         }
1667         if (batchcount > 0)
1668         {
1669                 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1670                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1671         }
1672
1673         if (outcolor)
1674                 *outcolor = colorindex;
1675
1676         // note: this relies on the proper text (not shadow) being drawn last
1677         return x;
1678 }
1679
1680 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)
1681 {
1682         return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1683 }
1684
1685 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)
1686 {
1687         return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1688 }
1689
1690 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1691 {
1692         return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1693 }
1694
1695 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1696 {
1697         return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1698 }
1699
1700 #if 0
1701 // not used
1702 // no ^xrgb management
1703 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1704 {
1705         int color, numchars = 0;
1706         char *outputend2c = output2c + maxoutchars - 2;
1707         if (!outcolor || *outcolor == -1)
1708                 color = STRING_COLOR_DEFAULT;
1709         else
1710                 color = *outcolor;
1711         if (!maxreadchars)
1712                 maxreadchars = 1<<30;
1713         textend = text + maxreadchars;
1714         while (text != textend && *text)
1715         {
1716                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1717                 {
1718                         if (text[1] == STRING_COLOR_TAG)
1719                                 text++;
1720                         else if (text[1] >= '0' && text[1] <= '9')
1721                         {
1722                                 color = text[1] - '0';
1723                                 text += 2;
1724                                 continue;
1725                         }
1726                 }
1727                 if (output2c >= outputend2c)
1728                         break;
1729                 *output2c++ = *text++;
1730                 *output2c++ = color;
1731                 numchars++;
1732         }
1733         output2c[0] = output2c[1] = 0;
1734         if (outcolor)
1735                 *outcolor = color;
1736         return numchars;
1737 }
1738 #endif
1739
1740 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)
1741 {
1742         float floats[36];
1743
1744         _DrawQ_ProcessDrawFlag(flags);
1745
1746         R_Mesh_ResetTextureState();
1747         if (pic)
1748         {
1749                 if (width == 0)
1750                         width = pic->width;
1751                 if (height == 0)
1752                         height = pic->height;
1753                 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1754         }
1755         else
1756                 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1757
1758         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1759         floats[0] = floats[9] = x;
1760         floats[1] = floats[4] = y;
1761         floats[3] = floats[6] = x + width;
1762         floats[7] = floats[10] = y + height;
1763         floats[12] = s1;floats[13] = t1;
1764         floats[14] = s2;floats[15] = t2;
1765         floats[16] = s4;floats[17] = t4;
1766         floats[18] = s3;floats[19] = t3;
1767         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1768         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1769         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1770         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1771
1772         R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1773         R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1774 }
1775
1776 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1777 {
1778         _DrawQ_ProcessDrawFlag(flags);
1779
1780         R_Mesh_ResetTextureState();
1781         R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1782
1783         R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1784         R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1785 }
1786
1787 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1788 {
1789         int num;
1790
1791         _DrawQ_ProcessDrawFlag(flags);
1792
1793         GL_Color(1,1,1,1);
1794         CHECKGLERROR
1795         qglBegin(GL_LINE_LOOP);
1796         for (num = 0;num < mesh->num_vertices;num++)
1797         {
1798                 if (mesh->data_color4f)
1799                         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]);
1800                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1801         }
1802         qglEnd();
1803         CHECKGLERROR
1804 }
1805
1806 //[515]: this is old, delete
1807 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1808 {
1809         _DrawQ_ProcessDrawFlag(flags);
1810
1811         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1812
1813         CHECKGLERROR
1814         //qglLineWidth(width);CHECKGLERROR
1815
1816         GL_Color(r,g,b,alpha);
1817         CHECKGLERROR
1818         qglBegin(GL_LINES);
1819         qglVertex2f(x1, y1);
1820         qglVertex2f(x2, y2);
1821         qglEnd();
1822         CHECKGLERROR
1823 }
1824
1825 void DrawQ_SetClipArea(float x, float y, float width, float height)
1826 {
1827         int ix, iy, iw, ih;
1828         _DrawQ_Setup();
1829
1830         // We have to convert the con coords into real coords
1831         // OGL uses top to bottom
1832         ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1833         iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1834         iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1835         ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1836         GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1837
1838         GL_ScissorTest(true);
1839 }
1840
1841 void DrawQ_ResetClipArea(void)
1842 {
1843         _DrawQ_Setup();
1844         GL_ScissorTest(false);
1845 }
1846
1847 void DrawQ_Finish(void)
1848 {
1849         r_refdef.draw2dstage = false;
1850 }
1851
1852 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1853 void R_DrawGamma(void)
1854 {
1855         float c[4];
1856         switch(vid.renderpath)
1857         {
1858         case RENDERPATH_GL20:
1859         case RENDERPATH_CGGL:
1860                 if (vid_usinghwgamma || v_glslgamma.integer)
1861                         return;
1862                 break;
1863         case RENDERPATH_GL13:
1864         case RENDERPATH_GL11:
1865                 if (vid_usinghwgamma)
1866                         return;
1867                 break;
1868         }
1869         // all the blends ignore depth
1870         R_Mesh_ResetTextureState();
1871         R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1872         GL_DepthMask(true);
1873         GL_DepthRange(0, 1);
1874         GL_PolygonOffset(0, 0);
1875         GL_DepthTest(false);
1876         if (v_color_enable.integer)
1877         {
1878                 c[0] = v_color_white_r.value;
1879                 c[1] = v_color_white_g.value;
1880                 c[2] = v_color_white_b.value;
1881         }
1882         else
1883                 c[0] = c[1] = c[2] = v_contrast.value;
1884         if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1885         {
1886                 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1887                 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1888                 {
1889                         GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1890                         R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1891                         R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1892                         VectorScale(c, 0.5, c);
1893                 }
1894         }
1895         if (v_color_enable.integer)
1896         {
1897                 c[0] = v_color_black_r.value;
1898                 c[1] = v_color_black_g.value;
1899                 c[2] = v_color_black_b.value;
1900         }
1901         else
1902                 c[0] = c[1] = c[2] = v_brightness.value;
1903         if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1904         {
1905                 GL_BlendFunc(GL_ONE, GL_ONE);
1906                 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1907                 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1908                 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1909         }
1910 }
1911