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 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, NULL);
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, -1, 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, -1, 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, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
316 unsigned char *pixels = NULL;
319 unsigned char *lmpdata;
320 char lmpname[MAX_QPATH];
323 qboolean ddshasalpha;
324 float ddsavgcolor[4];
325 qboolean loaded = false;
327 texflags = TEXF_ALPHA;
328 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329 texflags |= TEXF_CLAMP;
330 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331 texflags |= TEXF_COMPRESS;
333 // check whether the picture has already been cached
334 crc = CRC_Block((unsigned char *)path, strlen(path));
335 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
338 if (!strcmp (path, pic->name))
340 // if it was created (or replaced) by Draw_NewPic, just return it
341 if(pic->flags & CACHEPICFLAG_NEWPIC)
343 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
345 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
348 pic->autoload = false; // persist it
350 goto reload; // load it below, and then persist
357 if (numcachepics == MAX_CACHED_PICS)
359 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360 // FIXME: support NULL in callers?
361 return cachepics; // return the first one
363 pic = cachepics + (numcachepics++);
364 memset(pic, 0, sizeof(*pic));
365 strlcpy (pic->name, path, sizeof(pic->name));
367 pic->chain = cachepichash[hashkey];
368 cachepichash[hashkey] = pic;
371 // TODO why does this crash?
372 if(pic->allow_free_tex && pic->tex)
373 R_PurgeTexture(pic->tex);
375 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
376 pic->flags = cachepicflags;
377 pic->tex = CL_GetDynTexture( path );
378 // if so, set the width/height, too
380 pic->allow_free_tex = false;
381 pic->width = R_TextureWidth(pic->tex);
382 pic->height = R_TextureHeight(pic->tex);
383 // we're done now (early-out)
387 pic->allow_free_tex = true;
389 pic->hasalpha = true; // assume alpha unless we know it has none
390 pic->texflags = texflags;
391 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
392 pic->lastusedframe = draw_frame;
394 // load a high quality image from disk if possible
395 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
397 // note this loads even if autoload is true, otherwise we can't get the width/height
399 pic->hasalpha = ddshasalpha;
400 pic->width = R_TextureWidth(pic->tex);
401 pic->height = R_TextureHeight(pic->tex);
403 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
406 pic->hasalpha = false;
407 if (pic->texflags & TEXF_ALPHA)
409 for (j = 3;j < image_width * image_height * 4;j += 4)
413 pic->hasalpha = true;
419 pic->width = image_width;
420 pic->height = image_height;
423 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, image_width, image_height, pixels, vid.sRGB2D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
425 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
426 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
432 pic->autoload = false;
433 // never compress the fallback images
434 pic->texflags &= ~TEXF_COMPRESS;
437 // now read the low quality version (wad or lmp file), and take the pic
438 // size from that even if we don't upload the texture, this way the pics
439 // show up the right size in the menu even if they were replaced with
440 // higher or lower resolution versions
441 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
442 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
444 if (developer_loading.integer)
445 Con_Printf("loading lump \"%s\"\n", pic->name);
449 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
450 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
451 // if no high quality replacement image was found, upload the original low quality texture
455 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
460 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
462 if (developer_loading.integer)
463 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
465 if (!strcmp(pic->name, "gfx/conchars"))
467 // conchars is a raw image and with color 0 as transparent instead of 255
470 // if no high quality replacement image was found, upload the original low quality texture
474 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
479 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
480 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
481 // if no high quality replacement image was found, upload the original low quality texture
485 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
497 // if it's not found on disk, generate an image
498 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
499 pic->width = R_TextureWidth(pic->tex);
500 pic->height = R_TextureHeight(pic->tex);
501 pic->allow_free_tex = (pic->tex != r_texture_notexture);
507 cachepic_t *Draw_CachePic (const char *path)
509 return Draw_CachePic_Flags (path, 0); // default to persistent!
512 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
514 if (pic->autoload && !pic->tex)
516 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
518 qboolean ddshasalpha;
519 float ddsavgcolor[4];
520 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
522 if (pic->tex == NULL)
524 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
526 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
527 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
530 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
532 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
534 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
535 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
538 if (pic->tex == NULL)
539 pic->tex = draw_generatepic(pic->name, true);
541 pic->lastusedframe = draw_frame;
545 void Draw_Frame(void)
549 static double nextpurgetime;
550 if (nextpurgetime > realtime)
552 nextpurgetime = realtime + 0.05;
553 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
555 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
557 R_FreeTexture(pic->tex);
564 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
569 crc = CRC_Block((unsigned char *)picname, strlen(picname));
570 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
571 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
572 if (!strcmp (picname, pic->name))
577 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
579 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
585 if (numcachepics == MAX_CACHED_PICS)
587 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
588 // FIXME: support NULL in callers?
589 return cachepics; // return the first one
591 pic = cachepics + (numcachepics++);
592 memset(pic, 0, sizeof(*pic));
593 strlcpy (pic->name, picname, sizeof(pic->name));
595 pic->chain = cachepichash[hashkey];
596 cachepichash[hashkey] = pic;
599 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
601 pic->height = height;
602 if (pic->allow_free_tex && pic->tex)
603 R_FreeTexture(pic->tex);
604 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
608 void Draw_FreePic(const char *picname)
613 // this doesn't really free the pic, but does free it's texture
614 crc = CRC_Block((unsigned char *)picname, strlen(picname));
615 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
616 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
618 if (!strcmp (picname, pic->name) && pic->tex)
620 R_FreeTexture(pic->tex);
629 static float snap_to_pixel_x(float x, float roundUpAt);
630 extern int con_linewidth; // to force rewrapping
631 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
635 char widthfile[MAX_QPATH];
637 fs_offset_t widthbufsize;
639 if(override || !fnt->texpath[0])
641 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
642 // load the cvars when the font is FIRST loader
643 fnt->settings.scale = scale;
644 fnt->settings.voffset = voffset;
645 fnt->settings.antialias = r_font_antialias.integer;
646 fnt->settings.hinting = r_font_hinting.integer;
647 fnt->settings.outline = r_font_postprocess_outline.value;
648 fnt->settings.blur = r_font_postprocess_blur.value;
649 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
650 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
651 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
654 if (fnt->settings.scale <= 0)
655 fnt->settings.scale = 1;
657 if(drawtexturepool == NULL)
658 return; // before gl_draw_start, so will be loaded later
662 // clear freetype font
663 Font_UnloadFont(fnt->ft2);
668 if(fnt->req_face != -1)
670 if(!Font_LoadFont(fnt->texpath, fnt))
671 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
674 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675 if(fnt->tex == r_texture_notexture)
677 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
679 if (!fnt->fallbacks[i][0])
681 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
682 if(fnt->tex != r_texture_notexture)
685 if(fnt->tex == r_texture_notexture)
687 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
688 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
691 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
694 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
696 // unspecified width == 1 (base width)
697 for(ch = 0; ch < 256; ++ch)
698 fnt->width_of[ch] = 1;
700 // FIXME load "name.width", if it fails, fill all with 1
701 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
703 float extraspacing = 0;
704 const char *p = widthbuf;
709 if(!COM_ParseToken_Simple(&p, false, false, true))
727 fnt->width_of[ch] = atof(com_token) + extraspacing;
731 if(!strcmp(com_token, "extraspacing"))
733 if(!COM_ParseToken_Simple(&p, false, false, true))
735 extraspacing = atof(com_token);
737 else if(!strcmp(com_token, "scale"))
739 if(!COM_ParseToken_Simple(&p, false, false, true))
741 fnt->settings.scale = atof(com_token);
745 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
746 if(!COM_ParseToken_Simple(&p, false, false, true))
758 for (i = 0; i < MAX_FONT_SIZES; ++i)
760 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
763 for(ch = 0; ch < 256; ++ch)
764 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
768 maxwidth = fnt->width_of[0];
769 for(i = 1; i < 256; ++i)
770 maxwidth = max(maxwidth, fnt->width_of[i]);
771 fnt->maxwidth = maxwidth;
773 // fix up maxwidth for overlap
774 fnt->maxwidth *= fnt->settings.scale;
776 if(fnt == FONT_CONSOLE)
777 con_linewidth = -1; // rewrap console in next frame
780 extern cvar_t developer_font;
781 dp_font_t *FindFont(const char *title, qboolean allocate_new)
786 for(i = 0; i < dp_fonts.maxsize; ++i)
787 if(!strcmp(dp_fonts.f[i].title, title))
788 return &dp_fonts.f[i];
789 // if not found - try allocate
792 // find any font with empty title
793 for(i = 0; i < dp_fonts.maxsize; ++i)
795 if(!strcmp(dp_fonts.f[i].title, ""))
797 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
798 return &dp_fonts.f[i];
801 // if no any 'free' fonts - expand buffer
802 oldsize = dp_fonts.maxsize;
803 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
804 if (developer_font.integer)
805 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
806 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
807 // relink ft2 structures
808 for(i = 0; i < oldsize; ++i)
809 if (dp_fonts.f[i].ft2)
810 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
811 // register a font in first expanded slot
812 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
813 return &dp_fonts.f[oldsize];
818 static float snap_to_pixel_x(float x, float roundUpAt)
820 float pixelpos = x * vid.width / vid_conwidth.value;
821 int snap = (int) pixelpos;
822 if (pixelpos - snap >= roundUpAt) ++snap;
823 return ((float)snap * vid_conwidth.value / vid.width);
825 x = (int)(x * vid.width / vid_conwidth.value);
826 x = (x * vid_conwidth.value / vid.width);
831 static float snap_to_pixel_y(float y, float roundUpAt)
833 float pixelpos = y * vid.height / vid_conheight.value;
834 int snap = (int) pixelpos;
835 if (pixelpos - snap > roundUpAt) ++snap;
836 return ((float)snap * vid_conheight.value / vid.height);
838 y = (int)(y * vid.height / vid_conheight.value);
839 y = (y * vid_conheight.value / vid.height);
844 static void LoadFont_f(void)
848 const char *filelist, *c, *cm;
849 float sz, scale, voffset;
850 char mainfont[MAX_QPATH];
854 Con_Printf("Available font commands:\n");
855 for(i = 0; i < dp_fonts.maxsize; ++i)
856 if (dp_fonts.f[i].title[0])
857 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
858 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
859 "can specify multiple fonts and faces\n"
860 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
861 "to load face 2 of the font gfx/vera-sans and use face 1\n"
862 "of gfx/fallback as fallback font.\n"
863 "You can also specify a list of font sizes to load, like this:\n"
864 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
865 "In many cases, 8 12 16 24 32 should be a good choice.\n"
867 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
868 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
872 f = FindFont(Cmd_Argv(1), true);
875 Con_Printf("font function not found\n");
880 filelist = "gfx/conchars";
882 filelist = Cmd_Argv(2);
884 memset(f->fallbacks, 0, sizeof(f->fallbacks));
885 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
887 // first font is handled "normally"
888 c = strchr(filelist, ':');
889 cm = strchr(filelist, ',');
890 if(c && (!cm || c < cm))
891 f->req_face = atoi(c+1);
898 if(!c || (c - filelist) > MAX_QPATH)
899 strlcpy(mainfont, filelist, sizeof(mainfont));
902 memcpy(mainfont, filelist, c - filelist);
903 mainfont[c - filelist] = 0;
906 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
908 c = strchr(filelist, ',');
914 c = strchr(filelist, ':');
915 cm = strchr(filelist, ',');
916 if(c && (!cm || c < cm))
917 f->fallback_faces[i] = atoi(c+1);
920 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
923 if(!c || (c-filelist) > MAX_QPATH)
925 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
929 memcpy(f->fallbacks[i], filelist, c - filelist);
930 f->fallbacks[i][c - filelist] = 0;
934 // for now: by default load only one size: the default size
936 for(i = 1; i < MAX_FONT_SIZES; ++i)
937 f->req_sizes[i] = -1;
943 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
946 if (!strcmp(Cmd_Argv(i), "scale"))
950 scale = atof(Cmd_Argv(i));
953 if (!strcmp(Cmd_Argv(i), "voffset"))
957 voffset = atof(Cmd_Argv(i));
962 continue; // no slot for other sizes
964 // parse one of sizes
965 sz = atof(Cmd_Argv(i));
966 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
968 // search for duplicated sizes
970 for (j=0; j<sizes; j++)
971 if (f->req_sizes[j] == sz)
974 continue; // sz already in req_sizes, don't add it again
976 if (sizes == MAX_FONT_SIZES)
978 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
982 f->req_sizes[sizes] = sz;
988 LoadFont(true, mainfont, f, scale, voffset);
996 static void gl_draw_start(void)
999 drawtexturepool = R_AllocTexturePool();
1002 memset(cachepichash, 0, sizeof(cachepichash));
1006 // load default font textures
1007 for(i = 0; i < dp_fonts.maxsize; ++i)
1008 if (dp_fonts.f[i].title[0])
1009 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1011 // draw the loading screen so people have something to see in the newly opened window
1012 SCR_UpdateLoadingScreen(true);
1015 static void gl_draw_shutdown(void)
1019 R_FreeTexturePool(&drawtexturepool);
1022 memset(cachepichash, 0, sizeof(cachepichash));
1025 static void gl_draw_newmap(void)
1030 void GL_Draw_Init (void)
1034 Cvar_RegisterVariable(&r_font_postprocess_blur);
1035 Cvar_RegisterVariable(&r_font_postprocess_outline);
1036 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1037 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1038 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1039 Cvar_RegisterVariable(&r_font_hinting);
1040 Cvar_RegisterVariable(&r_font_antialias);
1041 Cvar_RegisterVariable(&r_textshadow);
1042 Cvar_RegisterVariable(&r_textbrightness);
1043 Cvar_RegisterVariable(&r_textcontrast);
1045 // allocate fonts storage
1046 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1047 dp_fonts.maxsize = MAX_FONTS;
1048 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1049 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1051 // assign starting font names
1052 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1053 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1054 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1055 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1056 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1057 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1058 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1059 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1060 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1061 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1062 if(!FONT_USER(i)->title[0])
1063 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1065 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1066 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1069 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1071 if (r_refdef.draw2dstage == 1)
1073 r_refdef.draw2dstage = 1;
1075 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
1078 qboolean r_draw2d_force = false;
1079 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1082 if(!r_draw2d.integer && !r_draw2d_force)
1084 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1086 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1088 if(flags == DRAWFLAG_ADDITIVE)
1090 GL_DepthMask(false);
1091 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1093 else if(flags == DRAWFLAG_MODULATE)
1095 GL_DepthMask(false);
1096 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1098 else if(flags == DRAWFLAG_2XMODULATE)
1100 GL_DepthMask(false);
1101 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1103 else if(flags == DRAWFLAG_SCREEN)
1105 GL_DepthMask(false);
1106 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1110 GL_DepthMask(false);
1111 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1116 GL_BlendFunc(GL_ONE, GL_ZERO);
1120 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1124 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1125 if(!r_draw2d.integer && !r_draw2d_force)
1128 // R_Mesh_ResetTextureState();
1129 floats[12] = 0.0f;floats[13] = 0.0f;
1130 floats[14] = 1.0f;floats[15] = 0.0f;
1131 floats[16] = 1.0f;floats[17] = 1.0f;
1132 floats[18] = 0.0f;floats[19] = 1.0f;
1133 floats[20] = floats[24] = floats[28] = floats[32] = red;
1134 floats[21] = floats[25] = floats[29] = floats[33] = green;
1135 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1136 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1142 height = pic->height;
1143 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1146 // AK07: lets be texel correct on the corners
1148 float horz_offset = 0.5f / pic->width;
1149 float vert_offset = 0.5f / pic->height;
1151 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1152 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1153 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1154 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1159 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1161 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1162 floats[0] = floats[9] = x;
1163 floats[1] = floats[4] = y;
1164 floats[3] = floats[6] = x + width;
1165 floats[7] = floats[10] = y + height;
1167 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1168 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1171 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)
1174 float af = DEG2RAD(-angle); // forward
1175 float ar = DEG2RAD(-angle + 90); // right
1176 float sinaf = sin(af);
1177 float cosaf = cos(af);
1178 float sinar = sin(ar);
1179 float cosar = cos(ar);
1181 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1182 if(!r_draw2d.integer && !r_draw2d_force)
1185 // R_Mesh_ResetTextureState();
1191 height = pic->height;
1192 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1195 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1197 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1200 floats[0] = x - cosaf*org_x - cosar*org_y;
1201 floats[1] = y - sinaf*org_x - sinar*org_y;
1204 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1205 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1208 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1209 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1212 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1213 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1215 floats[12] = 0.0f;floats[13] = 0.0f;
1216 floats[14] = 1.0f;floats[15] = 0.0f;
1217 floats[16] = 1.0f;floats[17] = 1.0f;
1218 floats[18] = 0.0f;floats[19] = 1.0f;
1219 floats[20] = floats[24] = floats[28] = floats[32] = red;
1220 floats[21] = floats[25] = floats[29] = floats[33] = green;
1221 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1222 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1224 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1225 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1228 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1232 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1233 if(!r_draw2d.integer && !r_draw2d_force)
1236 // R_Mesh_ResetTextureState();
1237 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1239 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1240 floats[0] = floats[9] = x;
1241 floats[1] = floats[4] = y;
1242 floats[3] = floats[6] = x + width;
1243 floats[7] = floats[10] = y + height;
1244 floats[12] = 0.0f;floats[13] = 0.0f;
1245 floats[14] = 1.0f;floats[15] = 0.0f;
1246 floats[16] = 1.0f;floats[17] = 1.0f;
1247 floats[18] = 0.0f;floats[19] = 1.0f;
1248 floats[20] = floats[24] = floats[28] = floats[32] = red;
1249 floats[21] = floats[25] = floats[29] = floats[33] = green;
1250 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1251 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1253 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1254 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1257 /// color tag printing
1258 static const vec4_t string_colors[] =
1261 // LordHavoc: why on earth is cyan before magenta in Quake3?
1262 // LordHavoc: note: Doom3 uses white for [0] and [7]
1263 {0.0, 0.0, 0.0, 1.0}, // black
1264 {1.0, 0.0, 0.0, 1.0}, // red
1265 {0.0, 1.0, 0.0, 1.0}, // green
1266 {1.0, 1.0, 0.0, 1.0}, // yellow
1267 {0.0, 0.0, 1.0, 1.0}, // blue
1268 {0.0, 1.0, 1.0, 1.0}, // cyan
1269 {1.0, 0.0, 1.0, 1.0}, // magenta
1270 {1.0, 1.0, 1.0, 1.0}, // white
1271 // [515]'s BX_COLOREDTEXT extension
1272 {1.0, 1.0, 1.0, 0.5}, // half transparent
1273 {0.5, 0.5, 0.5, 1.0} // half brightness
1274 // Black's color table
1275 //{1.0, 1.0, 1.0, 1.0},
1276 //{1.0, 0.0, 0.0, 1.0},
1277 //{0.0, 1.0, 0.0, 1.0},
1278 //{0.0, 0.0, 1.0, 1.0},
1279 //{1.0, 1.0, 0.0, 1.0},
1280 //{0.0, 1.0, 1.0, 1.0},
1281 //{1.0, 0.0, 1.0, 1.0},
1282 //{0.1, 0.1, 0.1, 1.0}
1285 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1287 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1289 float C = r_textcontrast.value;
1290 float B = r_textbrightness.value;
1291 if (colorindex & 0x10000) // that bit means RGB color
1293 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1294 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1295 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1296 color[3] = (colorindex & 0xf) / 15.0;
1299 Vector4Copy(string_colors[colorindex], color);
1300 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1303 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1304 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1308 // NOTE: this function always draws exactly one character if maxwidth <= 0
1309 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)
1311 const char *text_start = text;
1312 int colorindex = STRING_COLOR_DEFAULT;
1315 Uchar ch, mapch, nextch;
1316 Uchar prevch = 0; // used for kerning
1321 ft2_font_map_t *fontmap = NULL;
1322 ft2_font_map_t *map = NULL;
1323 //ft2_font_map_t *prevmap = NULL;
1324 ft2_font_t *ft2 = fnt->ft2;
1326 qboolean snap = true;
1327 qboolean least_one = false;
1328 float dw; // display w
1329 //float dh; // display h
1330 const float *width_of;
1337 // do this in the end
1338 w *= fnt->settings.scale;
1339 h *= fnt->settings.scale;
1341 // find the most fitting size:
1345 map_index = Font_IndexForSize(ft2, h, &w, &h);
1347 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1348 fontmap = Font_MapForIndex(ft2, map_index);
1357 if (!outcolor || *outcolor == -1)
1358 colorindex = STRING_COLOR_DEFAULT;
1360 colorindex = *outcolor;
1362 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1363 // ftbase_x = snap_to_pixel_x(0);
1368 maxwidth = -maxwidth;
1372 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1375 width_of = fontmap->width_of;
1377 width_of = fnt->width_of;
1379 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1382 nextch = ch = u8_getnchar(text, &text, bytes_left);
1383 i = text - text_start;
1386 if (ch == ' ' && !fontmap)
1388 if(!least_one || i0) // never skip the first character
1389 if(x + width_of[(int) ' '] * dw > maxwidth)
1392 break; // oops, can't draw this
1394 x += width_of[(int) ' '] * dw;
1397 // i points to the char after ^
1398 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1400 ch = *text; // colors are ascii, so no u8_ needed
1401 if (ch <= '9' && ch >= '0') // ^[0-9] found
1403 colorindex = ch - '0';
1408 // i points to the char after ^...
1409 // i+3 points to 3 in ^x123
1410 // i+3 == *maxlen would mean that char is missing
1411 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1413 // building colorindex...
1414 ch = tolower(text[1]);
1415 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1416 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1417 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1418 else tempcolorindex = 0;
1421 ch = tolower(text[2]);
1422 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1423 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1424 else tempcolorindex = 0;
1427 ch = tolower(text[3]);
1428 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1429 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1430 else tempcolorindex = 0;
1433 colorindex = tempcolorindex | 0xf;
1434 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1442 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1451 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1458 map = ft2_oldstyle_map;
1460 if(!least_one || i0) // never skip the first character
1461 if(x + width_of[ch] * dw > maxwidth)
1464 break; // oops, can't draw this
1466 x += width_of[ch] * dw;
1468 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1470 map = FontMap_FindForChar(fontmap, ch);
1473 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1479 mapch = ch - map->start;
1480 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1482 x += map->glyphs[mapch].advance_x * dw;
1491 *outcolor = colorindex;
1496 float DrawQ_Color[4];
1497 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)
1499 int shadow, colorindex = STRING_COLOR_DEFAULT;
1501 float x = startx, y, s, t, u, v, thisw;
1502 float *av, *at, *ac;
1504 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1505 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1506 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1507 Uchar ch, mapch, nextch;
1508 Uchar prevch = 0; // used for kerning
1511 //ft2_font_map_t *prevmap = NULL; // the previous map
1512 ft2_font_map_t *map = NULL; // the currently used map
1513 ft2_font_map_t *fontmap = NULL; // the font map for the size
1515 const char *text_start = text;
1517 ft2_font_t *ft2 = fnt->ft2;
1518 qboolean snap = true;
1522 const float *width_of;
1525 tw = R_TextureWidth(fnt->tex);
1526 th = R_TextureHeight(fnt->tex);
1534 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1535 w *= fnt->settings.scale;
1536 h *= fnt->settings.scale;
1541 map_index = Font_IndexForSize(ft2, h, &w, &h);
1543 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1544 fontmap = Font_MapForIndex(ft2, map_index);
1550 // draw the font at its baseline when using freetype
1552 ftbase_y = dh * (4.5/6.0);
1557 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1558 if(!r_draw2d.integer && !r_draw2d_force)
1559 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1561 // R_Mesh_ResetTextureState();
1563 R_Mesh_TexBind(0, fnt->tex);
1564 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1571 //ftbase_x = snap_to_pixel_x(ftbase_x);
1574 startx = snap_to_pixel_x(startx, 0.4);
1575 starty = snap_to_pixel_y(starty, 0.4);
1576 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1579 pix_x = vid.width / vid_conwidth.value;
1580 pix_y = vid.height / vid_conheight.value;
1583 width_of = fontmap->width_of;
1585 width_of = fnt->width_of;
1587 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1592 if (!outcolor || *outcolor == -1)
1593 colorindex = STRING_COLOR_DEFAULT;
1595 colorindex = *outcolor;
1597 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1604 x += r_textshadow.value * vid.width / vid_conwidth.value;
1605 y += r_textshadow.value * vid.height / vid_conheight.value;
1608 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1610 nextch = ch = u8_getnchar(text, &text, bytes_left);
1611 i = text - text_start;
1614 if (ch == ' ' && !fontmap)
1616 x += width_of[(int) ' '] * dw;
1619 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1621 ch = *text; // colors are ascii, so no u8_ needed
1622 if (ch <= '9' && ch >= '0') // ^[0-9] found
1624 colorindex = ch - '0';
1625 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1630 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1632 // building colorindex...
1633 ch = tolower(text[1]);
1634 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1635 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1636 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1637 else tempcolorindex = 0;
1640 ch = tolower(text[2]);
1641 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1642 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1643 else tempcolorindex = 0;
1646 ch = tolower(text[3]);
1647 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1648 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1649 else tempcolorindex = 0;
1652 colorindex = tempcolorindex | 0xf;
1653 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1654 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1655 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1663 else if (ch == STRING_COLOR_TAG)
1672 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1673 // this way we don't need to rebind fnt->tex for every old-style character
1674 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1677 x += 1.0/pix_x * r_textshadow.value;
1678 y += 1.0/pix_y * r_textshadow.value;
1680 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1688 if (map != ft2_oldstyle_map)
1692 // switching from freetype to non-freetype rendering
1693 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1694 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1700 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1701 map = ft2_oldstyle_map;
1705 //num = (unsigned char) text[i];
1706 //thisw = fnt->width_of[num];
1707 thisw = fnt->width_of[ch];
1708 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1709 s = (ch & 15)*0.0625f + (0.5f / tw);
1710 t = (ch >> 4)*0.0625f + (0.5f / th);
1711 u = 0.0625f * thisw - (1.0f / tw);
1712 v = 0.0625f - (1.0f / th);
1713 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1714 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1715 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1716 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1717 at[ 0] = s ; at[ 1] = t ;
1718 at[ 2] = s+u ; at[ 3] = t ;
1719 at[ 4] = s+u ; at[ 5] = t+v ;
1720 at[ 6] = s ; at[ 7] = t+v ;
1721 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1722 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1723 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1724 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1729 if (batchcount >= QUADELEMENTS_MAXQUADS)
1731 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1732 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1738 x += width_of[ch] * dw;
1740 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1742 // new charmap - need to render
1745 // we need a different character map, render what we currently have:
1746 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1747 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1754 map = FontMap_FindForChar(fontmap, ch);
1757 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1764 // this shouldn't happen
1769 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1772 mapch = ch - map->start;
1773 thisw = map->glyphs[mapch].advance_x;
1777 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1784 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1785 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1786 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1787 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1788 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1789 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1790 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1791 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1792 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1793 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1794 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1795 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1804 if (batchcount >= QUADELEMENTS_MAXQUADS)
1806 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1807 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1820 x -= 1.0/pix_x * r_textshadow.value;
1821 y -= 1.0/pix_y * r_textshadow.value;
1827 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1828 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1832 *outcolor = colorindex;
1834 // note: this relies on the proper text (not shadow) being drawn last
1838 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)
1840 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1843 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)
1845 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1848 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1850 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1853 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1855 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1860 // no ^xrgb management
1861 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1863 int color, numchars = 0;
1864 char *outputend2c = output2c + maxoutchars - 2;
1865 if (!outcolor || *outcolor == -1)
1866 color = STRING_COLOR_DEFAULT;
1870 maxreadchars = 1<<30;
1871 textend = text + maxreadchars;
1872 while (text != textend && *text)
1874 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1876 if (text[1] == STRING_COLOR_TAG)
1878 else if (text[1] >= '0' && text[1] <= '9')
1880 color = text[1] - '0';
1885 if (output2c >= outputend2c)
1887 *output2c++ = *text++;
1888 *output2c++ = color;
1891 output2c[0] = output2c[1] = 0;
1898 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)
1902 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1903 if(!r_draw2d.integer && !r_draw2d_force)
1906 // R_Mesh_ResetTextureState();
1912 height = pic->height;
1913 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1916 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1918 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1919 floats[0] = floats[9] = x;
1920 floats[1] = floats[4] = y;
1921 floats[3] = floats[6] = x + width;
1922 floats[7] = floats[10] = y + height;
1923 floats[12] = s1;floats[13] = t1;
1924 floats[14] = s2;floats[15] = t2;
1925 floats[16] = s4;floats[17] = t4;
1926 floats[18] = s3;floats[19] = t3;
1927 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1928 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1929 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1930 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1932 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1933 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1936 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1939 if(!r_draw2d.integer && !r_draw2d_force)
1941 DrawQ_ProcessDrawFlag(flags, hasalpha);
1943 // R_Mesh_ResetTextureState();
1944 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1946 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1947 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1950 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1954 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1955 if(!r_draw2d.integer && !r_draw2d_force)
1959 switch(vid.renderpath)
1961 case RENDERPATH_GL11:
1962 case RENDERPATH_GL13:
1963 case RENDERPATH_GL20:
1966 qglBegin(GL_LINE_LOOP);
1967 for (num = 0;num < mesh->num_vertices;num++)
1969 if (mesh->data_color4f)
1970 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]);
1971 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1977 case RENDERPATH_D3D9:
1978 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1980 case RENDERPATH_D3D10:
1981 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1983 case RENDERPATH_D3D11:
1984 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1986 case RENDERPATH_SOFT:
1987 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1989 case RENDERPATH_GLES1:
1990 case RENDERPATH_GLES2:
1991 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1996 //[515]: this is old, delete
1997 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1999 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2000 if(!r_draw2d.integer && !r_draw2d_force)
2003 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2005 switch(vid.renderpath)
2007 case RENDERPATH_GL11:
2008 case RENDERPATH_GL13:
2009 case RENDERPATH_GL20:
2013 //qglLineWidth(width);CHECKGLERROR
2015 GL_Color(r,g,b,alpha);
2018 qglVertex2f(x1, y1);
2019 qglVertex2f(x2, y2);
2024 case RENDERPATH_D3D9:
2025 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2027 case RENDERPATH_D3D10:
2028 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2030 case RENDERPATH_D3D11:
2031 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2033 case RENDERPATH_SOFT:
2034 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2036 case RENDERPATH_GLES1:
2037 case RENDERPATH_GLES2:
2038 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2043 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2046 qboolean hasalpha = false;
2047 for (i = 0;i < numlines*2;i++)
2048 if (color4f[i*4+3] < 1.0f)
2051 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2053 if(!r_draw2d.integer && !r_draw2d_force)
2056 switch(vid.renderpath)
2058 case RENDERPATH_GL11:
2059 case RENDERPATH_GL13:
2060 case RENDERPATH_GL20:
2063 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2065 //qglLineWidth(width);CHECKGLERROR
2068 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2069 qglDrawArrays(GL_LINES, 0, numlines*2);
2072 case RENDERPATH_D3D9:
2073 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2075 case RENDERPATH_D3D10:
2076 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2078 case RENDERPATH_D3D11:
2079 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2081 case RENDERPATH_SOFT:
2082 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2084 case RENDERPATH_GLES1:
2085 case RENDERPATH_GLES2:
2086 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2091 void DrawQ_SetClipArea(float x, float y, float width, float height)
2096 // We have to convert the con coords into real coords
2097 // OGL uses top to bottom
2098 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2099 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2100 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2101 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2102 switch(vid.renderpath)
2104 case RENDERPATH_GL11:
2105 case RENDERPATH_GL13:
2106 case RENDERPATH_GL20:
2107 case RENDERPATH_GLES1:
2108 case RENDERPATH_GLES2:
2109 case RENDERPATH_SOFT:
2110 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2112 case RENDERPATH_D3D9:
2113 GL_Scissor(ix, iy, iw, ih);
2115 case RENDERPATH_D3D10:
2116 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2118 case RENDERPATH_D3D11:
2119 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2123 GL_ScissorTest(true);
2126 void DrawQ_ResetClipArea(void)
2129 GL_ScissorTest(false);
2132 void DrawQ_Finish(void)
2134 r_refdef.draw2dstage = 0;
2137 void DrawQ_RecalcView(void)
2139 if(r_refdef.draw2dstage)
2140 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2143 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2144 void R_DrawGamma(void)
2147 switch(vid.renderpath)
2149 case RENDERPATH_GL20:
2150 case RENDERPATH_D3D9:
2151 case RENDERPATH_D3D10:
2152 case RENDERPATH_D3D11:
2153 case RENDERPATH_GLES2:
2154 if (vid_usinghwgamma || v_glslgamma.integer)
2157 case RENDERPATH_GL11:
2158 case RENDERPATH_GL13:
2159 if (vid_usinghwgamma)
2162 case RENDERPATH_GLES1:
2163 case RENDERPATH_SOFT:
2166 // all the blends ignore depth
2167 // R_Mesh_ResetTextureState();
2168 R_SetupShader_Generic_NoTexture(true, true);
2170 GL_DepthRange(0, 1);
2171 GL_PolygonOffset(0, 0);
2172 GL_DepthTest(false);
2174 // interpretation of brightness and contrast:
2175 // color range := brightness .. (brightness + contrast)
2176 // i.e. "c *= contrast; c += brightness"
2177 // plausible values for brightness thus range from -contrast to 1
2179 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2180 if (vid.support.ext_blend_subtract)
2182 if (v_color_enable.integer)
2184 c[0] = -v_color_black_r.value / v_color_white_r.value;
2185 c[1] = -v_color_black_g.value / v_color_white_g.value;
2186 c[2] = -v_color_black_b.value / v_color_white_b.value;
2189 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2190 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2192 // need SUBTRACTIVE blending to do this!
2193 GL_BlendEquationSubtract(true);
2194 GL_BlendFunc(GL_ONE, GL_ONE);
2195 GL_Color(c[0], c[1], c[2], 1);
2196 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2197 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2198 GL_BlendEquationSubtract(false);
2203 if (v_color_enable.integer)
2205 c[0] = v_color_white_r.value;
2206 c[1] = v_color_white_g.value;
2207 c[2] = v_color_white_b.value;
2210 c[0] = c[1] = c[2] = v_contrast.value;
2211 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2213 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2214 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2217 cc[0] = bound(0, c[0] - 1, 1);
2218 cc[1] = bound(0, c[1] - 1, 1);
2219 cc[2] = bound(0, c[2] - 1, 1);
2220 GL_Color(cc[0], cc[1], cc[2], 1);
2221 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2222 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2228 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2230 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2231 GL_Color(c[0], c[1], c[2], 1);
2232 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2233 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2236 // apply post-brightness (additive brightness, for where contrast was <= 1)
2237 if (v_color_enable.integer)
2239 c[0] = v_color_black_r.value;
2240 c[1] = v_color_black_g.value;
2241 c[2] = v_color_black_b.value;
2244 c[0] = c[1] = c[2] = v_brightness.value;
2245 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2247 GL_BlendFunc(GL_ONE, GL_ONE);
2248 GL_Color(c[0], c[1], c[2], 1);
2249 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2250 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);