2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 static rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
315 unsigned char *pixels;
318 unsigned char *lmpdata;
319 char lmpname[MAX_QPATH];
321 // check whether the picture has already been cached
322 crc = CRC_Block((unsigned char *)path, strlen(path));
323 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
324 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
325 if (!strcmp (path, pic->name))
328 if (numcachepics == MAX_CACHED_PICS)
330 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
331 // FIXME: support NULL in callers?
332 return cachepics; // return the first one
334 pic = cachepics + (numcachepics++);
335 strlcpy (pic->name, path, sizeof(pic->name));
337 pic->chain = cachepichash[hashkey];
338 cachepichash[hashkey] = pic;
340 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
341 pic->tex = CL_GetDynTexture( path );
342 // if so, set the width/height, too
344 pic->width = R_TextureWidth(pic->tex);
345 pic->height = R_TextureHeight(pic->tex);
346 // we're done now (early-out)
350 pic->texflags = TEXF_ALPHA;
351 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
352 pic->texflags |= TEXF_CLAMP;
353 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
354 pic->texflags |= TEXF_COMPRESS;
356 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
358 // load a high quality image from disk if possible
359 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer);
360 if (pixels == NULL && !strncmp(path, "gfx/", 4))
361 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer);
364 pic->width = image_width;
365 pic->height = image_height;
367 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
371 pic->autoload = false;
372 // never compress the fallback images
373 pic->texflags &= ~TEXF_COMPRESS;
376 // now read the low quality version (wad or lmp file), and take the pic
377 // size from that even if we don't upload the texture, this way the pics
378 // show up the right size in the menu even if they were replaced with
379 // higher or lower resolution versions
380 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
381 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
383 if (developer_loading.integer)
384 Con_Printf("loading lump \"%s\"\n", path);
388 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
389 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
390 // if no high quality replacement image was found, upload the original low quality texture
392 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
396 else if ((lmpdata = W_GetLumpName (path + 4)))
398 if (developer_loading.integer)
399 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
401 if (!strcmp(path, "gfx/conchars"))
403 // conchars is a raw image and with color 0 as transparent instead of 255
406 // if no high quality replacement image was found, upload the original low quality texture
408 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
412 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
413 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
414 // if no high quality replacement image was found, upload the original low quality texture
416 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
425 else if (pic->tex == NULL)
427 // if it's not found on disk, generate an image
428 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
429 pic->width = R_TextureWidth(pic->tex);
430 pic->height = R_TextureHeight(pic->tex);
436 cachepic_t *Draw_CachePic (const char *path)
438 return Draw_CachePic_Flags (path, 0);
443 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
445 if (pic->autoload && !pic->tex)
447 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
448 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
449 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
450 if (pic->tex == NULL)
451 pic->tex = draw_generatepic(pic->name, true);
453 pic->lastusedframe = draw_frame;
457 void Draw_Frame(void)
461 static double nextpurgetime;
462 if (nextpurgetime > realtime)
464 nextpurgetime = realtime + 0.05;
465 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
467 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
469 R_FreeTexture(pic->tex);
476 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
481 crc = CRC_Block((unsigned char *)picname, strlen(picname));
482 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
483 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
484 if (!strcmp (picname, pic->name))
489 if (pic->tex && pic->width == width && pic->height == height)
491 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
499 if (numcachepics == MAX_CACHED_PICS)
501 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
502 // FIXME: support NULL in callers?
503 return cachepics; // return the first one
505 pic = cachepics + (numcachepics++);
506 strlcpy (pic->name, picname, sizeof(pic->name));
508 pic->chain = cachepichash[hashkey];
509 cachepichash[hashkey] = pic;
514 pic->height = height;
516 R_FreeTexture(pic->tex);
517 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
521 void Draw_FreePic(const char *picname)
526 // this doesn't really free the pic, but does free it's texture
527 crc = CRC_Block((unsigned char *)picname, strlen(picname));
528 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
529 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
531 if (!strcmp (picname, pic->name) && pic->tex)
533 R_FreeTexture(pic->tex);
542 static float snap_to_pixel_x(float x, float roundUpAt);
543 extern int con_linewidth; // to force rewrapping
544 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
548 char widthfile[MAX_QPATH];
550 fs_offset_t widthbufsize;
552 if(override || !fnt->texpath[0])
554 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
556 // load the cvars when the font is FIRST loaded
557 fnt->settings.antialias = r_font_antialias.integer;
558 fnt->settings.hinting = r_font_hinting.integer;
559 fnt->settings.outline = r_font_postprocess_outline.value;
560 fnt->settings.blur = r_font_postprocess_blur.value;
561 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
562 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
563 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
566 if(drawtexturepool == NULL)
567 return; // before gl_draw_start, so will be loaded later
571 // clear freetype font
572 Font_UnloadFont(fnt->ft2);
577 if(fnt->req_face != -1)
579 if(!Font_LoadFont(fnt->texpath, fnt))
580 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
583 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
584 if(fnt->tex == r_texture_notexture)
586 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
588 if (!fnt->fallbacks[i][0])
590 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
591 if(fnt->tex != r_texture_notexture)
594 if(fnt->tex == r_texture_notexture)
596 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
597 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
600 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
603 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
605 // unspecified width == 1 (base width)
606 for(i = 1; i < 256; ++i)
607 fnt->width_of[i] = 1;
609 // FIXME load "name.width", if it fails, fill all with 1
610 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
612 float extraspacing = 0;
613 const char *p = widthbuf;
618 if(!COM_ParseToken_Simple(&p, false, false))
636 fnt->width_of[ch] = atof(com_token) + extraspacing;
639 for (i = 0; i < MAX_FONT_SIZES; ++i)
641 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
642 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
645 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
651 if(!strcmp(com_token, "extraspacing"))
653 if(!COM_ParseToken_Simple(&p, false, false))
655 extraspacing = atof(com_token);
657 else if(!strcmp(com_token, "scale"))
659 if(!COM_ParseToken_Simple(&p, false, false))
661 scale = atof(com_token);
665 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
666 if(!COM_ParseToken_Simple(&p, false, false))
676 maxwidth = fnt->width_of[1];
677 for(i = 2; i < 256; ++i)
678 maxwidth = max(maxwidth, fnt->width_of[i]);
679 fnt->maxwidth = maxwidth;
681 // fix up maxwidth for overlap
682 fnt->maxwidth *= scale;
684 fnt->voffset = voffset;
686 if(fnt == FONT_CONSOLE)
687 con_linewidth = -1; // rewrap console in next frame
690 extern cvar_t developer_font;
691 dp_font_t *FindFont(const char *title, qboolean allocate_new)
696 for(i = 0; i < dp_fonts.maxsize; ++i)
697 if(!strcmp(dp_fonts.f[i].title, title))
698 return &dp_fonts.f[i];
699 // if not found - try allocate
702 // find any font with empty title
703 for(i = 0; i < dp_fonts.maxsize; ++i)
705 if(!strcmp(dp_fonts.f[i].title, ""))
707 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
708 return &dp_fonts.f[i];
711 // if no any 'free' fonts - expand buffer
712 i = dp_fonts.maxsize;
713 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
714 if (developer_font.integer)
715 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
716 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
717 // register a font in first expanded slot
718 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
719 return &dp_fonts.f[i];
724 static float snap_to_pixel_x(float x, float roundUpAt)
726 float pixelpos = x * vid.width / vid_conwidth.value;
727 int snap = (int) pixelpos;
728 if (pixelpos - snap >= roundUpAt) ++snap;
729 return ((float)snap * vid_conwidth.value / vid.width);
731 x = (int)(x * vid.width / vid_conwidth.value);
732 x = (x * vid_conwidth.value / vid.width);
737 static float snap_to_pixel_y(float y, float roundUpAt)
739 float pixelpos = y * vid.height / vid_conheight.value;
740 int snap = (int) pixelpos;
741 if (pixelpos - snap > roundUpAt) ++snap;
742 return ((float)snap * vid_conheight.value / vid.height);
744 y = (int)(y * vid.height / vid_conheight.value);
745 y = (y * vid_conheight.value / vid.height);
750 static void LoadFont_f(void)
754 const char *filelist, *c, *cm;
756 char mainfont[MAX_QPATH];
760 Con_Printf("Available font commands:\n");
761 for(i = 0; i < dp_fonts.maxsize; ++i)
762 if (dp_fonts.f[i].title[0])
763 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts.f[i].title);
764 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
765 "can specify multiple fonts and faces\n"
766 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
767 "to load face 2 of the font gfx/vera-sans and use face 1\n"
768 "of gfx/fallback as fallback font.\n"
769 "You can also specify a list of font sizes to load, like this:\n"
770 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
771 "In many cases, 8 12 16 24 32 should be a good choice.\n"
775 f = FindFont(Cmd_Argv(1), true);
778 Con_Printf("font function not found\n");
783 filelist = "gfx/conchars";
785 filelist = Cmd_Argv(2);
787 memset(f->fallbacks, 0, sizeof(f->fallbacks));
788 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
790 // first font is handled "normally"
791 c = strchr(filelist, ':');
792 cm = strchr(filelist, ',');
793 if(c && (!cm || c < cm))
794 f->req_face = atoi(c+1);
801 if(!c || (c - filelist) > MAX_QPATH)
802 strlcpy(mainfont, filelist, sizeof(mainfont));
805 memcpy(mainfont, filelist, c - filelist);
806 mainfont[c - filelist] = 0;
809 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
811 c = strchr(filelist, ',');
817 c = strchr(filelist, ':');
818 cm = strchr(filelist, ',');
819 if(c && (!cm || c < cm))
820 f->fallback_faces[i] = atoi(c+1);
823 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
826 if(!c || (c-filelist) > MAX_QPATH)
828 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
832 memcpy(f->fallbacks[i], filelist, c - filelist);
833 f->fallbacks[i][c - filelist] = 0;
837 // for now: by default load only one size: the default size
839 for(i = 1; i < MAX_FONT_SIZES; ++i)
840 f->req_sizes[i] = -1;
844 for(i = 0; i < Cmd_Argc()-3; ++i)
846 sz = atof(Cmd_Argv(i+3));
847 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
848 f->req_sizes[i] = sz;
852 LoadFont(true, mainfont, f, 1, 0);
860 static void gl_draw_start(void)
863 drawtexturepool = R_AllocTexturePool();
866 memset(cachepichash, 0, sizeof(cachepichash));
870 // load default font textures
871 for(i = 0; i < dp_fonts.maxsize; ++i)
872 if (dp_fonts.f[i].title[0])
873 LoadFont(false, va("gfx/font_%s", &dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
875 // draw the loading screen so people have something to see in the newly opened window
876 SCR_UpdateLoadingScreen(true);
879 static void gl_draw_shutdown(void)
883 R_FreeTexturePool(&drawtexturepool);
886 memset(cachepichash, 0, sizeof(cachepichash));
889 static void gl_draw_newmap(void)
894 void GL_Draw_Init (void)
898 Cvar_RegisterVariable(&r_font_postprocess_blur);
899 Cvar_RegisterVariable(&r_font_postprocess_outline);
900 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
901 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
902 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
903 Cvar_RegisterVariable(&r_font_hinting);
904 Cvar_RegisterVariable(&r_font_antialias);
905 Cvar_RegisterVariable(&r_textshadow);
906 Cvar_RegisterVariable(&r_textbrightness);
907 Cvar_RegisterVariable(&r_textcontrast);
909 // allocate fonts storage
910 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
911 dp_fonts.maxsize = MAX_FONTS;
912 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
913 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
915 // assign starting font names
916 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
917 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
918 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
919 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
920 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
921 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
922 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
923 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
924 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
925 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
926 if(!FONT_USER(i)->title[0])
927 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
929 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
930 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
933 void _DrawQ_Setup(void)
935 r_viewport_t viewport;
936 if (r_refdef.draw2dstage)
938 r_refdef.draw2dstage = true;
940 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);
941 R_SetViewport(&viewport);
942 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
943 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
944 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
945 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
946 R_EntityMatrix(&identitymatrix);
950 GL_PolygonOffset(0, 0);
954 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
957 static void _DrawQ_ProcessDrawFlag(int flags)
961 if(flags == DRAWFLAG_ADDITIVE)
962 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
963 else if(flags == DRAWFLAG_MODULATE)
964 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
965 else if(flags == DRAWFLAG_2XMODULATE)
966 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
967 else if(flags == DRAWFLAG_SCREEN)
968 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
970 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
973 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
977 _DrawQ_ProcessDrawFlag(flags);
978 GL_Color(red, green, blue, alpha);
980 R_Mesh_VertexPointer(floats, 0, 0);
981 R_Mesh_ColorPointer(NULL, 0, 0);
982 R_Mesh_ResetTextureState();
988 height = pic->height;
989 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
990 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
993 floats[12] = 0.0f;floats[13] = 0.0f;
994 floats[14] = 1.0f;floats[15] = 0.0f;
995 floats[16] = 1.0f;floats[17] = 1.0f;
996 floats[18] = 0.0f;floats[19] = 1.0f;
998 // AK07: lets be texel correct on the corners
1000 float horz_offset = 0.5f / pic->width;
1001 float vert_offset = 0.5f / pic->height;
1003 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1004 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1005 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1006 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1011 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1013 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1014 floats[0] = floats[9] = x;
1015 floats[1] = floats[4] = y;
1016 floats[3] = floats[6] = x + width;
1017 floats[7] = floats[10] = y + height;
1019 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1022 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)
1025 float af = DEG2RAD(-angle); // forward
1026 float ar = DEG2RAD(-angle + 90); // right
1027 float sinaf = sin(af);
1028 float cosaf = cos(af);
1029 float sinar = sin(ar);
1030 float cosar = cos(ar);
1032 _DrawQ_ProcessDrawFlag(flags);
1033 GL_Color(red, green, blue, alpha);
1035 R_Mesh_VertexPointer(floats, 0, 0);
1036 R_Mesh_ColorPointer(NULL, 0, 0);
1037 R_Mesh_ResetTextureState();
1043 height = pic->height;
1044 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1045 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1047 floats[12] = 0.0f;floats[13] = 0.0f;
1048 floats[14] = 1.0f;floats[15] = 0.0f;
1049 floats[16] = 1.0f;floats[17] = 1.0f;
1050 floats[18] = 0.0f;floats[19] = 1.0f;
1053 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1055 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1058 floats[0] = x - cosaf*org_x - cosar*org_y;
1059 floats[1] = y - sinaf*org_x - sinar*org_y;
1062 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1063 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1066 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1067 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1070 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1071 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1073 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1076 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1080 _DrawQ_ProcessDrawFlag(flags);
1081 GL_Color(red, green, blue, alpha);
1083 R_Mesh_VertexPointer(floats, 0, 0);
1084 R_Mesh_ColorPointer(NULL, 0, 0);
1085 R_Mesh_ResetTextureState();
1086 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1088 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1089 floats[0] = floats[9] = x;
1090 floats[1] = floats[4] = y;
1091 floats[3] = floats[6] = x + width;
1092 floats[7] = floats[10] = y + height;
1094 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1097 /// color tag printing
1098 static const vec4_t string_colors[] =
1101 // LordHavoc: why on earth is cyan before magenta in Quake3?
1102 // LordHavoc: note: Doom3 uses white for [0] and [7]
1103 {0.0, 0.0, 0.0, 1.0}, // black
1104 {1.0, 0.0, 0.0, 1.0}, // red
1105 {0.0, 1.0, 0.0, 1.0}, // green
1106 {1.0, 1.0, 0.0, 1.0}, // yellow
1107 {0.0, 0.0, 1.0, 1.0}, // blue
1108 {0.0, 1.0, 1.0, 1.0}, // cyan
1109 {1.0, 0.0, 1.0, 1.0}, // magenta
1110 {1.0, 1.0, 1.0, 1.0}, // white
1111 // [515]'s BX_COLOREDTEXT extension
1112 {1.0, 1.0, 1.0, 0.5}, // half transparent
1113 {0.5, 0.5, 0.5, 1.0} // half brightness
1114 // Black's color table
1115 //{1.0, 1.0, 1.0, 1.0},
1116 //{1.0, 0.0, 0.0, 1.0},
1117 //{0.0, 1.0, 0.0, 1.0},
1118 //{0.0, 0.0, 1.0, 1.0},
1119 //{1.0, 1.0, 0.0, 1.0},
1120 //{0.0, 1.0, 1.0, 1.0},
1121 //{1.0, 0.0, 1.0, 1.0},
1122 //{0.1, 0.1, 0.1, 1.0}
1125 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1127 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1129 float C = r_textcontrast.value;
1130 float B = r_textbrightness.value;
1131 if (colorindex & 0x10000) // that bit means RGB color
1133 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1134 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1135 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1136 color[3] = (colorindex & 0xf) / 15.0;
1139 Vector4Copy(string_colors[colorindex], color);
1140 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1143 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1144 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1148 // NOTE: this function always draws exactly one character if maxwidth <= 0
1149 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1151 const char *text_start = text;
1152 int colorindex = STRING_COLOR_DEFAULT;
1155 Uchar ch, mapch, nextch;
1156 Uchar prevch = 0; // used for kerning
1161 ft2_font_map_t *fontmap = NULL;
1162 ft2_font_map_t *map = NULL;
1163 //ft2_font_map_t *prevmap = NULL;
1164 ft2_font_t *ft2 = fnt->ft2;
1166 qboolean snap = true;
1167 qboolean least_one = false;
1168 float dw; // display w
1169 //float dh; // display h
1170 const float *width_of;
1177 // do this in the end
1181 // find the most fitting size:
1185 map_index = Font_IndexForSize(ft2, h, &w, &h);
1187 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1188 fontmap = Font_MapForIndex(ft2, map_index);
1197 if (!outcolor || *outcolor == -1)
1198 colorindex = STRING_COLOR_DEFAULT;
1200 colorindex = *outcolor;
1202 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1203 // ftbase_x = snap_to_pixel_x(0);
1208 maxwidth = -maxwidth;
1212 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1215 width_of = fontmap->width_of;
1217 width_of = fnt->width_of;
1219 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1222 nextch = ch = u8_getnchar(text, &text, bytes_left);
1223 i = text - text_start;
1226 if (ch == ' ' && !fontmap)
1228 if(!least_one || i0) // never skip the first character
1229 if(x + width_of[(int) ' '] * dw > maxwidth)
1232 break; // oops, can't draw this
1234 x += width_of[(int) ' '] * dw;
1237 // i points to the char after ^
1238 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1240 ch = *text; // colors are ascii, so no u8_ needed
1241 if (ch <= '9' && ch >= '0') // ^[0-9] found
1243 colorindex = ch - '0';
1248 // i points to the char after ^...
1249 // i+3 points to 3 in ^x123
1250 // i+3 == *maxlen would mean that char is missing
1251 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1253 // building colorindex...
1254 ch = tolower(text[1]);
1255 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1256 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1257 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1258 else tempcolorindex = 0;
1261 ch = tolower(text[2]);
1262 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1263 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1264 else tempcolorindex = 0;
1267 ch = tolower(text[3]);
1268 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1269 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1270 else tempcolorindex = 0;
1273 colorindex = tempcolorindex | 0xf;
1274 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1282 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1291 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1298 map = ft2_oldstyle_map;
1300 if(!least_one || i0) // never skip the first character
1301 if(x + width_of[ch] * dw > maxwidth)
1304 break; // oops, can't draw this
1306 x += width_of[ch] * dw;
1308 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1310 map = FontMap_FindForChar(fontmap, ch);
1313 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1319 mapch = ch - map->start;
1320 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1322 x += map->glyphs[mapch].advance_x * dw;
1331 *outcolor = colorindex;
1336 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1338 int shadow, colorindex = STRING_COLOR_DEFAULT;
1340 float x = startx, y, s, t, u, v, thisw;
1341 float *av, *at, *ac;
1344 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1345 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1346 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1347 Uchar ch, mapch, nextch;
1348 Uchar prevch = 0; // used for kerning
1351 //ft2_font_map_t *prevmap = NULL; // the previous map
1352 ft2_font_map_t *map = NULL; // the currently used map
1353 ft2_font_map_t *fontmap = NULL; // the font map for the size
1355 const char *text_start = text;
1357 ft2_font_t *ft2 = fnt->ft2;
1358 qboolean snap = true;
1362 const float *width_of;
1365 tw = R_TextureWidth(fnt->tex);
1366 th = R_TextureHeight(fnt->tex);
1374 starty -= (fnt->scale - 1) * h * 0.5 - fnt->voffset*h; // center & offset
1381 map_index = Font_IndexForSize(ft2, h, &w, &h);
1383 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1384 fontmap = Font_MapForIndex(ft2, map_index);
1390 // draw the font at its baseline when using freetype
1392 ftbase_y = dh * (4.5/6.0);
1397 _DrawQ_ProcessDrawFlag(flags);
1399 R_Mesh_ColorPointer(color4f, 0, 0);
1400 R_Mesh_ResetTextureState();
1402 R_Mesh_TexBind(0, fnt->tex);
1403 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1404 R_Mesh_VertexPointer(vertex3f, 0, 0);
1405 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1412 //ftbase_x = snap_to_pixel_x(ftbase_x);
1415 startx = snap_to_pixel_x(startx, 0.4);
1416 starty = snap_to_pixel_y(starty, 0.4);
1417 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1420 pix_x = vid.width / vid_conwidth.value;
1421 pix_y = vid.height / vid_conheight.value;
1424 width_of = fontmap->width_of;
1426 width_of = fnt->width_of;
1428 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1433 if (!outcolor || *outcolor == -1)
1434 colorindex = STRING_COLOR_DEFAULT;
1436 colorindex = *outcolor;
1438 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1445 x += r_textshadow.value * vid.width / vid_conwidth.value;
1446 y += r_textshadow.value * vid.height / vid_conheight.value;
1449 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1451 nextch = ch = u8_getnchar(text, &text, bytes_left);
1452 i = text - text_start;
1455 if (ch == ' ' && !fontmap)
1457 x += width_of[(int) ' '] * dw;
1460 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1462 ch = *text; // colors are ascii, so no u8_ needed
1463 if (ch <= '9' && ch >= '0') // ^[0-9] found
1465 colorindex = ch - '0';
1466 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1471 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1473 // building colorindex...
1474 ch = tolower(text[1]);
1475 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1476 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1477 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1478 else tempcolorindex = 0;
1481 ch = tolower(text[2]);
1482 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1483 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1484 else tempcolorindex = 0;
1487 ch = tolower(text[3]);
1488 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1489 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1490 else tempcolorindex = 0;
1493 colorindex = tempcolorindex | 0xf;
1494 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1495 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1496 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1504 else if (ch == STRING_COLOR_TAG)
1513 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1514 // this way we don't need to rebind fnt->tex for every old-style character
1515 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1518 x += 1.0/pix_x * r_textshadow.value;
1519 y += 1.0/pix_y * r_textshadow.value;
1521 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1529 if (map != ft2_oldstyle_map)
1533 // switching from freetype to non-freetype rendering
1534 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1540 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1541 map = ft2_oldstyle_map;
1545 //num = (unsigned char) text[i];
1546 //thisw = fnt->width_of[num];
1547 thisw = fnt->width_of[ch];
1548 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1549 s = (ch & 15)*0.0625f + (0.5f / tw);
1550 t = (ch >> 4)*0.0625f + (0.5f / th);
1551 u = 0.0625f * thisw - (1.0f / tw);
1552 v = 0.0625f - (1.0f / th);
1553 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1554 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1555 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1556 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1557 at[ 0] = s ; at[ 1] = t ;
1558 at[ 2] = s+u ; at[ 3] = t ;
1559 at[ 4] = s+u ; at[ 5] = t+v ;
1560 at[ 6] = s ; at[ 7] = t+v ;
1561 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1562 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1563 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1564 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1569 if (batchcount >= QUADELEMENTS_MAXQUADS)
1571 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1577 x += width_of[ch] * dw;
1579 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1581 // new charmap - need to render
1584 // we need a different character map, render what we currently have:
1585 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1592 map = FontMap_FindForChar(fontmap, ch);
1595 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1602 // this shouldn't happen
1607 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1610 mapch = ch - map->start;
1611 thisw = map->glyphs[mapch].advance_x;
1615 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1622 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1623 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1624 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1625 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1626 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1627 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1628 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1629 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1630 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1631 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1632 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1633 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1642 if (batchcount >= QUADELEMENTS_MAXQUADS)
1644 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1656 x -= 1.0/pix_x * r_textshadow.value;
1657 y -= 1.0/pix_y * r_textshadow.value;
1662 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1665 *outcolor = colorindex;
1667 // note: this relies on the proper text (not shadow) being drawn last
1671 float DrawQ_String(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1673 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1676 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1678 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1681 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1683 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1686 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1688 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1693 // no ^xrgb management
1694 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1696 int color, numchars = 0;
1697 char *outputend2c = output2c + maxoutchars - 2;
1698 if (!outcolor || *outcolor == -1)
1699 color = STRING_COLOR_DEFAULT;
1703 maxreadchars = 1<<30;
1704 textend = text + maxreadchars;
1705 while (text != textend && *text)
1707 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1709 if (text[1] == STRING_COLOR_TAG)
1711 else if (text[1] >= '0' && text[1] <= '9')
1713 color = text[1] - '0';
1718 if (output2c >= outputend2c)
1720 *output2c++ = *text++;
1721 *output2c++ = color;
1724 output2c[0] = output2c[1] = 0;
1731 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)
1735 _DrawQ_ProcessDrawFlag(flags);
1737 R_Mesh_VertexPointer(floats, 0, 0);
1738 R_Mesh_ColorPointer(floats + 20, 0, 0);
1739 R_Mesh_ResetTextureState();
1745 height = pic->height;
1746 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1747 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1748 floats[12] = s1;floats[13] = t1;
1749 floats[14] = s2;floats[15] = t2;
1750 floats[16] = s4;floats[17] = t4;
1751 floats[18] = s3;floats[19] = t3;
1754 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1756 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1757 floats[0] = floats[9] = x;
1758 floats[1] = floats[4] = y;
1759 floats[3] = floats[6] = x + width;
1760 floats[7] = floats[10] = y + height;
1761 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1762 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1763 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1764 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1766 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1769 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1771 _DrawQ_ProcessDrawFlag(flags);
1773 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1774 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1775 R_Mesh_ResetTextureState();
1776 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1777 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1779 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1782 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1786 _DrawQ_ProcessDrawFlag(flags);
1790 qglBegin(GL_LINE_LOOP);
1791 for (num = 0;num < mesh->num_vertices;num++)
1793 if (mesh->data_color4f)
1794 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]);
1795 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1801 //[515]: this is old, delete
1802 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1804 _DrawQ_ProcessDrawFlag(flags);
1806 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1809 //qglLineWidth(width);CHECKGLERROR
1811 GL_Color(r,g,b,alpha);
1814 qglVertex2f(x1, y1);
1815 qglVertex2f(x2, y2);
1820 void DrawQ_SetClipArea(float x, float y, float width, float height)
1825 // We have to convert the con coords into real coords
1826 // OGL uses top to bottom
1827 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1828 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1829 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1830 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1831 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1833 GL_ScissorTest(true);
1836 void DrawQ_ResetClipArea(void)
1839 GL_ScissorTest(false);
1842 void DrawQ_Finish(void)
1844 r_refdef.draw2dstage = false;
1847 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1848 void R_DrawGamma(void)
1851 switch(vid.renderpath)
1853 case RENDERPATH_GL20:
1854 case RENDERPATH_CGGL:
1855 if (vid_usinghwgamma || v_glslgamma.integer)
1858 case RENDERPATH_GL13:
1859 case RENDERPATH_GL11:
1860 if (vid_usinghwgamma)
1864 // all the blends ignore depth
1865 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1866 R_Mesh_ColorPointer(NULL, 0, 0);
1867 R_Mesh_ResetTextureState();
1868 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1870 GL_DepthRange(0, 1);
1871 GL_PolygonOffset(0, 0);
1872 GL_DepthTest(false);
1873 if (v_color_enable.integer)
1875 c[0] = v_color_white_r.value;
1876 c[1] = v_color_white_g.value;
1877 c[2] = v_color_white_b.value;
1880 c[0] = c[1] = c[2] = v_contrast.value;
1881 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1883 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1884 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1886 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1887 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1888 VectorScale(c, 0.5, c);
1891 if (v_color_enable.integer)
1893 c[0] = v_color_black_r.value;
1894 c[1] = v_color_black_g.value;
1895 c[2] = v_color_black_b.value;
1898 c[0] = c[1] = c[2] = v_brightness.value;
1899 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1901 GL_BlendFunc(GL_ONE, GL_ONE);
1902 GL_Color(c[0], c[1], c[2], 1);
1903 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);