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