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