Fixed user font initialization writing beyond fonts array bounds.
[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 static float DrawQ_String_Font_UntilX(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, float maxx)
886 {
887         int num, shadow, colorindex = STRING_COLOR_DEFAULT;
888         size_t i;
889         float x = startx, y, s, t, u, v;
890         float *av, *at, *ac;
891         float color[4];
892         int batchcount;
893         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
894         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
895         float color4f[QUADELEMENTS_MAXQUADS*4*4];
896         qboolean checkwidth;
897
898         if (*maxlen < 1)
899                 *maxlen = 1<<30;
900
901         // when basealpha == 0, skip as much as possible (just return width)
902         if(basealpha > 0)
903         {
904                 _DrawQ_ProcessDrawFlag(flags);
905
906                 R_Mesh_ColorPointer(color4f, 0, 0);
907                 R_Mesh_ResetTextureState();
908                 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
909                 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
910                 R_Mesh_VertexPointer(vertex3f, 0, 0);
911         }
912
913         ac = color4f;
914         at = texcoord2f;
915         av = vertex3f;
916         batchcount = 0;
917         checkwidth = (maxx >= startx);
918
919         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
920         {
921                 if (!outcolor || *outcolor == -1)
922                         colorindex = STRING_COLOR_DEFAULT;
923                 else
924                         colorindex = *outcolor;
925                 if(basealpha > 0)
926                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
927
928                 x = startx;
929                 y = starty;
930                 if (shadow)
931                 {
932                         x += r_textshadow.value;
933                         y += r_textshadow.value;
934                 }
935                 for (i = 0;i < *maxlen && text[i];i++)
936                 {
937                         if (text[i] == ' ')
938                         {
939                                 if(checkwidth)
940                                         if(x + fnt->width_of[' '] * w > maxx)
941                                                 break; // oops, can't draw this
942                                 x += fnt->width_of[' '] * w;
943                                 continue;
944                         }
945                         if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
946                         {
947                                 if (text[i+1] == STRING_COLOR_TAG)
948                                 {
949                                         i++;
950                                 }
951                                 else if (text[i+1] >= '0' && text[i+1] <= '9')
952                                 {
953                                         colorindex = text[i+1] - '0';
954                                         DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
955                                         i++;
956                                         continue;
957                                 }
958                         }
959                         num = (unsigned char) text[i];
960                         if(checkwidth)
961                                 if(x + fnt->width_of[num] * w > maxx)
962                                         break; // oops, can't draw this
963                         if(basealpha > 0)
964                         {
965                                 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
966                                 s = (num & 15)*0.0625f + (0.5f / 256.0f);
967                                 t = (num >> 4)*0.0625f + (0.5f / 256.0f);
968                                 u = 0.0625f - (1.0f / 256.0f);
969                                 v = 0.0625f - (1.0f / 256.0f);
970                                 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
971                                 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
972                                 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
973                                 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
974                                 at[ 0] = s  ;at[ 1] = t  ;
975                                 at[ 2] = s+u;at[ 3] = t  ;
976                                 at[ 4] = s+u;at[ 5] = t+v;
977                                 at[ 6] = s  ;at[ 7] = t+v;
978                                 av[ 0] = x  ;av[ 1] = y  ;av[ 2] = 10;
979                                 av[ 3] = x+w;av[ 4] = y  ;av[ 5] = 10;
980                                 av[ 6] = x+w;av[ 7] = y+h;av[ 8] = 10;
981                                 av[ 9] = x  ;av[10] = y+h;av[11] = 10;
982                                 ac += 16;
983                                 at += 8;
984                                 av += 12;
985                                 batchcount++;
986                                 if (batchcount >= QUADELEMENTS_MAXQUADS)
987                                 {
988                                         if (basealpha >= (1.0f / 255.0f))
989                                         {
990                                                 GL_LockArrays(0, batchcount * 4);
991                                                 R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
992                                                 GL_LockArrays(0, 0);
993                                         }
994                                         batchcount = 0;
995                                         ac = color4f;
996                                         at = texcoord2f;
997                                         av = vertex3f;
998                                 }
999                         }
1000                         x += fnt->width_of[num] * w;
1001                 }
1002                 if(checkwidth)
1003                         *maxlen = i;
1004                 checkwidth = 0; // we've done all we had to
1005         }
1006         if (basealpha > 0)
1007         {
1008                 if (batchcount > 0)
1009                 {
1010                         GL_LockArrays(0, batchcount * 4);
1011                         R_Mesh_Draw(0, batchcount * 4, batchcount * 2, quadelements, 0, 0);
1012                         GL_LockArrays(0, 0);
1013                 }
1014         }
1015
1016         if (outcolor)
1017                 *outcolor = colorindex;
1018
1019         // note: this relies on the proper text (not shadow) being drawn last
1020         return x;
1021 }
1022
1023 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)
1024 {
1025         return DrawQ_String_Font_UntilX(startx, starty, text, &maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt, startx-1);
1026 }
1027
1028 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)
1029 {
1030         return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1031 }
1032
1033 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1034 {
1035         return DrawQ_String_Font_UntilX(0, 0, text, maxlen, scalex, scaley, 1, 1, 1, 0, 0, NULL, ignorecolorcodes, fnt, maxWidth);
1036 }
1037
1038 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, float scalex, float scaley, qboolean ignorecolorcodes, const dp_font_t *fnt)
1039 {
1040         return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, scalex, scaley, ignorecolorcodes, fnt, -1);
1041 }
1042
1043 #if 0
1044 // not used
1045 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1046 {
1047         int color, numchars = 0;
1048         char *outputend2c = output2c + maxoutchars - 2;
1049         if (!outcolor || *outcolor == -1)
1050                 color = STRING_COLOR_DEFAULT;
1051         else
1052                 color = *outcolor;
1053         if (!maxreadchars)
1054                 maxreadchars = 1<<30;
1055         textend = text + maxreadchars;
1056         while (text != textend && *text)
1057         {
1058                 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1059                 {
1060                         if (text[1] == STRING_COLOR_TAG)
1061                                 text++;
1062                         else if (text[1] >= '0' && text[1] <= '9')
1063                         {
1064                                 color = text[1] - '0';
1065                                 text += 2;
1066                                 continue;
1067                         }
1068                 }
1069                 if (output2c >= outputend2c)
1070                         break;
1071                 *output2c++ = *text++;
1072                 *output2c++ = color;
1073                 numchars++;
1074         }
1075         output2c[0] = output2c[1] = 0;
1076         if (outcolor)
1077                 *outcolor = color;
1078         return numchars;
1079 }
1080 #endif
1081
1082 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)
1083 {
1084         float floats[36];
1085
1086         _DrawQ_ProcessDrawFlag(flags);
1087
1088         R_Mesh_VertexPointer(floats, 0, 0);
1089         R_Mesh_ColorPointer(floats + 20, 0, 0);
1090         R_Mesh_ResetTextureState();
1091         if (pic)
1092         {
1093                 if (width == 0)
1094                         width = pic->width;
1095                 if (height == 0)
1096                         height = pic->height;
1097                 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1098                 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1099                 floats[12] = s1;floats[13] = t1;
1100                 floats[14] = s2;floats[15] = t2;
1101                 floats[16] = s4;floats[17] = t4;
1102                 floats[18] = s3;floats[19] = t3;
1103         }
1104
1105         floats[2] = floats[5] = floats[8] = floats[11] = 0;
1106         floats[0] = floats[9] = x;
1107         floats[1] = floats[4] = y;
1108         floats[3] = floats[6] = x + width;
1109         floats[7] = floats[10] = y + height;
1110         floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1111         floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1112         floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1113         floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1114
1115         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
1116 }
1117
1118 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1119 {
1120         _DrawQ_ProcessDrawFlag(flags);
1121
1122         R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1123         R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1124         R_Mesh_ResetTextureState();
1125         R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1126         R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1127
1128         GL_LockArrays(0, mesh->num_vertices);
1129         R_Mesh_Draw(0, mesh->num_vertices, mesh->num_triangles, mesh->data_element3i, 0, 0);
1130         GL_LockArrays(0, 0);
1131 }
1132
1133 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1134 {
1135         int num;
1136
1137         _DrawQ_ProcessDrawFlag(flags);
1138
1139         GL_Color(1,1,1,1);
1140         CHECKGLERROR
1141         qglBegin(GL_LINE_LOOP);
1142         for (num = 0;num < mesh->num_vertices;num++)
1143         {
1144                 if (mesh->data_color4f)
1145                         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]);
1146                 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1147         }
1148         qglEnd();
1149         CHECKGLERROR
1150 }
1151
1152 //[515]: this is old, delete
1153 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1154 {
1155         _DrawQ_ProcessDrawFlag(flags);
1156
1157         CHECKGLERROR
1158         qglLineWidth(width);CHECKGLERROR
1159
1160         GL_Color(r,g,b,alpha);
1161         CHECKGLERROR
1162         qglBegin(GL_LINES);
1163         qglVertex2f(x1, y1);
1164         qglVertex2f(x2, y2);
1165         qglEnd();
1166         CHECKGLERROR
1167 }
1168
1169 void DrawQ_SetClipArea(float x, float y, float width, float height)
1170 {
1171         _DrawQ_Setup();
1172
1173         // We have to convert the con coords into real coords
1174         // OGL uses top to bottom
1175         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)));
1176
1177         GL_ScissorTest(true);
1178 }
1179
1180 void DrawQ_ResetClipArea(void)
1181 {
1182         _DrawQ_Setup();
1183         GL_ScissorTest(false);
1184 }
1185
1186 void DrawQ_Finish(void)
1187 {
1188         r_refdef.draw2dstage = false;
1189 }
1190
1191 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1192 void R_DrawGamma(void)
1193 {
1194         float c[4];
1195         if (!vid_usinghwgamma)
1196         {
1197                 // all the blends ignore depth
1198                 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1199                 R_Mesh_ColorPointer(NULL, 0, 0);
1200                 R_Mesh_ResetTextureState();
1201                 GL_DepthMask(true);
1202                 GL_DepthRange(0, 1);
1203                 GL_PolygonOffset(0, 0);
1204                 GL_DepthTest(false);
1205                 if (v_color_enable.integer)
1206                 {
1207                         c[0] = v_color_white_r.value;
1208                         c[1] = v_color_white_g.value;
1209                         c[2] = v_color_white_b.value;
1210                 }
1211                 else
1212                         c[0] = c[1] = c[2] = v_contrast.value;
1213                 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1214                 {
1215                         GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1216                         while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1217                         {
1218                                 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1219                                 R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1220                                 VectorScale(c, 0.5, c);
1221                         }
1222                 }
1223                 if (v_color_enable.integer)
1224                 {
1225                         c[0] = v_color_black_r.value;
1226                         c[1] = v_color_black_g.value;
1227                         c[2] = v_color_black_b.value;
1228                 }
1229                 else
1230                         c[0] = c[1] = c[2] = v_brightness.value;
1231                 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1232                 {
1233                         GL_BlendFunc(GL_ONE, GL_ONE);
1234                         GL_Color(c[0], c[1], c[2], 1);
1235                         R_Mesh_Draw(0, 3, 1, polygonelements, 0, 0);
1236                 }
1237         }
1238 }
1239