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