split up DrawQ_String_Font_UntilX in DrawQ_TextWidth_Font_TrackColors and DrawQ_Strin...
[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 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
29
30 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)"};
31 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)"};
32
33 //=============================================================================
34 /* Support Routines */
35
36 #define FONT_FILESIZE 13468
37 #define MAX_CACHED_PICS 1024
38 #define CACHEPICHASHSIZE 256
39 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
40 static cachepic_t cachepics[MAX_CACHED_PICS];
41 static int numcachepics;
42
43 static rtexturepool_t *drawtexturepool;
44
45 static unsigned char concharimage[FONT_FILESIZE] =
46 {
47 #include "lhfont.h"
48 };
49
50 static rtexture_t *draw_generateconchars(void)
51 {
52         int i;
53         unsigned char buffer[65536][4], *data = NULL;
54         double random;
55
56         data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
57 // Gold numbers
58         for (i = 0;i < 8192;i++)
59         {
60                 random = lhrandom (0.0,1.0);
61                 buffer[i][2] = 83 + (unsigned char)(random * 64);
62                 buffer[i][1] = 71 + (unsigned char)(random * 32);
63                 buffer[i][0] = 23 + (unsigned char)(random * 16);
64                 buffer[i][3] = data[i*4+0];
65         }
66 // White chars
67         for (i = 8192;i < 32768;i++)
68         {
69                 random = lhrandom (0.0,1.0);
70                 buffer[i][2] = 95 + (unsigned char)(random * 64);
71                 buffer[i][1] = 95 + (unsigned char)(random * 64);
72                 buffer[i][0] = 95 + (unsigned char)(random * 64);
73                 buffer[i][3] = data[i*4+0];
74         }
75 // Gold numbers
76         for (i = 32768;i < 40960;i++)
77         {
78                 random = lhrandom (0.0,1.0);
79                 buffer[i][2] = 83 + (unsigned char)(random * 64);
80                 buffer[i][1] = 71 + (unsigned char)(random * 32);
81                 buffer[i][0] = 23 + (unsigned char)(random * 16);
82                 buffer[i][3] = data[i*4+0];
83         }
84 // Red chars
85         for (i = 40960;i < 65536;i++)
86         {
87                 random = lhrandom (0.0,1.0);
88                 buffer[i][2] = 96 + (unsigned char)(random * 64);
89                 buffer[i][1] = 43 + (unsigned char)(random * 32);
90                 buffer[i][0] = 27 + (unsigned char)(random * 32);
91                 buffer[i][3] = data[i*4+0];
92         }
93
94 #if 0
95         Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
96 #endif
97
98         Mem_Free(data);
99         return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
100 }
101
102 static rtexture_t *draw_generateditherpattern(void)
103 {
104         int x, y;
105         unsigned char pixels[8][8];
106         for (y = 0;y < 8;y++)
107                 for (x = 0;x < 8;x++)
108                         pixels[y][x] = ((x^y) & 4) ? 254 : 0;
109         return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, NULL);
110 }
111
112 typedef struct embeddedpic_s
113 {
114         const char *name;
115         int width;
116         int height;
117         const char *pixels;
118 }
119 embeddedpic_t;
120
121 static embeddedpic_t embeddedpics[] =
122 {
123         {
124         "gfx/prydoncursor001", 16, 16,
125         "477777774......."
126         "77.....6........"
127         "7.....6........."
128         "7....6.........."
129         "7.....6........."
130         "7..6...6........"
131         "7.6.6...6......."
132         "76...6...6......"
133         "4.....6.6......."
134         ".......6........"
135         "................"
136         "................"
137         "................"
138         "................"
139         "................"
140         "................"
141         },
142         {
143         "ui/mousepointer", 16, 16,
144         "477777774......."
145         "77.....6........"
146         "7.....6........."
147         "7....6.........."
148         "7.....6........."
149         "7..6...6........"
150         "7.6.6...6......."
151         "76...6...6......"
152         "4.....6.6......."
153         ".......6........"
154         "................"
155         "................"
156         "................"
157         "................"
158         "................"
159         "................"
160         },
161         {
162         "gfx/crosshair1", 16, 16,
163         "................"
164         "................"
165         "................"
166         "...33......33..."
167         "...355....553..."
168         "....577..775...."
169         ".....77..77....."
170         "................"
171         "................"
172         ".....77..77....."
173         "....577..775...."
174         "...355....553..."
175         "...33......33..."
176         "................"
177         "................"
178         "................"
179         },
180         {
181         "gfx/crosshair2", 16, 16,
182         "................"
183         "................"
184         "................"
185         "...3........3..."
186         "....5......5...."
187         ".....7....7....."
188         "......7..7......"
189         "................"
190         "................"
191         "......7..7......"
192         ".....7....7....."
193         "....5......5...."
194         "...3........3..."
195         "................"
196         "................"
197         "................"
198         },
199         {
200         "gfx/crosshair3", 16, 16,
201         "................"
202         ".......77......."
203         ".......77......."
204         "................"
205         "................"
206         ".......44......."
207         ".......44......."
208         ".77..44..44..77."
209         ".77..44..44..77."
210         ".......44......."
211         ".......44......."
212         "................"
213         "................"
214         ".......77......."
215         ".......77......."
216         "................"
217         },
218         {
219         "gfx/crosshair4", 16, 16,
220         "................"
221         "................"
222         "................"
223         "................"
224         "................"
225         "................"
226         "................"
227         "................"
228         "........7777777."
229         "........752....."
230         "........72......"
231         "........7......."
232         "........7......."
233         "........7......."
234         "........7......."
235         "................"
236         },
237         {
238         "gfx/crosshair5", 8, 8,
239         "........"
240         "........"
241         "....7..."
242         "........"
243         "..7.7.7."
244         "........"
245         "....7..."
246         "........"
247         },
248         {
249         "gfx/crosshair6", 2, 2,
250         "77"
251         "77"
252         },
253         {
254         "gfx/crosshair7", 16, 16,
255         "................"
256         ".3............3."
257         "..5...2332...5.."
258         "...7.3....3.7..."
259         "....7......7...."
260         "...3.7....7.3..."
261         "..2...7..7...2.."
262         "..3..........3.."
263         "..3..........3.."
264         "..2...7..7...2.."
265         "...3.7....7.3..."
266         "....7......7...."
267         "...7.3....3.7..."
268         "..5...2332...5.."
269         ".3............3."
270         "................"
271         },
272         {
273         "gfx/editlights/cursor", 16, 16,
274         "................"
275         ".3............3."
276         "..5...2332...5.."
277         "...7.3....3.7..."
278         "....7......7...."
279         "...3.7....7.3..."
280         "..2...7..7...2.."
281         "..3..........3.."
282         "..3..........3.."
283         "..2...7..7...2.."
284         "...3.7....7.3..."
285         "....7......7...."
286         "...7.3....3.7..."
287         "..5...2332...5.."
288         ".3............3."
289         "................"
290         },
291         {
292         "gfx/editlights/light", 16, 16,
293         "................"
294         "................"
295         "......1111......"
296         "....11233211...."
297         "...1234554321..."
298         "...1356776531..."
299         "..124677776421.."
300         "..135777777531.."
301         "..135777777531.."
302         "..124677776421.."
303         "...1356776531..."
304         "...1234554321..."
305         "....11233211...."
306         "......1111......"
307         "................"
308         "................"
309         },
310         {
311         "gfx/editlights/noshadow", 16, 16,
312         "................"
313         "................"
314         "......1111......"
315         "....11233211...."
316         "...1234554321..."
317         "...1356226531..."
318         "..12462..26421.."
319         "..1352....2531.."
320         "..1352....2531.."
321         "..12462..26421.."
322         "...1356226531..."
323         "...1234554321..."
324         "....11233211...."
325         "......1111......"
326         "................"
327         "................"
328         },
329         {
330         "gfx/editlights/selection", 16, 16,
331         "................"
332         ".777752..257777."
333         ".742........247."
334         ".72..........27."
335         ".7............7."
336         ".5............5."
337         ".2............2."
338         "................"
339         "................"
340         ".2............2."
341         ".5............5."
342         ".7............7."
343         ".72..........27."
344         ".742........247."
345         ".777752..257777."
346         "................"
347         },
348         {
349         "gfx/editlights/cubemaplight", 16, 16,
350         "................"
351         "................"
352         "......2772......"
353         "....27755772...."
354         "..277533335772.."
355         "..753333333357.."
356         "..777533335777.."
357         "..735775577537.."
358         "..733357753337.."
359         "..733337733337.."
360         "..753337733357.."
361         "..277537735772.."
362         "....27777772...."
363         "......2772......"
364         "................"
365         "................"
366         },
367         {
368         "gfx/editlights/cubemapnoshadowlight", 16, 16,
369         "................"
370         "................"
371         "......2772......"
372         "....27722772...."
373         "..2772....2772.."
374         "..72........27.."
375         "..7772....2777.."
376         "..7.27722772.7.."
377         "..7...2772...7.."
378         "..7....77....7.."
379         "..72...77...27.."
380         "..2772.77.2772.."
381         "....27777772...."
382         "......2772......"
383         "................"
384         "................"
385         },
386         {NULL, 0, 0, NULL}
387 };
388
389 static rtexture_t *draw_generatepic(const char *name)
390 {
391         embeddedpic_t *p;
392         for (p = embeddedpics;p->name;p++)
393                 if (!strcmp(name, p->name))
394                         return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
395         if (!strcmp(name, "gfx/conchars"))
396                 return draw_generateconchars();
397         if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
398                 return draw_generateditherpattern();
399         Con_Printf("Draw_CachePic: failed to load %s\n", name);
400         return r_texture_notexture;
401 }
402
403
404 /*
405 ================
406 Draw_CachePic
407 ================
408 */
409 // FIXME: move this to client somehow
410 static cachepic_t       *Draw_CachePic_Compression (const char *path, qboolean persistent, qboolean allow_compression)
411 {
412         int crc, hashkey;
413         cachepic_t *pic;
414         int flags;
415         fs_offset_t lmpsize;
416         unsigned char *lmpdata;
417         char lmpname[MAX_QPATH];
418
419         // check whether the picture has already been cached
420         crc = CRC_Block((unsigned char *)path, strlen(path));
421         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
422         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
423                 if (!strcmp (path, pic->name))
424                         return pic;
425
426         if (numcachepics == MAX_CACHED_PICS)
427         {
428                 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
429                 // FIXME: support NULL in callers?
430                 return cachepics; // return the first one
431         }
432         pic = cachepics + (numcachepics++);
433         strlcpy (pic->name, path, sizeof(pic->name));
434         // link into list
435         pic->chain = cachepichash[hashkey];
436         cachepichash[hashkey] = pic;
437
438         // check whether it is an dynamic texture (if so, we can directly use its texture handler)
439         pic->tex = CL_GetDynTexture( path );
440         // if so, set the width/height, too
441         if( pic->tex ) {
442                 pic->width = R_TextureWidth(pic->tex);
443                 pic->height = R_TextureHeight(pic->tex);
444                 // we're done now (early-out)
445                 return pic;
446         }
447
448         flags = TEXF_ALPHA;
449         if (persistent)
450                 flags |= TEXF_PRECACHE;
451         if (strcmp(path, "gfx/colorcontrol/ditherpattern"))
452                 flags |= TEXF_CLAMP;
453         if(allow_compression && gl_texturecompression_2d.integer)
454                 flags |= TEXF_COMPRESS;
455
456         // load a high quality image from disk if possible
457         pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
458         if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
459         {
460                 // compatibility with older versions which did not require gfx/ prefix
461                 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
462         }
463         // if a high quality image was loaded, set the pic's size to match it, just
464         // in case there's no low quality version to get the size from
465         if (pic->tex)
466         {
467                 pic->width = R_TextureWidth(pic->tex);
468                 pic->height = R_TextureHeight(pic->tex);
469         }
470
471         // now read the low quality version (wad or lmp file), and take the pic
472         // size from that even if we don't upload the texture, this way the pics
473         // show up the right size in the menu even if they were replaced with
474         // higher or lower resolution versions
475         dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
476         if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
477         {
478                 if (lmpsize >= 9)
479                 {
480                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
481                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
482                         // if no high quality replacement image was found, upload the original low quality texture
483                         if (!pic->tex)
484                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
485                 }
486                 Mem_Free(lmpdata);
487         }
488         else if ((lmpdata = W_GetLumpName (path + 4)))
489         {
490                 if (!strcmp(path, "gfx/conchars"))
491                 {
492                         // conchars is a raw image and with color 0 as transparent instead of 255
493                         pic->width = 128;
494                         pic->height = 128;
495                         // if no high quality replacement image was found, upload the original low quality texture
496                         if (!pic->tex)
497                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
498                 }
499                 else
500                 {
501                         pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
502                         pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
503                         // if no high quality replacement image was found, upload the original low quality texture
504                         if (!pic->tex)
505                                 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
506                 }
507         }
508
509         // if it's not found on disk, generate an image
510         if (pic->tex == NULL)
511         {
512                 pic->tex = draw_generatepic(path);
513                 pic->width = R_TextureWidth(pic->tex);
514                 pic->height = R_TextureHeight(pic->tex);
515         }
516
517         return pic;
518 }
519 cachepic_t      *Draw_CachePic (const char *path, qboolean persistent)
520 {
521         return Draw_CachePic_Compression(path, persistent, true);
522 }
523
524 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
525 {
526         int crc, hashkey;
527         cachepic_t *pic;
528
529         crc = CRC_Block((unsigned char *)picname, strlen(picname));
530         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
531         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
532                 if (!strcmp (picname, pic->name))
533                         break;
534
535         if (pic)
536         {
537                 if (pic->tex && pic->width == width && pic->height == height)
538                 {
539                         R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
540                         return pic;
541                 }
542         }
543         else
544         {
545                 if (pic == NULL)
546                 {
547                         if (numcachepics == MAX_CACHED_PICS)
548                         {
549                                 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
550                                 // FIXME: support NULL in callers?
551                                 return cachepics; // return the first one
552                         }
553                         pic = cachepics + (numcachepics++);
554                         strlcpy (pic->name, picname, sizeof(pic->name));
555                         // link into list
556                         pic->chain = cachepichash[hashkey];
557                         cachepichash[hashkey] = pic;
558                 }
559         }
560
561         pic->width = width;
562         pic->height = height;
563         if (pic->tex)
564                 R_FreeTexture(pic->tex);
565         pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
566         return pic;
567 }
568
569 void Draw_FreePic(const char *picname)
570 {
571         int crc;
572         int hashkey;
573         cachepic_t *pic;
574         // this doesn't really free the pic, but does free it's texture
575         crc = CRC_Block((unsigned char *)picname, strlen(picname));
576         hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
577         for (pic = cachepichash[hashkey];pic;pic = pic->chain)
578         {
579                 if (!strcmp (picname, pic->name) && pic->tex)
580                 {
581                         R_FreeTexture(pic->tex);
582                         pic->width = 0;
583                         pic->height = 0;
584                         return;
585                 }
586         }
587 }
588
589 extern int con_linewidth; // to force rewrapping
590 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
591 {
592         int i;
593         float maxwidth;
594         char widthfile[MAX_QPATH];
595         char *widthbuf;
596         fs_offset_t widthbufsize;
597
598         if(override || !fnt->texpath[0])
599                 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
600
601         if(drawtexturepool == NULL)
602                 return; // before gl_draw_start, so will be loaded later
603
604         fnt->tex = Draw_CachePic_Compression(fnt->texpath, true, false)->tex;
605         if(fnt->tex == r_texture_notexture)
606         {
607                 fnt->tex = Draw_CachePic_Compression("gfx/conchars", true, false)->tex;
608                 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
609         }
610         else
611                 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
612
613         // unspecified width == 1 (base width)
614         for(i = 1; i < 256; ++i)
615                 fnt->width_of[i] = 1;
616
617         // FIXME load "name.width", if it fails, fill all with 1
618         if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
619         {
620                 float extraspacing = 0;
621                 const char *p = widthbuf;
622                 int ch = 0;
623
624                 while(ch < 256)
625                 {
626                         if(!COM_ParseToken_Simple(&p, false, false))
627                                 return;
628
629                         if(!strcmp(com_token, "extraspacing"))
630                         {
631                                 if(!COM_ParseToken_Simple(&p, false, false))
632                                         return;
633                                 extraspacing = atof(com_token);
634                         }
635                         else
636                                 fnt->width_of[ch++] = atof(com_token) + extraspacing;
637                 }
638
639                 Mem_Free(widthbuf);
640         }
641
642         maxwidth = fnt->width_of[1];
643         for(i = 2; i < 256; ++i)
644                 maxwidth = max(maxwidth, fnt->width_of[i]);
645         fnt->width_of[0] = maxwidth;
646
647         if(fnt == FONT_CONSOLE)
648                 con_linewidth = -1; // rewrap console in next frame
649 }
650
651 static dp_font_t *FindFont(const char *title)
652 {
653         int i;
654         for(i = 0; i < MAX_FONTS; ++i)
655                 if(!strcmp(dp_fonts[i].title, title))
656                         return &dp_fonts[i];
657         return NULL;
658 }
659
660 static void LoadFont_f(void)
661 {
662         dp_font_t *f;
663         int i;
664         if(Cmd_Argc() < 2)
665         {
666                 Con_Printf("Available font commands:\n");
667                 for(i = 0; i < MAX_FONTS; ++i)
668                         Con_Printf("  loadfont %s gfx/tgafile\n", dp_fonts[i].title);
669                 return;
670         }
671         f = FindFont(Cmd_Argv(1));
672         if(f == NULL)
673         {
674                 Con_Printf("font function not found\n");
675                 return;
676         }
677         LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
678 }
679
680 /*
681 ===============
682 Draw_Init
683 ===============
684 */
685 static void gl_draw_start(void)
686 {
687         int i;
688         drawtexturepool = R_AllocTexturePool();
689
690         numcachepics = 0;
691         memset(cachepichash, 0, sizeof(cachepichash));
692
693         for(i = 0; i < MAX_FONTS; ++i)
694                 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
695
696         // draw the loading screen so people have something to see in the newly opened window
697         SCR_UpdateLoadingScreen(true);
698 }
699
700 static void gl_draw_shutdown(void)
701 {
702         R_FreeTexturePool(&drawtexturepool);
703
704         numcachepics = 0;
705         memset(cachepichash, 0, sizeof(cachepichash));
706 }
707
708 static void gl_draw_newmap(void)
709 {
710 }
711
712 void GL_Draw_Init (void)
713 {
714         int i, j;
715         Cvar_RegisterVariable(&r_textshadow);
716         Cvar_RegisterVariable(&r_textbrightness);
717         Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
718         R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
719
720         strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
721                 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
722         strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
723         strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
724         strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
725         strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
726         strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
727         strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
728         strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
729         for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
730                 if(!FONT_USER[i].title[0])
731                         dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
732 }
733
734 static void _DrawQ_Setup(void)
735 {
736         if (r_refdef.draw2dstage)
737                 return;
738         r_refdef.draw2dstage = true;
739         CHECKGLERROR
740         qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
741         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
742         GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
743         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
744         qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
745         GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
746         R_Mesh_Matrix(&identitymatrix);
747
748         GL_DepthMask(true);
749         GL_DepthRange(0, 1);
750         GL_PolygonOffset(0, 0);
751         GL_DepthTest(false);
752         GL_Color(1,1,1,1);
753         GL_AlphaTest(false);
754         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
755
756         if (gl_support_fragment_shader)
757         {
758                 qglUseProgramObjectARB(0);CHECKGLERROR
759         }
760 }
761
762 static void _DrawQ_ProcessDrawFlag(int flags)
763 {
764         _DrawQ_Setup();
765         CHECKGLERROR
766         if(flags == DRAWFLAG_ADDITIVE)
767                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
768         else if(flags == DRAWFLAG_MODULATE)
769                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
770         else if(flags == DRAWFLAG_2XMODULATE)
771                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
772         else
773                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
774 }
775
776 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
777 {
778         float floats[20];
779
780         _DrawQ_ProcessDrawFlag(flags);
781         GL_Color(red, green, blue, alpha);
782
783         R_Mesh_VertexPointer(floats, 0, 0);
784         R_Mesh_ColorPointer(NULL, 0, 0);
785         R_Mesh_ResetTextureState();
786         if (pic)
787         {
788                 if (width == 0)
789                         width = pic->width;
790                 if (height == 0)
791                         height = pic->height;
792                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
793                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
794
795 #if 1
796                 floats[12] = 0.0f;floats[13] = 0.0f;
797                 floats[14] = 1.0f;floats[15] = 0.0f;
798                 floats[16] = 1.0f;floats[17] = 1.0f;
799                 floats[18] = 0.0f;floats[19] = 1.0f;
800 #else
801       // AK07: lets be texel correct on the corners
802       {
803          float horz_offset = 0.5f / pic->width;
804          float vert_offset = 0.5f / pic->height;
805
806                    floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
807                    floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
808                    floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
809                    floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
810       }
811 #endif
812         }
813
814         floats[2] = floats[5] = floats[8] = floats[11] = 0;
815         floats[0] = floats[9] = x;
816         floats[1] = floats[4] = y;
817         floats[3] = floats[6] = x + width;
818         floats[7] = floats[10] = y + height;
819
820         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
821 }
822
823 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
824 {
825         float floats[12];
826
827         _DrawQ_ProcessDrawFlag(flags);
828         GL_Color(red, green, blue, alpha);
829
830         R_Mesh_VertexPointer(floats, 0, 0);
831         R_Mesh_ColorPointer(NULL, 0, 0);
832         R_Mesh_ResetTextureState();
833
834         floats[2] = floats[5] = floats[8] = floats[11] = 0;
835         floats[0] = floats[9] = x;
836         floats[1] = floats[4] = y;
837         floats[3] = floats[6] = x + width;
838         floats[7] = floats[10] = y + height;
839
840         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
841 }
842
843 // color tag printing
844 static vec4_t string_colors[] =
845 {
846         // Quake3 colors
847         // LordHavoc: why on earth is cyan before magenta in Quake3?
848         // LordHavoc: note: Doom3 uses white for [0] and [7]
849         {0.0, 0.0, 0.0, 1.0}, // black
850         {1.0, 0.0, 0.0, 1.0}, // red
851         {0.0, 1.0, 0.0, 1.0}, // green
852         {1.0, 1.0, 0.0, 1.0}, // yellow
853         {0.0, 0.0, 1.0, 1.0}, // blue
854         {0.0, 1.0, 1.0, 1.0}, // cyan
855         {1.0, 0.0, 1.0, 1.0}, // magenta
856         {1.0, 1.0, 1.0, 1.0}, // white
857         // [515]'s BX_COLOREDTEXT extension
858         {1.0, 1.0, 1.0, 0.5}, // half transparent
859         {0.5, 0.5, 0.5, 1.0}  // half brightness
860         // Black's color table
861         //{1.0, 1.0, 1.0, 1.0},
862         //{1.0, 0.0, 0.0, 1.0},
863         //{0.0, 1.0, 0.0, 1.0},
864         //{0.0, 0.0, 1.0, 1.0},
865         //{1.0, 1.0, 0.0, 1.0},
866         //{0.0, 1.0, 1.0, 1.0},
867         //{1.0, 0.0, 1.0, 1.0},
868         //{0.1, 0.1, 0.1, 1.0}
869 };
870
871 #define STRING_COLORS_COUNT     (sizeof(string_colors) / sizeof(vec4_t))
872
873 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
874 {
875         float v = r_textbrightness.value;
876         Vector4Copy(string_colors[colorindex], color);
877         Vector4Set(color, (color[0] * (1-v) + v) * r, (color[1] * (1-v) + v) * g, (color[2] * (1-v) + v) * b, color[3] * a);
878         if (shadow)
879         {
880                 float shadowalpha = color[0]+color[1]+color[2] * 0.8;
881                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
882         }
883 }
884
885 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
886 {
887         int num, colorindex = STRING_COLOR_DEFAULT;
888         size_t i;
889         float x = 0;
890
891         if (*maxlen < 1)
892                 *maxlen = 1<<30;
893
894         if (!outcolor || *outcolor == -1)
895                 colorindex = STRING_COLOR_DEFAULT;
896         else
897                 colorindex = *outcolor;
898
899         for (i = 0;i < *maxlen && text[i];i++)
900         {
901                 if (text[i] == ' ')
902                 {
903                         if(x + fnt->width_of[' '] > maxwidth)
904                                 break; // oops, can't draw this
905                         x += fnt->width_of[' '];
906                         continue;
907                 }
908                 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
909                 {
910                         if (text[i+1] == STRING_COLOR_TAG)
911                         {
912                                 i++;
913                         }
914                         else if (text[i+1] >= '0' && text[i+1] <= '9')
915                         {
916                                 colorindex = text[i+1] - '0';
917                                 i++;
918                                 continue;
919                         }
920                 }
921                 num = (unsigned char) text[i];
922                 if(x + fnt->width_of[num] > maxwidth)
923                         break; // oops, can't draw this
924                 x += fnt->width_of[num];
925         }
926
927         *maxlen = i;
928
929         if (outcolor)
930                 *outcolor = colorindex;
931
932         return x;
933 }
934
935 float DrawQ_String_Font(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)
936 {
937         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
938         size_t i;
939         float x = startx, y, s, t, u, v;
940         float *av, *at, *ac;
941         float color[4];
942         int batchcount;
943         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
944         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
945         float color4f[QUADELEMENTS_MAXQUADS*4*4];
946
947         if (maxlen < 1)
948                 maxlen = 1<<30;
949
950         // when basealpha == 0, skip as much as possible (just return width)
951         if(basealpha > 0)
952         {
953                 _DrawQ_ProcessDrawFlag(flags);
954
955                 R_Mesh_ColorPointer(color4f, 0, 0);
956                 R_Mesh_ResetTextureState();
957                 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
958                 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
959                 R_Mesh_VertexPointer(vertex3f, 0, 0);
960         }
961
962         ac = color4f;
963         at = texcoord2f;
964         av = vertex3f;
965         batchcount = 0;
966
967         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
968         {
969                 if (!outcolor || *outcolor == -1)
970                         colorindex = STRING_COLOR_DEFAULT;
971                 else
972                         colorindex = *outcolor;
973                 if(basealpha > 0)
974                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
975
976                 x = startx;
977                 y = starty;
978                 if (shadow)
979                 {
980                         x += r_textshadow.value;
981                         y += r_textshadow.value;
982                 }
983                 for (i = 0;i < maxlen && text[i];i++)
984                 {
985                         if (text[i] == ' ')
986                         {
987                                 x += fnt->width_of[' '] * w;
988                                 continue;
989                         }
990                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
991                         {
992                                 if (text[i+1] == STRING_COLOR_TAG)
993                                 {
994                                         i++;
995                                 }
996                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
997                                 {
998                                         colorindex = text[i+1] - '0';
999                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1000                                         i++;
1001                                         continue;
1002                                 }
1003                         }
1004                         num = (unsigned char) text[i];
1005                         if(basealpha > 0)
1006                         {
1007                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1008                                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
1009                                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
1010                                 u = 0.0625f - (1.0f / 256.0f);
1011                                 v = 0.0625f - (1.0f / 256.0f);
1012                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1013                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1014                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1015                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1016                                 at[ 0] = s  ;at[ 1] = t  ;
1017                                 at[ 2] = s+u;at[ 3] = t  ;
1018                                 at[ 4] = s+u;at[ 5] = t+v;
1019                                 at[ 6] = s  ;at[ 7] = t+v;
1020                                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
1021                                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
1022                                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
1023                                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
1024                                 ac += 16;
1025                                 at += 8;
1026                                 av += 12;
1027                                 batchcount++;
1028                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
1029                                 {
1030                                         if (basealpha >= (1.0f / 255.0f))
1031                                         {
1032                                                 GL_LockArrays(0, batchcount * 4);
1033                                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1034                                                 GL_LockArrays(0, 0);
1035                                         }
1036                                         batchcount = 0;
1037                                         ac = color4f;
1038                                         at = texcoord2f;
1039                                         av = vertex3f;
1040                                 }
1041                         }
1042                         x += fnt->width_of[num] * w;
1043                 }
1044         }
1045         if (basealpha > 0)
1046         {
1047                 if (batchcount > 0)
1048                 {
1049                         GL_LockArrays(0, batchcount * 4);
1050                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1051                         GL_LockArrays(0, 0);
1052                 }
1053         }
1054
1055         if (outcolor)
1056                 *outcolor = colorindex;
1057
1058         // note: this relies on the proper text (not shadow) being drawn last
1059         return x;
1060 }
1061
1062 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)
1063 {
1064         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1065 }
1066
1067 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1068 {
1069         return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1070 }
1071
1072 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1073 {
1074         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1075 }
1076
1077 #if 0
1078 // not used
1079 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1080 {
1081         int color, numchars = 0;
1082         char *outputend2c = output2c + maxoutchars - 2;
1083         if (!outcolor || *outcolor == -1)
1084                 color = STRING_COLOR_DEFAULT;
1085         else
1086                 color = *outcolor;
1087         if (!maxreadchars)
1088                 maxreadchars = 1<<30;
1089         textend = text + maxreadchars;
1090         while (text != textend && *text)
1091         {
1092                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1093                 {
1094                         if (text[1] == STRING_COLOR_TAG)
1095                                 text++;
1096                         else if (text[1] >= '0' && text[1] <= '9')
1097                         {
1098                                 color = text[1] - '0';
1099                                 text += 2;
1100                                 continue;
1101                         }
1102                 }
1103                 if (output2c >= outputend2c)
1104                         break;
1105                 *output2c++ = *text++;
1106                 *output2c++ = color;
1107                 numchars++;
1108         }
1109         output2c[0] = output2c[1] = 0;
1110         if (outcolor)
1111                 *outcolor = color;
1112         return numchars;
1113 }
1114 #endif
1115
1116 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)
1117 {
1118         float floats[36];
1119
1120         _DrawQ_ProcessDrawFlag(flags);
1121
1122         R_Mesh_VertexPointer(floats, 0, 0);
1123         R_Mesh_ColorPointer(floats + 20, 0, 0);
1124         R_Mesh_ResetTextureState();
1125         if (pic)
1126         {
1127                 if (width == 0)
1128                         width = pic->width;
1129                 if (height == 0)
1130                         height = pic->height;
1131                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1132                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1133                 floats[12] = s1;floats[13] = t1;
1134                 floats[14] = s2;floats[15] = t2;
1135                 floats[16] = s4;floats[17] = t4;
1136                 floats[18] = s3;floats[19] = t3;
1137         }
1138
1139         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1140         floats[0] = floats[9] = x;
1141         floats[1] = floats[4] = y;
1142         floats[3] = floats[6] = x + width;
1143         floats[7] = floats[10] = y + height;
1144         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1145         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1146         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1147         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1148
1149         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1150 }
1151
1152 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1153 {
1154         _DrawQ_ProcessDrawFlag(flags);
1155
1156         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1157         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1158         R_Mesh_ResetTextureState();
1159         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1160         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1161
1162         GL_LockArrays(0, mesh->num_vertices);
1163         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1164         GL_LockArrays(0, 0);
1165 }
1166
1167 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1168 {
1169         int num;
1170
1171         _DrawQ_ProcessDrawFlag(flags);
1172
1173         GL_Color(1,1,1,1);
1174         CHECKGLERROR
1175         qglBegin(GL_LINE_LOOP);
1176         for (num = 0;num < mesh->num_vertices;num++)
1177         {
1178                 if (mesh->data_color4f)
1179                         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]);
1180                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1181         }
1182         qglEnd();
1183         CHECKGLERROR
1184 }
1185
1186 //[515]: this is old, delete
1187 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1188 {
1189         _DrawQ_ProcessDrawFlag(flags);
1190
1191         CHECKGLERROR
1192         qglLineWidth(width);CHECKGLERROR
1193
1194         GL_Color(r,g,b,alpha);
1195         CHECKGLERROR
1196         qglBegin(GL_LINES);
1197         qglVertex2f(x1, y1);
1198         qglVertex2f(x2, y2);
1199         qglEnd();
1200         CHECKGLERROR
1201 }
1202
1203 void DrawQ_SetClipArea(float x, float y, float width, float height)
1204 {
1205         _DrawQ_Setup();
1206
1207         // We have to convert the con coords into real coords
1208         // OGL uses top to bottom
1209         GL_Scissor((int)(x * ((float)vid.width / vid_conwidth.integer)), (int)(y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
1210
1211         GL_ScissorTest(true);
1212 }
1213
1214 void DrawQ_ResetClipArea(void)
1215 {
1216         _DrawQ_Setup();
1217         GL_ScissorTest(false);
1218 }
1219
1220 void DrawQ_Finish(void)
1221 {
1222         r_refdef.draw2dstage = false;
1223 }
1224
1225 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1226 void R_DrawGamma(void)
1227 {
1228         float c[4];
1229         if (!vid_usinghwgamma)
1230         {
1231                 // all the blends ignore depth
1232                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1233                 R_Mesh_ColorPointer(NULL, 0, 0);
1234                 R_Mesh_ResetTextureState();
1235                 GL_DepthMask(true);
1236                 GL_DepthRange(0, 1);
1237                 GL_PolygonOffset(0, 0);
1238                 GL_DepthTest(false);
1239                 if (v_color_enable.integer)
1240                 {
1241                         c[0] = v_color_white_r.value;
1242                         c[1] = v_color_white_g.value;
1243                         c[2] = v_color_white_b.value;
1244                 }
1245                 else
1246                         c[0] = c[1] = c[2] = v_contrast.value;
1247                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1248                 {
1249                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1250                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1251                         {
1252                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1253                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1254                                 VectorScale(c, 0.5, c);
1255                         }
1256                 }
1257                 if (v_color_enable.integer)
1258                 {
1259                         c[0] = v_color_black_r.value;
1260                         c[1] = v_color_black_g.value;
1261                         c[2] = v_color_black_b.value;
1262                 }
1263                 else
1264                         c[0] = c[1] = c[2] = v_brightness.value;
1265                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1266                 {
1267                         GL_BlendFunc(GL_ONE, GL_ONE);
1268                         GL_Color(c[0], c[1], c[2], 1);
1269                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1270                 }
1271         }
1272 }
1273