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;
328 texflags = TEXF_ALPHA;
329 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
330 texflags |= TEXF_CLAMP;
331 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
332 texflags |= TEXF_COMPRESS;
334 // check whether the picture has already been cached
335 crc = CRC_Block((unsigned char *)path, strlen(path));
336 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
337 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
339 if (!strcmp (path, pic->name))
341 // if it was created (or replaced) by Draw_NewPic, just return it
342 if(pic->flags & CACHEPICFLAG_NEWPIC)
344 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
346 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
349 pic->autoload = false; // persist it
351 goto reload; // load it below, and then persist
358 if (numcachepics == MAX_CACHED_PICS)
360 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
361 // FIXME: support NULL in callers?
362 return cachepics; // return the first one
364 pic = cachepics + (numcachepics++);
365 memset(pic, 0, sizeof(*pic));
366 strlcpy (pic->name, path, sizeof(pic->name));
368 pic->chain = cachepichash[hashkey];
369 cachepichash[hashkey] = pic;
372 // TODO why does this crash?
373 if(pic->allow_free_tex && pic->tex)
374 R_PurgeTexture(pic->tex);
376 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
377 pic->flags = cachepicflags;
378 pic->tex = CL_GetDynTexture( path );
379 // if so, set the width/height, too
381 pic->allow_free_tex = false;
382 pic->width = R_TextureWidth(pic->tex);
383 pic->height = R_TextureHeight(pic->tex);
384 // we're done now (early-out)
388 pic->allow_free_tex = true;
390 pic->hasalpha = true; // assume alpha unless we know it has none
391 pic->texflags = texflags;
392 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
393 pic->lastusedframe = draw_frame;
395 // load a high quality image from disk if possible
396 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
398 // note this loads even if autoload is true, otherwise we can't get the width/height
400 pic->hasalpha = ddshasalpha;
401 pic->width = R_TextureWidth(pic->tex);
402 pic->height = R_TextureHeight(pic->tex);
404 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
407 pic->hasalpha = false;
408 if (pic->texflags & TEXF_ALPHA)
410 for (j = 3;j < image_width * image_height * 4;j += 4)
414 pic->hasalpha = true;
420 pic->width = image_width;
421 pic->height = image_height;
424 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);
426 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
427 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
433 pic->autoload = false;
434 // never compress the fallback images
435 pic->texflags &= ~TEXF_COMPRESS;
438 // now read the low quality version (wad or lmp file), and take the pic
439 // size from that even if we don't upload the texture, this way the pics
440 // show up the right size in the menu even if they were replaced with
441 // higher or lower resolution versions
442 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
443 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
445 if (developer_loading.integer)
446 Con_Printf("loading lump \"%s\"\n", pic->name);
450 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
451 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
452 // if no high quality replacement image was found, upload the original low quality texture
456 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);
461 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
463 if (developer_loading.integer)
464 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
466 if (!strcmp(pic->name, "gfx/conchars"))
468 // conchars is a raw image and with color 0 as transparent instead of 255
471 // if no high quality replacement image was found, upload the original low quality texture
475 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
480 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
481 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
482 // if no high quality replacement image was found, upload the original low quality texture
486 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);
498 // if it's not found on disk, generate an image
499 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
500 pic->width = R_TextureWidth(pic->tex);
501 pic->height = R_TextureHeight(pic->tex);
502 pic->allow_free_tex = (pic->tex != r_texture_notexture);
508 cachepic_t *Draw_CachePic (const char *path)
510 return Draw_CachePic_Flags (path, 0); // default to persistent!
513 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
516 if (pic->autoload && !pic->tex)
518 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
520 qboolean ddshasalpha;
521 float ddsavgcolor[4];
522 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
524 if (pic->tex == NULL)
526 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
528 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
529 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
532 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
534 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
536 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
537 R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
540 if (pic->tex == NULL)
541 pic->tex = draw_generatepic(pic->name, true);
543 pic->lastusedframe = draw_frame;
547 void Draw_Frame(void)
551 static double nextpurgetime;
552 if (nextpurgetime > realtime)
554 nextpurgetime = realtime + 0.05;
555 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
557 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
559 R_FreeTexture(pic->tex);
566 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
571 crc = CRC_Block((unsigned char *)picname, strlen(picname));
572 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
573 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
574 if (!strcmp (picname, pic->name))
579 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
581 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
587 if (numcachepics == MAX_CACHED_PICS)
589 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
590 // FIXME: support NULL in callers?
591 return cachepics; // return the first one
593 pic = cachepics + (numcachepics++);
594 memset(pic, 0, sizeof(*pic));
595 strlcpy (pic->name, picname, sizeof(pic->name));
597 pic->chain = cachepichash[hashkey];
598 cachepichash[hashkey] = pic;
601 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
603 pic->height = height;
604 if (pic->allow_free_tex && pic->tex)
605 R_FreeTexture(pic->tex);
606 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
610 void Draw_FreePic(const char *picname)
615 // this doesn't really free the pic, but does free it's texture
616 crc = CRC_Block((unsigned char *)picname, strlen(picname));
617 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
618 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
620 if (!strcmp (picname, pic->name) && pic->tex)
622 R_FreeTexture(pic->tex);
631 static float snap_to_pixel_x(float x, float roundUpAt);
632 extern int con_linewidth; // to force rewrapping
633 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
637 char widthfile[MAX_QPATH];
639 fs_offset_t widthbufsize;
641 if(override || !fnt->texpath[0])
643 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
644 // load the cvars when the font is FIRST loader
645 fnt->settings.scale = scale;
646 fnt->settings.voffset = voffset;
647 fnt->settings.antialias = r_font_antialias.integer;
648 fnt->settings.hinting = r_font_hinting.integer;
649 fnt->settings.outline = r_font_postprocess_outline.value;
650 fnt->settings.blur = r_font_postprocess_blur.value;
651 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
652 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
653 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
656 if (fnt->settings.scale <= 0)
657 fnt->settings.scale = 1;
659 if(drawtexturepool == NULL)
660 return; // before gl_draw_start, so will be loaded later
664 // clear freetype font
665 Font_UnloadFont(fnt->ft2);
670 if(fnt->req_face != -1)
672 if(!Font_LoadFont(fnt->texpath, fnt))
673 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
676 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
677 if(fnt->tex == r_texture_notexture)
679 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
681 if (!fnt->fallbacks[i][0])
683 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
684 if(fnt->tex != r_texture_notexture)
687 if(fnt->tex == r_texture_notexture)
689 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
690 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
693 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
696 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
698 // unspecified width == 1 (base width)
699 for(ch = 0; ch < 256; ++ch)
700 fnt->width_of[ch] = 1;
702 // FIXME load "name.width", if it fails, fill all with 1
703 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
705 float extraspacing = 0;
706 const char *p = widthbuf;
711 if(!COM_ParseToken_Simple(&p, false, false, true))
729 fnt->width_of[ch] = atof(com_token) + extraspacing;
733 if(!strcmp(com_token, "extraspacing"))
735 if(!COM_ParseToken_Simple(&p, false, false, true))
737 extraspacing = atof(com_token);
739 else if(!strcmp(com_token, "scale"))
741 if(!COM_ParseToken_Simple(&p, false, false, true))
743 fnt->settings.scale = atof(com_token);
747 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
748 if(!COM_ParseToken_Simple(&p, false, false, true))
760 for (i = 0; i < MAX_FONT_SIZES; ++i)
762 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
765 for(ch = 0; ch < 256; ++ch)
766 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
770 maxwidth = fnt->width_of[0];
771 for(i = 1; i < 256; ++i)
772 maxwidth = max(maxwidth, fnt->width_of[i]);
773 fnt->maxwidth = maxwidth;
775 // fix up maxwidth for overlap
776 fnt->maxwidth *= fnt->settings.scale;
778 if(fnt == FONT_CONSOLE)
779 con_linewidth = -1; // rewrap console in next frame
782 extern cvar_t developer_font;
783 dp_font_t *FindFont(const char *title, qboolean allocate_new)
788 for(i = 0; i < dp_fonts.maxsize; ++i)
789 if(!strcmp(dp_fonts.f[i].title, title))
790 return &dp_fonts.f[i];
791 // if not found - try allocate
794 // find any font with empty title
795 for(i = 0; i < dp_fonts.maxsize; ++i)
797 if(!strcmp(dp_fonts.f[i].title, ""))
799 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
800 return &dp_fonts.f[i];
803 // if no any 'free' fonts - expand buffer
804 oldsize = dp_fonts.maxsize;
805 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
806 if (developer_font.integer)
807 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
808 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
809 // relink ft2 structures
810 for(i = 0; i < oldsize; ++i)
811 if (dp_fonts.f[i].ft2)
812 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
813 // register a font in first expanded slot
814 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
815 return &dp_fonts.f[oldsize];
820 static float snap_to_pixel_x(float x, float roundUpAt)
822 float pixelpos = x * vid.width / vid_conwidth.value;
823 int snap = (int) pixelpos;
824 if (pixelpos - snap >= roundUpAt) ++snap;
825 return ((float)snap * vid_conwidth.value / vid.width);
827 x = (int)(x * vid.width / vid_conwidth.value);
828 x = (x * vid_conwidth.value / vid.width);
833 static float snap_to_pixel_y(float y, float roundUpAt)
835 float pixelpos = y * vid.height / vid_conheight.value;
836 int snap = (int) pixelpos;
837 if (pixelpos - snap > roundUpAt) ++snap;
838 return ((float)snap * vid_conheight.value / vid.height);
840 y = (int)(y * vid.height / vid_conheight.value);
841 y = (y * vid_conheight.value / vid.height);
846 static void LoadFont_f(void)
850 const char *filelist, *c, *cm;
851 float sz, scale, voffset;
852 char mainfont[MAX_QPATH];
856 Con_Printf("Available font commands:\n");
857 for(i = 0; i < dp_fonts.maxsize; ++i)
858 if (dp_fonts.f[i].title[0])
859 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
860 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
861 "can specify multiple fonts and faces\n"
862 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
863 "to load face 2 of the font gfx/vera-sans and use face 1\n"
864 "of gfx/fallback as fallback font.\n"
865 "You can also specify a list of font sizes to load, like this:\n"
866 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
867 "In many cases, 8 12 16 24 32 should be a good choice.\n"
869 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
870 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
874 f = FindFont(Cmd_Argv(1), true);
877 Con_Printf("font function not found\n");
882 filelist = "gfx/conchars";
884 filelist = Cmd_Argv(2);
886 memset(f->fallbacks, 0, sizeof(f->fallbacks));
887 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
889 // first font is handled "normally"
890 c = strchr(filelist, ':');
891 cm = strchr(filelist, ',');
892 if(c && (!cm || c < cm))
893 f->req_face = atoi(c+1);
900 if(!c || (c - filelist) > MAX_QPATH)
901 strlcpy(mainfont, filelist, sizeof(mainfont));
904 memcpy(mainfont, filelist, c - filelist);
905 mainfont[c - filelist] = 0;
908 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
910 c = strchr(filelist, ',');
916 c = strchr(filelist, ':');
917 cm = strchr(filelist, ',');
918 if(c && (!cm || c < cm))
919 f->fallback_faces[i] = atoi(c+1);
922 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
925 if(!c || (c-filelist) > MAX_QPATH)
927 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
931 memcpy(f->fallbacks[i], filelist, c - filelist);
932 f->fallbacks[i][c - filelist] = 0;
936 // for now: by default load only one size: the default size
938 for(i = 1; i < MAX_FONT_SIZES; ++i)
939 f->req_sizes[i] = -1;
945 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
948 if (!strcmp(Cmd_Argv(i), "scale"))
952 scale = atof(Cmd_Argv(i));
955 if (!strcmp(Cmd_Argv(i), "voffset"))
959 voffset = atof(Cmd_Argv(i));
964 continue; // no slot for other sizes
966 // parse one of sizes
967 sz = atof(Cmd_Argv(i));
968 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
970 // search for duplicated sizes
972 for (j=0; j<sizes; j++)
973 if (f->req_sizes[j] == sz)
976 continue; // sz already in req_sizes, don't add it again
978 if (sizes == MAX_FONT_SIZES)
980 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
984 f->req_sizes[sizes] = sz;
990 LoadFont(true, mainfont, f, scale, voffset);
998 static void gl_draw_start(void)
1002 drawtexturepool = R_AllocTexturePool();
1005 memset(cachepichash, 0, sizeof(cachepichash));
1009 // load default font textures
1010 for(i = 0; i < dp_fonts.maxsize; ++i)
1011 if (dp_fonts.f[i].title[0])
1012 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1014 // draw the loading screen so people have something to see in the newly opened window
1015 SCR_UpdateLoadingScreen(true);
1018 static void gl_draw_shutdown(void)
1022 R_FreeTexturePool(&drawtexturepool);
1025 memset(cachepichash, 0, sizeof(cachepichash));
1028 static void gl_draw_newmap(void)
1033 void GL_Draw_Init (void)
1037 Cvar_RegisterVariable(&r_font_postprocess_blur);
1038 Cvar_RegisterVariable(&r_font_postprocess_outline);
1039 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1040 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1041 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1042 Cvar_RegisterVariable(&r_font_hinting);
1043 Cvar_RegisterVariable(&r_font_antialias);
1044 Cvar_RegisterVariable(&r_textshadow);
1045 Cvar_RegisterVariable(&r_textbrightness);
1046 Cvar_RegisterVariable(&r_textcontrast);
1048 // allocate fonts storage
1049 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1050 dp_fonts.maxsize = MAX_FONTS;
1051 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1052 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1054 // assign starting font names
1055 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1056 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1057 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1058 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1059 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1060 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1061 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1062 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1063 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1064 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1065 if(!FONT_USER(i)->title[0])
1066 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1068 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1069 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1072 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1074 if (r_refdef.draw2dstage == 1)
1076 r_refdef.draw2dstage = 1;
1078 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
1081 qboolean r_draw2d_force = false;
1082 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1085 if(!r_draw2d.integer && !r_draw2d_force)
1087 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1089 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1091 if(flags == DRAWFLAG_ADDITIVE)
1093 GL_DepthMask(false);
1094 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1096 else if(flags == DRAWFLAG_MODULATE)
1098 GL_DepthMask(false);
1099 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1101 else if(flags == DRAWFLAG_2XMODULATE)
1103 GL_DepthMask(false);
1104 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1106 else if(flags == DRAWFLAG_SCREEN)
1108 GL_DepthMask(false);
1109 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1113 GL_DepthMask(false);
1114 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1119 GL_BlendFunc(GL_ONE, GL_ZERO);
1123 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1127 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1128 if(!r_draw2d.integer && !r_draw2d_force)
1131 // R_Mesh_ResetTextureState();
1132 floats[12] = 0.0f;floats[13] = 0.0f;
1133 floats[14] = 1.0f;floats[15] = 0.0f;
1134 floats[16] = 1.0f;floats[17] = 1.0f;
1135 floats[18] = 0.0f;floats[19] = 1.0f;
1136 floats[20] = floats[24] = floats[28] = floats[32] = red;
1137 floats[21] = floats[25] = floats[29] = floats[33] = green;
1138 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1139 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1145 height = pic->height;
1146 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1149 // AK07: lets be texel correct on the corners
1151 float horz_offset = 0.5f / pic->width;
1152 float vert_offset = 0.5f / pic->height;
1154 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1155 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1156 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1157 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1162 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1164 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1165 floats[0] = floats[9] = x;
1166 floats[1] = floats[4] = y;
1167 floats[3] = floats[6] = x + width;
1168 floats[7] = floats[10] = y + height;
1170 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1171 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1174 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)
1177 float af = DEG2RAD(-angle); // forward
1178 float ar = DEG2RAD(-angle + 90); // right
1179 float sinaf = sin(af);
1180 float cosaf = cos(af);
1181 float sinar = sin(ar);
1182 float cosar = cos(ar);
1184 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1185 if(!r_draw2d.integer && !r_draw2d_force)
1188 // R_Mesh_ResetTextureState();
1194 height = pic->height;
1195 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1198 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1200 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1203 floats[0] = x - cosaf*org_x - cosar*org_y;
1204 floats[1] = y - sinaf*org_x - sinar*org_y;
1207 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1208 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1211 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1212 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1215 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1216 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1218 floats[12] = 0.0f;floats[13] = 0.0f;
1219 floats[14] = 1.0f;floats[15] = 0.0f;
1220 floats[16] = 1.0f;floats[17] = 1.0f;
1221 floats[18] = 0.0f;floats[19] = 1.0f;
1222 floats[20] = floats[24] = floats[28] = floats[32] = red;
1223 floats[21] = floats[25] = floats[29] = floats[33] = green;
1224 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1225 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1227 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1228 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1231 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1235 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1236 if(!r_draw2d.integer && !r_draw2d_force)
1239 // R_Mesh_ResetTextureState();
1240 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1242 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1243 floats[0] = floats[9] = x;
1244 floats[1] = floats[4] = y;
1245 floats[3] = floats[6] = x + width;
1246 floats[7] = floats[10] = y + height;
1247 floats[12] = 0.0f;floats[13] = 0.0f;
1248 floats[14] = 1.0f;floats[15] = 0.0f;
1249 floats[16] = 1.0f;floats[17] = 1.0f;
1250 floats[18] = 0.0f;floats[19] = 1.0f;
1251 floats[20] = floats[24] = floats[28] = floats[32] = red;
1252 floats[21] = floats[25] = floats[29] = floats[33] = green;
1253 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1254 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1256 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1257 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1260 /// color tag printing
1261 static const vec4_t string_colors[] =
1264 // LordHavoc: why on earth is cyan before magenta in Quake3?
1265 // LordHavoc: note: Doom3 uses white for [0] and [7]
1266 {0.0, 0.0, 0.0, 1.0}, // black
1267 {1.0, 0.0, 0.0, 1.0}, // red
1268 {0.0, 1.0, 0.0, 1.0}, // green
1269 {1.0, 1.0, 0.0, 1.0}, // yellow
1270 {0.0, 0.0, 1.0, 1.0}, // blue
1271 {0.0, 1.0, 1.0, 1.0}, // cyan
1272 {1.0, 0.0, 1.0, 1.0}, // magenta
1273 {1.0, 1.0, 1.0, 1.0}, // white
1274 // [515]'s BX_COLOREDTEXT extension
1275 {1.0, 1.0, 1.0, 0.5}, // half transparent
1276 {0.5, 0.5, 0.5, 1.0} // half brightness
1277 // Black's color table
1278 //{1.0, 1.0, 1.0, 1.0},
1279 //{1.0, 0.0, 0.0, 1.0},
1280 //{0.0, 1.0, 0.0, 1.0},
1281 //{0.0, 0.0, 1.0, 1.0},
1282 //{1.0, 1.0, 0.0, 1.0},
1283 //{0.0, 1.0, 1.0, 1.0},
1284 //{1.0, 0.0, 1.0, 1.0},
1285 //{0.1, 0.1, 0.1, 1.0}
1288 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1290 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1292 float C = r_textcontrast.value;
1293 float B = r_textbrightness.value;
1294 if (colorindex & 0x10000) // that bit means RGB color
1296 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1297 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1298 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1299 color[3] = (colorindex & 0xf) / 15.0;
1302 Vector4Copy(string_colors[colorindex], color);
1303 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1306 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1307 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1311 // NOTE: this function always draws exactly one character if maxwidth <= 0
1312 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)
1314 const char *text_start = text;
1315 int colorindex = STRING_COLOR_DEFAULT;
1318 Uchar ch, mapch, nextch;
1319 Uchar prevch = 0; // used for kerning
1324 ft2_font_map_t *fontmap = NULL;
1325 ft2_font_map_t *map = NULL;
1326 //ft2_font_map_t *prevmap = NULL;
1327 ft2_font_t *ft2 = fnt->ft2;
1329 qboolean snap = true;
1330 qboolean least_one = false;
1331 float dw; // display w
1332 //float dh; // display h
1333 const float *width_of;
1340 // do this in the end
1341 w *= fnt->settings.scale;
1342 h *= fnt->settings.scale;
1344 // find the most fitting size:
1348 map_index = Font_IndexForSize(ft2, h, &w, &h);
1350 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1351 fontmap = Font_MapForIndex(ft2, map_index);
1360 if (!outcolor || *outcolor == -1)
1361 colorindex = STRING_COLOR_DEFAULT;
1363 colorindex = *outcolor;
1365 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1366 // ftbase_x = snap_to_pixel_x(0);
1371 maxwidth = -maxwidth;
1375 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1378 width_of = fontmap->width_of;
1380 width_of = fnt->width_of;
1382 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1385 nextch = ch = u8_getnchar(text, &text, bytes_left);
1386 i = text - text_start;
1389 if (ch == ' ' && !fontmap)
1391 if(!least_one || i0) // never skip the first character
1392 if(x + width_of[(int) ' '] * dw > maxwidth)
1395 break; // oops, can't draw this
1397 x += width_of[(int) ' '] * dw;
1400 // i points to the char after ^
1401 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1403 ch = *text; // colors are ascii, so no u8_ needed
1404 if (ch <= '9' && ch >= '0') // ^[0-9] found
1406 colorindex = ch - '0';
1411 // i points to the char after ^...
1412 // i+3 points to 3 in ^x123
1413 // i+3 == *maxlen would mean that char is missing
1414 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1416 // building colorindex...
1417 ch = tolower(text[1]);
1418 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1419 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1420 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1421 else tempcolorindex = 0;
1424 ch = tolower(text[2]);
1425 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1426 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1427 else tempcolorindex = 0;
1430 ch = tolower(text[3]);
1431 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1432 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1433 else tempcolorindex = 0;
1436 colorindex = tempcolorindex | 0xf;
1437 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1445 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1454 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1461 map = ft2_oldstyle_map;
1463 if(!least_one || i0) // never skip the first character
1464 if(x + width_of[ch] * dw > maxwidth)
1467 break; // oops, can't draw this
1469 x += width_of[ch] * dw;
1471 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1473 map = FontMap_FindForChar(fontmap, ch);
1476 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1482 mapch = ch - map->start;
1483 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1485 x += map->glyphs[mapch].advance_x * dw;
1494 *outcolor = colorindex;
1499 float DrawQ_Color[4];
1500 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)
1502 int shadow, colorindex = STRING_COLOR_DEFAULT;
1504 float x = startx, y, s, t, u, v, thisw;
1505 float *av, *at, *ac;
1507 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1508 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1509 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1510 Uchar ch, mapch, nextch;
1511 Uchar prevch = 0; // used for kerning
1514 //ft2_font_map_t *prevmap = NULL; // the previous map
1515 ft2_font_map_t *map = NULL; // the currently used map
1516 ft2_font_map_t *fontmap = NULL; // the font map for the size
1518 const char *text_start = text;
1520 ft2_font_t *ft2 = fnt->ft2;
1521 qboolean snap = true;
1525 const float *width_of;
1528 tw = R_TextureWidth(fnt->tex);
1529 th = R_TextureHeight(fnt->tex);
1537 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1538 w *= fnt->settings.scale;
1539 h *= fnt->settings.scale;
1544 map_index = Font_IndexForSize(ft2, h, &w, &h);
1546 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1547 fontmap = Font_MapForIndex(ft2, map_index);
1553 // draw the font at its baseline when using freetype
1555 ftbase_y = dh * (4.5/6.0);
1560 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1561 if(!r_draw2d.integer && !r_draw2d_force)
1562 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1564 // R_Mesh_ResetTextureState();
1566 R_Mesh_TexBind(0, fnt->tex);
1567 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1574 //ftbase_x = snap_to_pixel_x(ftbase_x);
1577 startx = snap_to_pixel_x(startx, 0.4);
1578 starty = snap_to_pixel_y(starty, 0.4);
1579 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1582 pix_x = vid.width / vid_conwidth.value;
1583 pix_y = vid.height / vid_conheight.value;
1586 width_of = fontmap->width_of;
1588 width_of = fnt->width_of;
1590 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1595 if (!outcolor || *outcolor == -1)
1596 colorindex = STRING_COLOR_DEFAULT;
1598 colorindex = *outcolor;
1600 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1607 x += r_textshadow.value * vid.width / vid_conwidth.value;
1608 y += r_textshadow.value * vid.height / vid_conheight.value;
1611 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1613 nextch = ch = u8_getnchar(text, &text, bytes_left);
1614 i = text - text_start;
1617 if (ch == ' ' && !fontmap)
1619 x += width_of[(int) ' '] * dw;
1622 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1624 ch = *text; // colors are ascii, so no u8_ needed
1625 if (ch <= '9' && ch >= '0') // ^[0-9] found
1627 colorindex = ch - '0';
1628 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1633 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1635 // building colorindex...
1636 ch = tolower(text[1]);
1637 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1638 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1639 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1640 else tempcolorindex = 0;
1643 ch = tolower(text[2]);
1644 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1645 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1646 else tempcolorindex = 0;
1649 ch = tolower(text[3]);
1650 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1651 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1652 else tempcolorindex = 0;
1655 colorindex = tempcolorindex | 0xf;
1656 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1657 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1658 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1666 else if (ch == STRING_COLOR_TAG)
1675 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1676 // this way we don't need to rebind fnt->tex for every old-style character
1677 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1680 x += 1.0/pix_x * r_textshadow.value;
1681 y += 1.0/pix_y * r_textshadow.value;
1683 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1691 if (map != ft2_oldstyle_map)
1695 // switching from freetype to non-freetype rendering
1696 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1697 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1703 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1704 map = ft2_oldstyle_map;
1708 //num = (unsigned char) text[i];
1709 //thisw = fnt->width_of[num];
1710 thisw = fnt->width_of[ch];
1711 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1712 s = (ch & 15)*0.0625f + (0.5f / tw);
1713 t = (ch >> 4)*0.0625f + (0.5f / th);
1714 u = 0.0625f * thisw - (1.0f / tw);
1715 v = 0.0625f - (1.0f / th);
1716 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1717 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1718 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1719 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1720 at[ 0] = s ; at[ 1] = t ;
1721 at[ 2] = s+u ; at[ 3] = t ;
1722 at[ 4] = s+u ; at[ 5] = t+v ;
1723 at[ 6] = s ; at[ 7] = t+v ;
1724 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1725 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1726 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1727 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1732 if (batchcount >= QUADELEMENTS_MAXQUADS)
1734 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1735 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1741 x += width_of[ch] * dw;
1743 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1745 // new charmap - need to render
1748 // we need a different character map, render what we currently have:
1749 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1750 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1757 map = FontMap_FindForChar(fontmap, ch);
1760 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1767 // this shouldn't happen
1772 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1775 mapch = ch - map->start;
1776 thisw = map->glyphs[mapch].advance_x;
1780 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1787 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1788 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1789 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1790 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1791 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1792 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1793 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1794 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1795 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1796 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1797 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1798 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1807 if (batchcount >= QUADELEMENTS_MAXQUADS)
1809 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1810 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1823 x -= 1.0/pix_x * r_textshadow.value;
1824 y -= 1.0/pix_y * r_textshadow.value;
1830 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1831 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1835 *outcolor = colorindex;
1837 // note: this relies on the proper text (not shadow) being drawn last
1841 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)
1843 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1846 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)
1848 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1851 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1853 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1856 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1858 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1863 // no ^xrgb management
1864 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1866 int color, numchars = 0;
1867 char *outputend2c = output2c + maxoutchars - 2;
1868 if (!outcolor || *outcolor == -1)
1869 color = STRING_COLOR_DEFAULT;
1873 maxreadchars = 1<<30;
1874 textend = text + maxreadchars;
1875 while (text != textend && *text)
1877 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1879 if (text[1] == STRING_COLOR_TAG)
1881 else if (text[1] >= '0' && text[1] <= '9')
1883 color = text[1] - '0';
1888 if (output2c >= outputend2c)
1890 *output2c++ = *text++;
1891 *output2c++ = color;
1894 output2c[0] = output2c[1] = 0;
1901 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)
1905 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1906 if(!r_draw2d.integer && !r_draw2d_force)
1909 // R_Mesh_ResetTextureState();
1915 height = pic->height;
1916 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1919 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1921 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1922 floats[0] = floats[9] = x;
1923 floats[1] = floats[4] = y;
1924 floats[3] = floats[6] = x + width;
1925 floats[7] = floats[10] = y + height;
1926 floats[12] = s1;floats[13] = t1;
1927 floats[14] = s2;floats[15] = t2;
1928 floats[16] = s4;floats[17] = t4;
1929 floats[18] = s3;floats[19] = t3;
1930 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1931 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1932 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1933 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1935 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1936 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1939 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1942 if(!r_draw2d.integer && !r_draw2d_force)
1944 DrawQ_ProcessDrawFlag(flags, hasalpha);
1946 // R_Mesh_ResetTextureState();
1947 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1949 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1950 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1953 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1957 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1958 if(!r_draw2d.integer && !r_draw2d_force)
1962 switch(vid.renderpath)
1964 case RENDERPATH_GL11:
1965 case RENDERPATH_GL13:
1966 case RENDERPATH_GL20:
1969 qglBegin(GL_LINE_LOOP);
1970 for (num = 0;num < mesh->num_vertices;num++)
1972 if (mesh->data_color4f)
1973 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]);
1974 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1980 case RENDERPATH_D3D9:
1981 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1983 case RENDERPATH_D3D10:
1984 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1986 case RENDERPATH_D3D11:
1987 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1989 case RENDERPATH_SOFT:
1990 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1992 case RENDERPATH_GLES1:
1993 case RENDERPATH_GLES2:
1994 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1999 //[515]: this is old, delete
2000 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2002 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2003 if(!r_draw2d.integer && !r_draw2d_force)
2006 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2008 switch(vid.renderpath)
2010 case RENDERPATH_GL11:
2011 case RENDERPATH_GL13:
2012 case RENDERPATH_GL20:
2016 //qglLineWidth(width);CHECKGLERROR
2018 GL_Color(r,g,b,alpha);
2021 qglVertex2f(x1, y1);
2022 qglVertex2f(x2, y2);
2027 case RENDERPATH_D3D9:
2028 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2030 case RENDERPATH_D3D10:
2031 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2033 case RENDERPATH_D3D11:
2034 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2036 case RENDERPATH_SOFT:
2037 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2039 case RENDERPATH_GLES1:
2040 case RENDERPATH_GLES2:
2041 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2046 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2049 qboolean hasalpha = false;
2050 for (i = 0;i < numlines*2;i++)
2051 if (color4f[i*4+3] < 1.0f)
2054 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2056 if(!r_draw2d.integer && !r_draw2d_force)
2059 switch(vid.renderpath)
2061 case RENDERPATH_GL11:
2062 case RENDERPATH_GL13:
2063 case RENDERPATH_GL20:
2066 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2068 //qglLineWidth(width);CHECKGLERROR
2071 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2072 qglDrawArrays(GL_LINES, 0, numlines*2);
2075 case RENDERPATH_D3D9:
2076 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2078 case RENDERPATH_D3D10:
2079 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2081 case RENDERPATH_D3D11:
2082 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2084 case RENDERPATH_SOFT:
2085 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2087 case RENDERPATH_GLES1:
2088 case RENDERPATH_GLES2:
2089 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2094 void DrawQ_SetClipArea(float x, float y, float width, float height)
2099 // We have to convert the con coords into real coords
2100 // OGL uses top to bottom
2101 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2102 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2103 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2104 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2105 switch(vid.renderpath)
2107 case RENDERPATH_GL11:
2108 case RENDERPATH_GL13:
2109 case RENDERPATH_GL20:
2110 case RENDERPATH_GLES1:
2111 case RENDERPATH_GLES2:
2112 case RENDERPATH_SOFT:
2113 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2115 case RENDERPATH_D3D9:
2116 GL_Scissor(ix, iy, iw, ih);
2118 case RENDERPATH_D3D10:
2119 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2121 case RENDERPATH_D3D11:
2122 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2126 GL_ScissorTest(true);
2129 void DrawQ_ResetClipArea(void)
2132 GL_ScissorTest(false);
2135 void DrawQ_Finish(void)
2137 r_refdef.draw2dstage = 0;
2140 void DrawQ_RecalcView(void)
2142 if(r_refdef.draw2dstage)
2143 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2146 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2147 void R_DrawGamma(void)
2150 switch(vid.renderpath)
2152 case RENDERPATH_GL20:
2153 case RENDERPATH_D3D9:
2154 case RENDERPATH_D3D10:
2155 case RENDERPATH_D3D11:
2156 case RENDERPATH_GLES2:
2157 if (vid_usinghwgamma || v_glslgamma.integer)
2160 case RENDERPATH_GL11:
2161 case RENDERPATH_GL13:
2162 if (vid_usinghwgamma)
2165 case RENDERPATH_GLES1:
2166 case RENDERPATH_SOFT:
2169 // all the blends ignore depth
2170 // R_Mesh_ResetTextureState();
2171 R_SetupShader_Generic_NoTexture(true, true);
2173 GL_DepthRange(0, 1);
2174 GL_PolygonOffset(0, 0);
2175 GL_DepthTest(false);
2177 // interpretation of brightness and contrast:
2178 // color range := brightness .. (brightness + contrast)
2179 // i.e. "c *= contrast; c += brightness"
2180 // plausible values for brightness thus range from -contrast to 1
2182 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2183 if (vid.support.ext_blend_subtract)
2185 if (v_color_enable.integer)
2187 c[0] = -v_color_black_r.value / v_color_white_r.value;
2188 c[1] = -v_color_black_g.value / v_color_white_g.value;
2189 c[2] = -v_color_black_b.value / v_color_white_b.value;
2192 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2193 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2195 // need SUBTRACTIVE blending to do this!
2196 GL_BlendEquationSubtract(true);
2197 GL_BlendFunc(GL_ONE, GL_ONE);
2198 GL_Color(c[0], c[1], c[2], 1);
2199 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2200 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2201 GL_BlendEquationSubtract(false);
2206 if (v_color_enable.integer)
2208 c[0] = v_color_white_r.value;
2209 c[1] = v_color_white_g.value;
2210 c[2] = v_color_white_b.value;
2213 c[0] = c[1] = c[2] = v_contrast.value;
2214 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2216 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2217 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2220 cc[0] = bound(0, c[0] - 1, 1);
2221 cc[1] = bound(0, c[1] - 1, 1);
2222 cc[2] = bound(0, c[2] - 1, 1);
2223 GL_Color(cc[0], cc[1], cc[2], 1);
2224 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2225 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2231 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2233 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2234 GL_Color(c[0], c[1], c[2], 1);
2235 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2236 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2239 // apply post-brightness (additive brightness, for where contrast was <= 1)
2240 if (v_color_enable.integer)
2242 c[0] = v_color_black_r.value;
2243 c[1] = v_color_black_g.value;
2244 c[2] = v_color_black_b.value;
2247 c[0] = c[1] = c[2] = v_brightness.value;
2248 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2250 GL_BlendFunc(GL_ONE, GL_ONE);
2251 GL_Color(c[0], c[1], c[2], 1);
2252 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2253 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);