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 strlcpy (pic->name, path, sizeof(pic->name));
366 pic->chain = cachepichash[hashkey];
367 cachepichash[hashkey] = pic;
370 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
371 pic->flags = cachepicflags;
372 pic->tex = CL_GetDynTexture( path );
373 // if so, set the width/height, too
375 pic->width = R_TextureWidth(pic->tex);
376 pic->height = R_TextureHeight(pic->tex);
377 // we're done now (early-out)
381 pic->hasalpha = true; // assume alpha unless we know it has none
382 pic->texflags = texflags;
383 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
384 pic->lastusedframe = draw_frame;
386 // load a high quality image from disk if possible
387 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
389 // note this loads even if autoload is true, otherwise we can't get the width/height
391 pic->hasalpha = ddshasalpha;
392 pic->width = R_TextureWidth(pic->tex);
393 pic->height = R_TextureHeight(pic->tex);
395 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
398 pic->hasalpha = false;
399 if (pic->texflags & TEXF_ALPHA)
401 for (j = 3;j < image_width * image_height * 4;j += 4)
405 pic->hasalpha = true;
411 pic->width = image_width;
412 pic->height = image_height;
415 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);
416 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
417 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
422 pic->autoload = false;
423 // never compress the fallback images
424 pic->texflags &= ~TEXF_COMPRESS;
427 // now read the low quality version (wad or lmp file), and take the pic
428 // size from that even if we don't upload the texture, this way the pics
429 // show up the right size in the menu even if they were replaced with
430 // higher or lower resolution versions
431 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
432 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
434 if (developer_loading.integer)
435 Con_Printf("loading lump \"%s\"\n", pic->name);
439 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
440 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
441 // if no high quality replacement image was found, upload the original low quality texture
445 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);
450 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
452 if (developer_loading.integer)
453 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
455 if (!strcmp(pic->name, "gfx/conchars"))
457 // conchars is a raw image and with color 0 as transparent instead of 255
460 // if no high quality replacement image was found, upload the original low quality texture
464 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
469 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
470 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
471 // if no high quality replacement image was found, upload the original low quality texture
475 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);
487 // if it's not found on disk, generate an image
488 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
489 pic->width = R_TextureWidth(pic->tex);
490 pic->height = R_TextureHeight(pic->tex);
496 cachepic_t *Draw_CachePic (const char *path)
498 return Draw_CachePic_Flags (path, 0); // default to persistent!
501 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
503 if (pic->autoload && !pic->tex)
505 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
507 qboolean ddshasalpha;
508 float ddsavgcolor[4];
509 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
511 if (pic->tex == NULL)
513 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
514 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
515 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
517 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
519 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
520 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
521 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
523 if (pic->tex == NULL)
524 pic->tex = draw_generatepic(pic->name, true);
526 pic->lastusedframe = draw_frame;
530 void Draw_Frame(void)
534 static double nextpurgetime;
535 if (nextpurgetime > realtime)
537 nextpurgetime = realtime + 0.05;
538 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
540 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
542 R_FreeTexture(pic->tex);
549 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
554 crc = CRC_Block((unsigned char *)picname, strlen(picname));
555 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
556 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
557 if (!strcmp (picname, pic->name))
562 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
564 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
572 if (numcachepics == MAX_CACHED_PICS)
574 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
575 // FIXME: support NULL in callers?
576 return cachepics; // return the first one
578 pic = cachepics + (numcachepics++);
579 strlcpy (pic->name, picname, sizeof(pic->name));
581 pic->chain = cachepichash[hashkey];
582 cachepichash[hashkey] = pic;
586 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
588 pic->height = height;
590 R_FreeTexture(pic->tex);
591 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
595 void Draw_FreePic(const char *picname)
600 // this doesn't really free the pic, but does free it's texture
601 crc = CRC_Block((unsigned char *)picname, strlen(picname));
602 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
603 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
605 if (!strcmp (picname, pic->name) && pic->tex)
607 R_FreeTexture(pic->tex);
616 static float snap_to_pixel_x(float x, float roundUpAt);
617 extern int con_linewidth; // to force rewrapping
618 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
622 char widthfile[MAX_QPATH];
624 fs_offset_t widthbufsize;
626 if(override || !fnt->texpath[0])
628 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
629 // load the cvars when the font is FIRST loader
630 fnt->settings.scale = scale;
631 fnt->settings.voffset = voffset;
632 fnt->settings.antialias = r_font_antialias.integer;
633 fnt->settings.hinting = r_font_hinting.integer;
634 fnt->settings.outline = r_font_postprocess_outline.value;
635 fnt->settings.blur = r_font_postprocess_blur.value;
636 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
637 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
638 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
641 if (fnt->settings.scale <= 0)
642 fnt->settings.scale = 1;
644 if(drawtexturepool == NULL)
645 return; // before gl_draw_start, so will be loaded later
649 // clear freetype font
650 Font_UnloadFont(fnt->ft2);
655 if(fnt->req_face != -1)
657 if(!Font_LoadFont(fnt->texpath, fnt))
658 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
661 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
662 if(fnt->tex == r_texture_notexture)
664 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
666 if (!fnt->fallbacks[i][0])
668 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
669 if(fnt->tex != r_texture_notexture)
672 if(fnt->tex == r_texture_notexture)
674 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
675 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
678 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
681 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
683 // unspecified width == 1 (base width)
684 for(ch = 0; ch < 256; ++ch)
685 fnt->width_of[ch] = 1;
687 // FIXME load "name.width", if it fails, fill all with 1
688 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
690 float extraspacing = 0;
691 const char *p = widthbuf;
696 if(!COM_ParseToken_Simple(&p, false, false))
714 fnt->width_of[ch] = atof(com_token) + extraspacing;
718 if(!strcmp(com_token, "extraspacing"))
720 if(!COM_ParseToken_Simple(&p, false, false))
722 extraspacing = atof(com_token);
724 else if(!strcmp(com_token, "scale"))
726 if(!COM_ParseToken_Simple(&p, false, false))
728 fnt->settings.scale = atof(com_token);
732 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
733 if(!COM_ParseToken_Simple(&p, false, false))
745 for (i = 0; i < MAX_FONT_SIZES; ++i)
747 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
750 for(ch = 0; ch < 256; ++ch)
751 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
755 maxwidth = fnt->width_of[0];
756 for(i = 1; i < 256; ++i)
757 maxwidth = max(maxwidth, fnt->width_of[i]);
758 fnt->maxwidth = maxwidth;
760 // fix up maxwidth for overlap
761 fnt->maxwidth *= fnt->settings.scale;
763 if(fnt == FONT_CONSOLE)
764 con_linewidth = -1; // rewrap console in next frame
767 extern cvar_t developer_font;
768 dp_font_t *FindFont(const char *title, qboolean allocate_new)
773 for(i = 0; i < dp_fonts.maxsize; ++i)
774 if(!strcmp(dp_fonts.f[i].title, title))
775 return &dp_fonts.f[i];
776 // if not found - try allocate
779 // find any font with empty title
780 for(i = 0; i < dp_fonts.maxsize; ++i)
782 if(!strcmp(dp_fonts.f[i].title, ""))
784 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
785 return &dp_fonts.f[i];
788 // if no any 'free' fonts - expand buffer
789 oldsize = dp_fonts.maxsize;
790 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
791 if (developer_font.integer)
792 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
793 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
794 // relink ft2 structures
795 for(i = 0; i < oldsize; ++i)
796 if (dp_fonts.f[i].ft2)
797 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
798 // register a font in first expanded slot
799 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
800 return &dp_fonts.f[oldsize];
805 static float snap_to_pixel_x(float x, float roundUpAt)
807 float pixelpos = x * vid.width / vid_conwidth.value;
808 int snap = (int) pixelpos;
809 if (pixelpos - snap >= roundUpAt) ++snap;
810 return ((float)snap * vid_conwidth.value / vid.width);
812 x = (int)(x * vid.width / vid_conwidth.value);
813 x = (x * vid_conwidth.value / vid.width);
818 static float snap_to_pixel_y(float y, float roundUpAt)
820 float pixelpos = y * vid.height / vid_conheight.value;
821 int snap = (int) pixelpos;
822 if (pixelpos - snap > roundUpAt) ++snap;
823 return ((float)snap * vid_conheight.value / vid.height);
825 y = (int)(y * vid.height / vid_conheight.value);
826 y = (y * vid_conheight.value / vid.height);
831 static void LoadFont_f(void)
835 const char *filelist, *c, *cm;
836 float sz, scale, voffset;
837 char mainfont[MAX_QPATH];
841 Con_Printf("Available font commands:\n");
842 for(i = 0; i < dp_fonts.maxsize; ++i)
843 if (dp_fonts.f[i].title[0])
844 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
845 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
846 "can specify multiple fonts and faces\n"
847 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
848 "to load face 2 of the font gfx/vera-sans and use face 1\n"
849 "of gfx/fallback as fallback font.\n"
850 "You can also specify a list of font sizes to load, like this:\n"
851 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
852 "In many cases, 8 12 16 24 32 should be a good choice.\n"
854 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
855 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
859 f = FindFont(Cmd_Argv(1), true);
862 Con_Printf("font function not found\n");
867 filelist = "gfx/conchars";
869 filelist = Cmd_Argv(2);
871 memset(f->fallbacks, 0, sizeof(f->fallbacks));
872 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
874 // first font is handled "normally"
875 c = strchr(filelist, ':');
876 cm = strchr(filelist, ',');
877 if(c && (!cm || c < cm))
878 f->req_face = atoi(c+1);
885 if(!c || (c - filelist) > MAX_QPATH)
886 strlcpy(mainfont, filelist, sizeof(mainfont));
889 memcpy(mainfont, filelist, c - filelist);
890 mainfont[c - filelist] = 0;
893 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
895 c = strchr(filelist, ',');
901 c = strchr(filelist, ':');
902 cm = strchr(filelist, ',');
903 if(c && (!cm || c < cm))
904 f->fallback_faces[i] = atoi(c+1);
907 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
910 if(!c || (c-filelist) > MAX_QPATH)
912 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
916 memcpy(f->fallbacks[i], filelist, c - filelist);
917 f->fallbacks[i][c - filelist] = 0;
921 // for now: by default load only one size: the default size
923 for(i = 1; i < MAX_FONT_SIZES; ++i)
924 f->req_sizes[i] = -1;
930 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
933 if (!strcmp(Cmd_Argv(i), "scale"))
937 scale = atof(Cmd_Argv(i));
940 if (!strcmp(Cmd_Argv(i), "voffset"))
944 voffset = atof(Cmd_Argv(i));
949 continue; // no slot for other sizes
951 // parse one of sizes
952 sz = atof(Cmd_Argv(i));
953 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
955 // search for duplicated sizes
957 for (j=0; j<sizes; j++)
958 if (f->req_sizes[j] == sz)
961 continue; // sz already in req_sizes, don't add it again
963 if (sizes == MAX_FONT_SIZES)
965 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
969 f->req_sizes[sizes] = sz;
975 LoadFont(true, mainfont, f, scale, voffset);
983 static void gl_draw_start(void)
986 drawtexturepool = R_AllocTexturePool();
989 memset(cachepichash, 0, sizeof(cachepichash));
993 // load default font textures
994 for(i = 0; i < dp_fonts.maxsize; ++i)
995 if (dp_fonts.f[i].title[0])
996 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
998 // draw the loading screen so people have something to see in the newly opened window
999 SCR_UpdateLoadingScreen(true);
1002 static void gl_draw_shutdown(void)
1006 R_FreeTexturePool(&drawtexturepool);
1009 memset(cachepichash, 0, sizeof(cachepichash));
1012 static void gl_draw_newmap(void)
1017 void GL_Draw_Init (void)
1021 Cvar_RegisterVariable(&r_font_postprocess_blur);
1022 Cvar_RegisterVariable(&r_font_postprocess_outline);
1023 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1024 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1025 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1026 Cvar_RegisterVariable(&r_font_hinting);
1027 Cvar_RegisterVariable(&r_font_antialias);
1028 Cvar_RegisterVariable(&r_textshadow);
1029 Cvar_RegisterVariable(&r_textbrightness);
1030 Cvar_RegisterVariable(&r_textcontrast);
1032 // allocate fonts storage
1033 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1034 dp_fonts.maxsize = MAX_FONTS;
1035 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1036 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1038 // assign starting font names
1039 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1040 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1041 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1042 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1043 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1044 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1045 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1046 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1047 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1048 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1049 if(!FONT_USER(i)->title[0])
1050 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1052 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1053 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1056 static void _DrawQ_Setup(void)
1058 r_viewport_t viewport;
1059 if (r_refdef.draw2dstage == 1)
1061 r_refdef.draw2dstage = 1;
1063 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);
1064 R_Mesh_ResetRenderTargets();
1065 R_SetViewport(&viewport);
1066 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1067 GL_DepthFunc(GL_LEQUAL);
1068 GL_PolygonOffset(0,0);
1069 GL_CullFace(GL_NONE);
1070 R_EntityMatrix(&identitymatrix);
1072 GL_DepthRange(0, 1);
1073 GL_PolygonOffset(0, 0);
1074 GL_DepthTest(false);
1078 qboolean r_draw2d_force = false;
1079 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1083 if(!r_draw2d.integer && !r_draw2d_force)
1085 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1087 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1089 if(flags == DRAWFLAG_ADDITIVE)
1091 GL_DepthMask(false);
1092 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1094 else if(flags == DRAWFLAG_MODULATE)
1096 GL_DepthMask(false);
1097 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1099 else if(flags == DRAWFLAG_2XMODULATE)
1101 GL_DepthMask(false);
1102 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1104 else if(flags == DRAWFLAG_SCREEN)
1106 GL_DepthMask(false);
1107 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1111 GL_DepthMask(false);
1112 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1117 GL_BlendFunc(GL_ONE, GL_ZERO);
1121 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1125 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1126 if(!r_draw2d.integer && !r_draw2d_force)
1129 // R_Mesh_ResetTextureState();
1130 floats[12] = 0.0f;floats[13] = 0.0f;
1131 floats[14] = 1.0f;floats[15] = 0.0f;
1132 floats[16] = 1.0f;floats[17] = 1.0f;
1133 floats[18] = 0.0f;floats[19] = 1.0f;
1134 floats[20] = floats[24] = floats[28] = floats[32] = red;
1135 floats[21] = floats[25] = floats[29] = floats[33] = green;
1136 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1137 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1143 height = pic->height;
1144 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, true);
1147 // AK07: lets be texel correct on the corners
1149 float horz_offset = 0.5f / pic->width;
1150 float vert_offset = 0.5f / pic->height;
1152 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1153 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1154 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1155 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1160 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
1162 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1163 floats[0] = floats[9] = x;
1164 floats[1] = floats[4] = y;
1165 floats[3] = floats[6] = x + width;
1166 floats[7] = floats[10] = y + height;
1168 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1169 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1172 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)
1175 float af = DEG2RAD(-angle); // forward
1176 float ar = DEG2RAD(-angle + 90); // right
1177 float sinaf = sin(af);
1178 float cosaf = cos(af);
1179 float sinar = sin(ar);
1180 float cosar = cos(ar);
1182 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1183 if(!r_draw2d.integer && !r_draw2d_force)
1186 // R_Mesh_ResetTextureState();
1192 height = pic->height;
1193 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, true);
1196 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
1198 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1201 floats[0] = x - cosaf*org_x - cosar*org_y;
1202 floats[1] = y - sinaf*org_x - sinar*org_y;
1205 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1206 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1209 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1210 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1213 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1214 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1216 floats[12] = 0.0f;floats[13] = 0.0f;
1217 floats[14] = 1.0f;floats[15] = 0.0f;
1218 floats[16] = 1.0f;floats[17] = 1.0f;
1219 floats[18] = 0.0f;floats[19] = 1.0f;
1220 floats[20] = floats[24] = floats[28] = floats[32] = red;
1221 floats[21] = floats[25] = floats[29] = floats[33] = green;
1222 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1223 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1225 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1226 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1229 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1233 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1234 if(!r_draw2d.integer && !r_draw2d_force)
1237 // R_Mesh_ResetTextureState();
1238 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
1240 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1241 floats[0] = floats[9] = x;
1242 floats[1] = floats[4] = y;
1243 floats[3] = floats[6] = x + width;
1244 floats[7] = floats[10] = y + height;
1245 floats[12] = 0.0f;floats[13] = 0.0f;
1246 floats[14] = 1.0f;floats[15] = 0.0f;
1247 floats[16] = 1.0f;floats[17] = 1.0f;
1248 floats[18] = 0.0f;floats[19] = 1.0f;
1249 floats[20] = floats[24] = floats[28] = floats[32] = red;
1250 floats[21] = floats[25] = floats[29] = floats[33] = green;
1251 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1252 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1254 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1255 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1258 /// color tag printing
1259 static const vec4_t string_colors[] =
1262 // LordHavoc: why on earth is cyan before magenta in Quake3?
1263 // LordHavoc: note: Doom3 uses white for [0] and [7]
1264 {0.0, 0.0, 0.0, 1.0}, // black
1265 {1.0, 0.0, 0.0, 1.0}, // red
1266 {0.0, 1.0, 0.0, 1.0}, // green
1267 {1.0, 1.0, 0.0, 1.0}, // yellow
1268 {0.0, 0.0, 1.0, 1.0}, // blue
1269 {0.0, 1.0, 1.0, 1.0}, // cyan
1270 {1.0, 0.0, 1.0, 1.0}, // magenta
1271 {1.0, 1.0, 1.0, 1.0}, // white
1272 // [515]'s BX_COLOREDTEXT extension
1273 {1.0, 1.0, 1.0, 0.5}, // half transparent
1274 {0.5, 0.5, 0.5, 1.0} // half brightness
1275 // Black's color table
1276 //{1.0, 1.0, 1.0, 1.0},
1277 //{1.0, 0.0, 0.0, 1.0},
1278 //{0.0, 1.0, 0.0, 1.0},
1279 //{0.0, 0.0, 1.0, 1.0},
1280 //{1.0, 1.0, 0.0, 1.0},
1281 //{0.0, 1.0, 1.0, 1.0},
1282 //{1.0, 0.0, 1.0, 1.0},
1283 //{0.1, 0.1, 0.1, 1.0}
1286 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1288 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1290 float C = r_textcontrast.value;
1291 float B = r_textbrightness.value;
1292 if (colorindex & 0x10000) // that bit means RGB color
1294 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1295 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1296 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1297 color[3] = (colorindex & 0xf) / 15.0;
1300 Vector4Copy(string_colors[colorindex], color);
1301 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1304 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1305 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1309 // NOTE: this function always draws exactly one character if maxwidth <= 0
1310 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)
1312 const char *text_start = text;
1313 int colorindex = STRING_COLOR_DEFAULT;
1316 Uchar ch, mapch, nextch;
1317 Uchar prevch = 0; // used for kerning
1322 ft2_font_map_t *fontmap = NULL;
1323 ft2_font_map_t *map = NULL;
1324 //ft2_font_map_t *prevmap = NULL;
1325 ft2_font_t *ft2 = fnt->ft2;
1327 qboolean snap = true;
1328 qboolean least_one = false;
1329 float dw; // display w
1330 //float dh; // display h
1331 const float *width_of;
1338 // do this in the end
1339 w *= fnt->settings.scale;
1340 h *= fnt->settings.scale;
1342 // find the most fitting size:
1346 map_index = Font_IndexForSize(ft2, h, &w, &h);
1348 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1349 fontmap = Font_MapForIndex(ft2, map_index);
1358 if (!outcolor || *outcolor == -1)
1359 colorindex = STRING_COLOR_DEFAULT;
1361 colorindex = *outcolor;
1363 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1364 // ftbase_x = snap_to_pixel_x(0);
1369 maxwidth = -maxwidth;
1373 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1376 width_of = fontmap->width_of;
1378 width_of = fnt->width_of;
1380 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1383 nextch = ch = u8_getnchar(text, &text, bytes_left);
1384 i = text - text_start;
1387 if (ch == ' ' && !fontmap)
1389 if(!least_one || i0) // never skip the first character
1390 if(x + width_of[(int) ' '] * dw > maxwidth)
1393 break; // oops, can't draw this
1395 x += width_of[(int) ' '] * dw;
1398 // i points to the char after ^
1399 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1401 ch = *text; // colors are ascii, so no u8_ needed
1402 if (ch <= '9' && ch >= '0') // ^[0-9] found
1404 colorindex = ch - '0';
1409 // i points to the char after ^...
1410 // i+3 points to 3 in ^x123
1411 // i+3 == *maxlen would mean that char is missing
1412 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1414 // building colorindex...
1415 ch = tolower(text[1]);
1416 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1417 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1418 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1419 else tempcolorindex = 0;
1422 ch = tolower(text[2]);
1423 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1424 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1425 else tempcolorindex = 0;
1428 ch = tolower(text[3]);
1429 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1430 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1431 else tempcolorindex = 0;
1434 colorindex = tempcolorindex | 0xf;
1435 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1443 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1452 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1459 map = ft2_oldstyle_map;
1461 if(!least_one || i0) // never skip the first character
1462 if(x + width_of[ch] * dw > maxwidth)
1465 break; // oops, can't draw this
1467 x += width_of[ch] * dw;
1469 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1471 map = FontMap_FindForChar(fontmap, ch);
1474 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1480 mapch = ch - map->start;
1481 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1483 x += map->glyphs[mapch].advance_x * dw;
1492 *outcolor = colorindex;
1497 float DrawQ_Color[4];
1498 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)
1500 int shadow, colorindex = STRING_COLOR_DEFAULT;
1502 float x = startx, y, s, t, u, v, thisw;
1503 float *av, *at, *ac;
1505 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1506 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1507 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1508 Uchar ch, mapch, nextch;
1509 Uchar prevch = 0; // used for kerning
1512 //ft2_font_map_t *prevmap = NULL; // the previous map
1513 ft2_font_map_t *map = NULL; // the currently used map
1514 ft2_font_map_t *fontmap = NULL; // the font map for the size
1516 const char *text_start = text;
1518 ft2_font_t *ft2 = fnt->ft2;
1519 qboolean snap = true;
1523 const float *width_of;
1526 tw = R_TextureWidth(fnt->tex);
1527 th = R_TextureHeight(fnt->tex);
1535 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1536 w *= fnt->settings.scale;
1537 h *= fnt->settings.scale;
1542 map_index = Font_IndexForSize(ft2, h, &w, &h);
1544 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1545 fontmap = Font_MapForIndex(ft2, map_index);
1551 // draw the font at its baseline when using freetype
1553 ftbase_y = dh * (4.5/6.0);
1558 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1559 if(!r_draw2d.integer && !r_draw2d_force)
1560 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1562 // R_Mesh_ResetTextureState();
1564 R_Mesh_TexBind(0, fnt->tex);
1565 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, true);
1572 //ftbase_x = snap_to_pixel_x(ftbase_x);
1575 startx = snap_to_pixel_x(startx, 0.4);
1576 starty = snap_to_pixel_y(starty, 0.4);
1577 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1580 pix_x = vid.width / vid_conwidth.value;
1581 pix_y = vid.height / vid_conheight.value;
1584 width_of = fontmap->width_of;
1586 width_of = fnt->width_of;
1588 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1593 if (!outcolor || *outcolor == -1)
1594 colorindex = STRING_COLOR_DEFAULT;
1596 colorindex = *outcolor;
1598 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1605 x += r_textshadow.value * vid.width / vid_conwidth.value;
1606 y += r_textshadow.value * vid.height / vid_conheight.value;
1609 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1611 nextch = ch = u8_getnchar(text, &text, bytes_left);
1612 i = text - text_start;
1615 if (ch == ' ' && !fontmap)
1617 x += width_of[(int) ' '] * dw;
1620 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1622 ch = *text; // colors are ascii, so no u8_ needed
1623 if (ch <= '9' && ch >= '0') // ^[0-9] found
1625 colorindex = ch - '0';
1626 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1631 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1633 // building colorindex...
1634 ch = tolower(text[1]);
1635 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1636 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1637 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1638 else tempcolorindex = 0;
1641 ch = tolower(text[2]);
1642 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1643 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1644 else tempcolorindex = 0;
1647 ch = tolower(text[3]);
1648 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1649 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1650 else tempcolorindex = 0;
1653 colorindex = tempcolorindex | 0xf;
1654 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1655 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1656 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1664 else if (ch == STRING_COLOR_TAG)
1673 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1674 // this way we don't need to rebind fnt->tex for every old-style character
1675 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1678 x += 1.0/pix_x * r_textshadow.value;
1679 y += 1.0/pix_y * r_textshadow.value;
1681 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1689 if (map != ft2_oldstyle_map)
1693 // switching from freetype to non-freetype rendering
1694 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1695 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1701 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, true);
1702 map = ft2_oldstyle_map;
1706 //num = (unsigned char) text[i];
1707 //thisw = fnt->width_of[num];
1708 thisw = fnt->width_of[ch];
1709 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1710 s = (ch & 15)*0.0625f + (0.5f / tw);
1711 t = (ch >> 4)*0.0625f + (0.5f / th);
1712 u = 0.0625f * thisw - (1.0f / tw);
1713 v = 0.0625f - (1.0f / th);
1714 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1715 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1716 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1717 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1718 at[ 0] = s ; at[ 1] = t ;
1719 at[ 2] = s+u ; at[ 3] = t ;
1720 at[ 4] = s+u ; at[ 5] = t+v ;
1721 at[ 6] = s ; at[ 7] = t+v ;
1722 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1723 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1724 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1725 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1730 if (batchcount >= QUADELEMENTS_MAXQUADS)
1732 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1733 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1739 x += width_of[ch] * dw;
1741 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1743 // new charmap - need to render
1746 // we need a different character map, render what we currently have:
1747 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1748 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1755 map = FontMap_FindForChar(fontmap, ch);
1758 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1765 // this shouldn't happen
1770 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, true);
1773 mapch = ch - map->start;
1774 thisw = map->glyphs[mapch].advance_x;
1778 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1785 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1786 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1787 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1788 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1789 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1790 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1791 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1792 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1793 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1794 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1795 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1796 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1805 if (batchcount >= QUADELEMENTS_MAXQUADS)
1807 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1808 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1821 x -= 1.0/pix_x * r_textshadow.value;
1822 y -= 1.0/pix_y * r_textshadow.value;
1828 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1829 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1833 *outcolor = colorindex;
1835 // note: this relies on the proper text (not shadow) being drawn last
1839 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)
1841 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1844 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)
1846 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1849 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1851 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1854 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1856 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1861 // no ^xrgb management
1862 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1864 int color, numchars = 0;
1865 char *outputend2c = output2c + maxoutchars - 2;
1866 if (!outcolor || *outcolor == -1)
1867 color = STRING_COLOR_DEFAULT;
1871 maxreadchars = 1<<30;
1872 textend = text + maxreadchars;
1873 while (text != textend && *text)
1875 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1877 if (text[1] == STRING_COLOR_TAG)
1879 else if (text[1] >= '0' && text[1] <= '9')
1881 color = text[1] - '0';
1886 if (output2c >= outputend2c)
1888 *output2c++ = *text++;
1889 *output2c++ = color;
1892 output2c[0] = output2c[1] = 0;
1899 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)
1903 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1904 if(!r_draw2d.integer && !r_draw2d_force)
1907 // R_Mesh_ResetTextureState();
1913 height = pic->height;
1914 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, true);
1917 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
1919 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1920 floats[0] = floats[9] = x;
1921 floats[1] = floats[4] = y;
1922 floats[3] = floats[6] = x + width;
1923 floats[7] = floats[10] = y + height;
1924 floats[12] = s1;floats[13] = t1;
1925 floats[14] = s2;floats[15] = t2;
1926 floats[16] = s4;floats[17] = t4;
1927 floats[18] = s3;floats[19] = t3;
1928 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1929 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1930 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1931 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1933 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1934 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1937 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1941 if(!r_draw2d.integer && !r_draw2d_force)
1943 DrawQ_ProcessDrawFlag(flags, hasalpha);
1945 // R_Mesh_ResetTextureState();
1946 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, true);
1948 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1949 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1952 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1956 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1957 if(!r_draw2d.integer && !r_draw2d_force)
1961 switch(vid.renderpath)
1963 case RENDERPATH_GL11:
1964 case RENDERPATH_GL13:
1965 case RENDERPATH_GL20:
1967 qglBegin(GL_LINE_LOOP);
1968 for (num = 0;num < mesh->num_vertices;num++)
1970 if (mesh->data_color4f)
1971 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]);
1972 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(NULL, NULL, GL_MODULATE, 1, true);
2005 switch(vid.renderpath)
2007 case RENDERPATH_GL11:
2008 case RENDERPATH_GL13:
2009 case RENDERPATH_GL20:
2012 //qglLineWidth(width);CHECKGLERROR
2014 GL_Color(r,g,b,alpha);
2017 qglVertex2f(x1, y1);
2018 qglVertex2f(x2, y2);
2022 case RENDERPATH_D3D9:
2023 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2025 case RENDERPATH_D3D10:
2026 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2028 case RENDERPATH_D3D11:
2029 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2031 case RENDERPATH_SOFT:
2032 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2034 case RENDERPATH_GLES1:
2035 case RENDERPATH_GLES2:
2036 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2041 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2044 qboolean hasalpha = false;
2045 for (i = 0;i < numlines*2;i++)
2046 if (color4f[i*4+3] < 1.0f)
2049 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2051 if(!r_draw2d.integer && !r_draw2d_force)
2054 switch(vid.renderpath)
2056 case RENDERPATH_GL11:
2057 case RENDERPATH_GL13:
2058 case RENDERPATH_GL20:
2061 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
2063 //qglLineWidth(width);CHECKGLERROR
2066 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2067 qglDrawArrays(GL_LINES, 0, numlines*2);
2070 case RENDERPATH_D3D9:
2071 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2073 case RENDERPATH_D3D10:
2074 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2076 case RENDERPATH_D3D11:
2077 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2079 case RENDERPATH_SOFT:
2080 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2082 case RENDERPATH_GLES1:
2083 case RENDERPATH_GLES2:
2084 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2089 void DrawQ_SetClipArea(float x, float y, float width, float height)
2094 // We have to convert the con coords into real coords
2095 // OGL uses top to bottom
2096 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2097 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2098 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2099 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2100 switch(vid.renderpath)
2102 case RENDERPATH_GL11:
2103 case RENDERPATH_GL13:
2104 case RENDERPATH_GL20:
2105 case RENDERPATH_GLES1:
2106 case RENDERPATH_GLES2:
2107 case RENDERPATH_SOFT:
2108 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2110 case RENDERPATH_D3D9:
2111 GL_Scissor(ix, iy, iw, ih);
2113 case RENDERPATH_D3D10:
2114 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2116 case RENDERPATH_D3D11:
2117 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2121 GL_ScissorTest(true);
2124 void DrawQ_ResetClipArea(void)
2127 GL_ScissorTest(false);
2130 void DrawQ_Finish(void)
2132 r_refdef.draw2dstage = 0;
2135 void DrawQ_RecalcView(void)
2137 if(r_refdef.draw2dstage)
2138 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2141 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2142 void R_DrawGamma(void)
2145 switch(vid.renderpath)
2147 case RENDERPATH_GL20:
2148 case RENDERPATH_D3D9:
2149 case RENDERPATH_D3D10:
2150 case RENDERPATH_D3D11:
2151 case RENDERPATH_GLES2:
2152 if (vid_usinghwgamma || v_glslgamma.integer)
2155 case RENDERPATH_GL11:
2156 case RENDERPATH_GL13:
2157 if (vid_usinghwgamma)
2160 case RENDERPATH_GLES1:
2161 case RENDERPATH_SOFT:
2164 // all the blends ignore depth
2165 // R_Mesh_ResetTextureState();
2166 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1, true);
2168 GL_DepthRange(0, 1);
2169 GL_PolygonOffset(0, 0);
2170 GL_DepthTest(false);
2172 // interpretation of brightness and contrast:
2173 // color range := brightness .. (brightness + contrast)
2174 // i.e. "c *= contrast; c += brightness"
2175 // plausible values for brightness thus range from -contrast to 1
2177 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2178 if (vid.support.ext_blend_subtract)
2180 if (v_color_enable.integer)
2182 c[0] = -v_color_black_r.value / v_color_white_r.value;
2183 c[1] = -v_color_black_g.value / v_color_white_g.value;
2184 c[2] = -v_color_black_b.value / v_color_white_b.value;
2187 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2188 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2190 // need SUBTRACTIVE blending to do this!
2191 GL_BlendEquationSubtract(true);
2192 GL_BlendFunc(GL_ONE, GL_ONE);
2193 GL_Color(c[0], c[1], c[2], 1);
2194 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2195 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2196 GL_BlendEquationSubtract(false);
2201 if (v_color_enable.integer)
2203 c[0] = v_color_white_r.value;
2204 c[1] = v_color_white_g.value;
2205 c[2] = v_color_white_b.value;
2208 c[0] = c[1] = c[2] = v_contrast.value;
2209 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2211 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2212 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2215 cc[0] = bound(0, c[0] - 1, 1);
2216 cc[1] = bound(0, c[1] - 1, 1);
2217 cc[2] = bound(0, c[2] - 1, 1);
2218 GL_Color(cc[0], cc[1], cc[2], 1);
2219 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2220 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2226 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2228 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2229 GL_Color(c[0], c[1], c[2], 1);
2230 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2231 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2234 // apply post-brightness (additive brightness, for where contrast was <= 1)
2235 if (v_color_enable.integer)
2237 c[0] = v_color_black_r.value;
2238 c[1] = v_color_black_g.value;
2239 c[2] = v_color_black_b.value;
2242 c[0] = c[1] = c[2] = v_brightness.value;
2243 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2245 GL_BlendFunc(GL_ONE, GL_ONE);
2246 GL_Color(c[0], c[1], c[2], 1);
2247 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2248 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);