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