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)
1074 r_viewport_t viewport;
1075 if (r_refdef.draw2dstage == 1)
1077 r_refdef.draw2dstage = 1;
1079 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);
1080 R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
1081 R_SetViewport(&viewport);
1082 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1083 GL_DepthFunc(GL_LEQUAL);
1084 GL_PolygonOffset(0,0);
1085 GL_CullFace(GL_NONE);
1086 R_EntityMatrix(&identitymatrix);
1088 GL_DepthRange(0, 1);
1089 GL_PolygonOffset(0, 0);
1090 GL_DepthTest(false);
1094 qboolean r_draw2d_force = false;
1095 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1099 if(!r_draw2d.integer && !r_draw2d_force)
1101 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1103 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1105 if(flags == DRAWFLAG_ADDITIVE)
1107 GL_DepthMask(false);
1108 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1110 else if(flags == DRAWFLAG_MODULATE)
1112 GL_DepthMask(false);
1113 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1115 else if(flags == DRAWFLAG_2XMODULATE)
1117 GL_DepthMask(false);
1118 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1120 else if(flags == DRAWFLAG_SCREEN)
1122 GL_DepthMask(false);
1123 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1127 GL_DepthMask(false);
1128 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1133 GL_BlendFunc(GL_ONE, GL_ZERO);
1137 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1141 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1142 if(!r_draw2d.integer && !r_draw2d_force)
1145 // R_Mesh_ResetTextureState();
1146 floats[12] = 0.0f;floats[13] = 0.0f;
1147 floats[14] = 1.0f;floats[15] = 0.0f;
1148 floats[16] = 1.0f;floats[17] = 1.0f;
1149 floats[18] = 0.0f;floats[19] = 1.0f;
1150 floats[20] = floats[24] = floats[28] = floats[32] = red;
1151 floats[21] = floats[25] = floats[29] = floats[33] = green;
1152 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1153 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1159 height = pic->height;
1160 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1163 // AK07: lets be texel correct on the corners
1165 float horz_offset = 0.5f / pic->width;
1166 float vert_offset = 0.5f / pic->height;
1168 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1169 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1170 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1171 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1176 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1178 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1179 floats[0] = floats[9] = x;
1180 floats[1] = floats[4] = y;
1181 floats[3] = floats[6] = x + width;
1182 floats[7] = floats[10] = y + height;
1184 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1185 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1188 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)
1191 float af = DEG2RAD(-angle); // forward
1192 float ar = DEG2RAD(-angle + 90); // right
1193 float sinaf = sin(af);
1194 float cosaf = cos(af);
1195 float sinar = sin(ar);
1196 float cosar = cos(ar);
1198 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1199 if(!r_draw2d.integer && !r_draw2d_force)
1202 // R_Mesh_ResetTextureState();
1208 height = pic->height;
1209 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1212 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1214 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1217 floats[0] = x - cosaf*org_x - cosar*org_y;
1218 floats[1] = y - sinaf*org_x - sinar*org_y;
1221 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1222 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1225 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1226 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1229 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1230 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1232 floats[12] = 0.0f;floats[13] = 0.0f;
1233 floats[14] = 1.0f;floats[15] = 0.0f;
1234 floats[16] = 1.0f;floats[17] = 1.0f;
1235 floats[18] = 0.0f;floats[19] = 1.0f;
1236 floats[20] = floats[24] = floats[28] = floats[32] = red;
1237 floats[21] = floats[25] = floats[29] = floats[33] = green;
1238 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1239 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1241 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1242 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1245 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1249 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1250 if(!r_draw2d.integer && !r_draw2d_force)
1253 // R_Mesh_ResetTextureState();
1254 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1256 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1257 floats[0] = floats[9] = x;
1258 floats[1] = floats[4] = y;
1259 floats[3] = floats[6] = x + width;
1260 floats[7] = floats[10] = y + height;
1261 floats[12] = 0.0f;floats[13] = 0.0f;
1262 floats[14] = 1.0f;floats[15] = 0.0f;
1263 floats[16] = 1.0f;floats[17] = 1.0f;
1264 floats[18] = 0.0f;floats[19] = 1.0f;
1265 floats[20] = floats[24] = floats[28] = floats[32] = red;
1266 floats[21] = floats[25] = floats[29] = floats[33] = green;
1267 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1268 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1270 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1271 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1274 /// color tag printing
1275 static const vec4_t string_colors[] =
1278 // LordHavoc: why on earth is cyan before magenta in Quake3?
1279 // LordHavoc: note: Doom3 uses white for [0] and [7]
1280 {0.0, 0.0, 0.0, 1.0}, // black
1281 {1.0, 0.0, 0.0, 1.0}, // red
1282 {0.0, 1.0, 0.0, 1.0}, // green
1283 {1.0, 1.0, 0.0, 1.0}, // yellow
1284 {0.0, 0.0, 1.0, 1.0}, // blue
1285 {0.0, 1.0, 1.0, 1.0}, // cyan
1286 {1.0, 0.0, 1.0, 1.0}, // magenta
1287 {1.0, 1.0, 1.0, 1.0}, // white
1288 // [515]'s BX_COLOREDTEXT extension
1289 {1.0, 1.0, 1.0, 0.5}, // half transparent
1290 {0.5, 0.5, 0.5, 1.0} // half brightness
1291 // Black's color table
1292 //{1.0, 1.0, 1.0, 1.0},
1293 //{1.0, 0.0, 0.0, 1.0},
1294 //{0.0, 1.0, 0.0, 1.0},
1295 //{0.0, 0.0, 1.0, 1.0},
1296 //{1.0, 1.0, 0.0, 1.0},
1297 //{0.0, 1.0, 1.0, 1.0},
1298 //{1.0, 0.0, 1.0, 1.0},
1299 //{0.1, 0.1, 0.1, 1.0}
1302 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1304 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1306 float C = r_textcontrast.value;
1307 float B = r_textbrightness.value;
1308 if (colorindex & 0x10000) // that bit means RGB color
1310 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1311 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1312 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1313 color[3] = (colorindex & 0xf) / 15.0;
1316 Vector4Copy(string_colors[colorindex], color);
1317 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1320 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1321 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1325 // NOTE: this function always draws exactly one character if maxwidth <= 0
1326 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)
1328 const char *text_start = text;
1329 int colorindex = STRING_COLOR_DEFAULT;
1332 Uchar ch, mapch, nextch;
1333 Uchar prevch = 0; // used for kerning
1338 ft2_font_map_t *fontmap = NULL;
1339 ft2_font_map_t *map = NULL;
1340 //ft2_font_map_t *prevmap = NULL;
1341 ft2_font_t *ft2 = fnt->ft2;
1343 qboolean snap = true;
1344 qboolean least_one = false;
1345 float dw; // display w
1346 //float dh; // display h
1347 const float *width_of;
1354 // do this in the end
1355 w *= fnt->settings.scale;
1356 h *= fnt->settings.scale;
1358 // find the most fitting size:
1362 map_index = Font_IndexForSize(ft2, h, &w, &h);
1364 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1365 fontmap = Font_MapForIndex(ft2, map_index);
1374 if (!outcolor || *outcolor == -1)
1375 colorindex = STRING_COLOR_DEFAULT;
1377 colorindex = *outcolor;
1379 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1380 // ftbase_x = snap_to_pixel_x(0);
1385 maxwidth = -maxwidth;
1389 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1392 width_of = fontmap->width_of;
1394 width_of = fnt->width_of;
1396 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1399 nextch = ch = u8_getnchar(text, &text, bytes_left);
1400 i = text - text_start;
1403 if (ch == ' ' && !fontmap)
1405 if(!least_one || i0) // never skip the first character
1406 if(x + width_of[(int) ' '] * dw > maxwidth)
1409 break; // oops, can't draw this
1411 x += width_of[(int) ' '] * dw;
1414 // i points to the char after ^
1415 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1417 ch = *text; // colors are ascii, so no u8_ needed
1418 if (ch <= '9' && ch >= '0') // ^[0-9] found
1420 colorindex = ch - '0';
1425 // i points to the char after ^...
1426 // i+3 points to 3 in ^x123
1427 // i+3 == *maxlen would mean that char is missing
1428 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1430 // building colorindex...
1431 ch = tolower(text[1]);
1432 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1433 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1434 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1435 else tempcolorindex = 0;
1438 ch = tolower(text[2]);
1439 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1440 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1441 else tempcolorindex = 0;
1444 ch = tolower(text[3]);
1445 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1446 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1447 else tempcolorindex = 0;
1450 colorindex = tempcolorindex | 0xf;
1451 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1459 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1468 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1475 map = ft2_oldstyle_map;
1477 if(!least_one || i0) // never skip the first character
1478 if(x + width_of[ch] * dw > maxwidth)
1481 break; // oops, can't draw this
1483 x += width_of[ch] * dw;
1485 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1487 map = FontMap_FindForChar(fontmap, ch);
1490 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1496 mapch = ch - map->start;
1497 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1499 x += map->glyphs[mapch].advance_x * dw;
1508 *outcolor = colorindex;
1513 float DrawQ_Color[4];
1514 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)
1516 int shadow, colorindex = STRING_COLOR_DEFAULT;
1518 float x = startx, y, s, t, u, v, thisw;
1519 float *av, *at, *ac;
1521 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1522 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1523 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1524 Uchar ch, mapch, nextch;
1525 Uchar prevch = 0; // used for kerning
1528 //ft2_font_map_t *prevmap = NULL; // the previous map
1529 ft2_font_map_t *map = NULL; // the currently used map
1530 ft2_font_map_t *fontmap = NULL; // the font map for the size
1532 const char *text_start = text;
1534 ft2_font_t *ft2 = fnt->ft2;
1535 qboolean snap = true;
1539 const float *width_of;
1542 tw = R_TextureWidth(fnt->tex);
1543 th = R_TextureHeight(fnt->tex);
1551 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1552 w *= fnt->settings.scale;
1553 h *= fnt->settings.scale;
1558 map_index = Font_IndexForSize(ft2, h, &w, &h);
1560 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1561 fontmap = Font_MapForIndex(ft2, map_index);
1567 // draw the font at its baseline when using freetype
1569 ftbase_y = dh * (4.5/6.0);
1574 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1575 if(!r_draw2d.integer && !r_draw2d_force)
1576 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1578 // R_Mesh_ResetTextureState();
1580 R_Mesh_TexBind(0, fnt->tex);
1581 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1588 //ftbase_x = snap_to_pixel_x(ftbase_x);
1591 startx = snap_to_pixel_x(startx, 0.4);
1592 starty = snap_to_pixel_y(starty, 0.4);
1593 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1596 pix_x = vid.width / vid_conwidth.value;
1597 pix_y = vid.height / vid_conheight.value;
1600 width_of = fontmap->width_of;
1602 width_of = fnt->width_of;
1604 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1609 if (!outcolor || *outcolor == -1)
1610 colorindex = STRING_COLOR_DEFAULT;
1612 colorindex = *outcolor;
1614 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1621 x += r_textshadow.value * vid.width / vid_conwidth.value;
1622 y += r_textshadow.value * vid.height / vid_conheight.value;
1625 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1627 nextch = ch = u8_getnchar(text, &text, bytes_left);
1628 i = text - text_start;
1631 if (ch == ' ' && !fontmap)
1633 x += width_of[(int) ' '] * dw;
1636 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1638 ch = *text; // colors are ascii, so no u8_ needed
1639 if (ch <= '9' && ch >= '0') // ^[0-9] found
1641 colorindex = ch - '0';
1642 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1647 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1649 // building colorindex...
1650 ch = tolower(text[1]);
1651 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1652 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1653 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1654 else tempcolorindex = 0;
1657 ch = tolower(text[2]);
1658 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1659 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1660 else tempcolorindex = 0;
1663 ch = tolower(text[3]);
1664 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1665 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1666 else tempcolorindex = 0;
1669 colorindex = tempcolorindex | 0xf;
1670 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1671 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1672 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1680 else if (ch == STRING_COLOR_TAG)
1689 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1690 // this way we don't need to rebind fnt->tex for every old-style character
1691 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1694 x += 1.0/pix_x * r_textshadow.value;
1695 y += 1.0/pix_y * r_textshadow.value;
1697 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1705 if (map != ft2_oldstyle_map)
1709 // switching from freetype to non-freetype rendering
1710 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1711 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1717 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1718 map = ft2_oldstyle_map;
1722 //num = (unsigned char) text[i];
1723 //thisw = fnt->width_of[num];
1724 thisw = fnt->width_of[ch];
1725 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1726 s = (ch & 15)*0.0625f + (0.5f / tw);
1727 t = (ch >> 4)*0.0625f + (0.5f / th);
1728 u = 0.0625f * thisw - (1.0f / tw);
1729 v = 0.0625f - (1.0f / th);
1730 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1731 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1732 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1733 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1734 at[ 0] = s ; at[ 1] = t ;
1735 at[ 2] = s+u ; at[ 3] = t ;
1736 at[ 4] = s+u ; at[ 5] = t+v ;
1737 at[ 6] = s ; at[ 7] = t+v ;
1738 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1739 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1740 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1741 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1746 if (batchcount >= QUADELEMENTS_MAXQUADS)
1748 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1749 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1755 x += width_of[ch] * dw;
1757 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1759 // new charmap - need to render
1762 // we need a different character map, render what we currently have:
1763 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1764 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1771 map = FontMap_FindForChar(fontmap, ch);
1774 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1781 // this shouldn't happen
1786 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1789 mapch = ch - map->start;
1790 thisw = map->glyphs[mapch].advance_x;
1794 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1801 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1802 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1803 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1804 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1805 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1806 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1807 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1808 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1809 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1810 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1811 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1812 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1821 if (batchcount >= QUADELEMENTS_MAXQUADS)
1823 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1824 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1837 x -= 1.0/pix_x * r_textshadow.value;
1838 y -= 1.0/pix_y * r_textshadow.value;
1844 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1845 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1849 *outcolor = colorindex;
1851 // note: this relies on the proper text (not shadow) being drawn last
1855 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)
1857 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1860 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)
1862 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1865 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1867 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1870 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1872 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1877 // no ^xrgb management
1878 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1880 int color, numchars = 0;
1881 char *outputend2c = output2c + maxoutchars - 2;
1882 if (!outcolor || *outcolor == -1)
1883 color = STRING_COLOR_DEFAULT;
1887 maxreadchars = 1<<30;
1888 textend = text + maxreadchars;
1889 while (text != textend && *text)
1891 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1893 if (text[1] == STRING_COLOR_TAG)
1895 else if (text[1] >= '0' && text[1] <= '9')
1897 color = text[1] - '0';
1902 if (output2c >= outputend2c)
1904 *output2c++ = *text++;
1905 *output2c++ = color;
1908 output2c[0] = output2c[1] = 0;
1915 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)
1919 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1920 if(!r_draw2d.integer && !r_draw2d_force)
1923 // R_Mesh_ResetTextureState();
1929 height = pic->height;
1930 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1933 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1935 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1936 floats[0] = floats[9] = x;
1937 floats[1] = floats[4] = y;
1938 floats[3] = floats[6] = x + width;
1939 floats[7] = floats[10] = y + height;
1940 floats[12] = s1;floats[13] = t1;
1941 floats[14] = s2;floats[15] = t2;
1942 floats[16] = s4;floats[17] = t4;
1943 floats[18] = s3;floats[19] = t3;
1944 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1945 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1946 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1947 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1949 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1950 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1953 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1957 if(!r_draw2d.integer && !r_draw2d_force)
1959 DrawQ_ProcessDrawFlag(flags, hasalpha);
1961 // R_Mesh_ResetTextureState();
1962 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1964 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1965 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1968 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1972 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1973 if(!r_draw2d.integer && !r_draw2d_force)
1977 switch(vid.renderpath)
1979 case RENDERPATH_GL11:
1980 case RENDERPATH_GL13:
1981 case RENDERPATH_GL20:
1984 qglBegin(GL_LINE_LOOP);
1985 for (num = 0;num < mesh->num_vertices;num++)
1987 if (mesh->data_color4f)
1988 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]);
1989 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1995 case RENDERPATH_D3D9:
1996 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1998 case RENDERPATH_D3D10:
1999 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2001 case RENDERPATH_D3D11:
2002 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2004 case RENDERPATH_SOFT:
2005 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2007 case RENDERPATH_GLES1:
2008 case RENDERPATH_GLES2:
2009 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2014 //[515]: this is old, delete
2015 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2017 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2018 if(!r_draw2d.integer && !r_draw2d_force)
2021 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2023 switch(vid.renderpath)
2025 case RENDERPATH_GL11:
2026 case RENDERPATH_GL13:
2027 case RENDERPATH_GL20:
2031 //qglLineWidth(width);CHECKGLERROR
2033 GL_Color(r,g,b,alpha);
2036 qglVertex2f(x1, y1);
2037 qglVertex2f(x2, y2);
2042 case RENDERPATH_D3D9:
2043 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2045 case RENDERPATH_D3D10:
2046 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2048 case RENDERPATH_D3D11:
2049 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2051 case RENDERPATH_SOFT:
2052 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2054 case RENDERPATH_GLES1:
2055 case RENDERPATH_GLES2:
2056 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2061 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2064 qboolean hasalpha = false;
2065 for (i = 0;i < numlines*2;i++)
2066 if (color4f[i*4+3] < 1.0f)
2069 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2071 if(!r_draw2d.integer && !r_draw2d_force)
2074 switch(vid.renderpath)
2076 case RENDERPATH_GL11:
2077 case RENDERPATH_GL13:
2078 case RENDERPATH_GL20:
2081 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2083 //qglLineWidth(width);CHECKGLERROR
2086 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2087 qglDrawArrays(GL_LINES, 0, numlines*2);
2090 case RENDERPATH_D3D9:
2091 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2093 case RENDERPATH_D3D10:
2094 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2096 case RENDERPATH_D3D11:
2097 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2099 case RENDERPATH_SOFT:
2100 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2102 case RENDERPATH_GLES1:
2103 case RENDERPATH_GLES2:
2104 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2109 void DrawQ_SetClipArea(float x, float y, float width, float height)
2114 // We have to convert the con coords into real coords
2115 // OGL uses top to bottom
2116 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2117 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2118 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2119 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2120 switch(vid.renderpath)
2122 case RENDERPATH_GL11:
2123 case RENDERPATH_GL13:
2124 case RENDERPATH_GL20:
2125 case RENDERPATH_GLES1:
2126 case RENDERPATH_GLES2:
2127 case RENDERPATH_SOFT:
2128 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2130 case RENDERPATH_D3D9:
2131 GL_Scissor(ix, iy, iw, ih);
2133 case RENDERPATH_D3D10:
2134 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2136 case RENDERPATH_D3D11:
2137 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2141 GL_ScissorTest(true);
2144 void DrawQ_ResetClipArea(void)
2147 GL_ScissorTest(false);
2150 void DrawQ_Finish(void)
2152 r_refdef.draw2dstage = 0;
2155 void DrawQ_RecalcView(void)
2157 if(r_refdef.draw2dstage)
2158 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2161 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2162 void R_DrawGamma(void)
2165 switch(vid.renderpath)
2167 case RENDERPATH_GL20:
2168 case RENDERPATH_D3D9:
2169 case RENDERPATH_D3D10:
2170 case RENDERPATH_D3D11:
2171 case RENDERPATH_GLES2:
2172 if (vid_usinghwgamma || v_glslgamma.integer)
2175 case RENDERPATH_GL11:
2176 case RENDERPATH_GL13:
2177 if (vid_usinghwgamma)
2180 case RENDERPATH_GLES1:
2181 case RENDERPATH_SOFT:
2184 // all the blends ignore depth
2185 // R_Mesh_ResetTextureState();
2186 R_SetupShader_Generic_NoTexture(true, true);
2188 GL_DepthRange(0, 1);
2189 GL_PolygonOffset(0, 0);
2190 GL_DepthTest(false);
2192 // interpretation of brightness and contrast:
2193 // color range := brightness .. (brightness + contrast)
2194 // i.e. "c *= contrast; c += brightness"
2195 // plausible values for brightness thus range from -contrast to 1
2197 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2198 if (vid.support.ext_blend_subtract)
2200 if (v_color_enable.integer)
2202 c[0] = -v_color_black_r.value / v_color_white_r.value;
2203 c[1] = -v_color_black_g.value / v_color_white_g.value;
2204 c[2] = -v_color_black_b.value / v_color_white_b.value;
2207 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2208 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2210 // need SUBTRACTIVE blending to do this!
2211 GL_BlendEquationSubtract(true);
2212 GL_BlendFunc(GL_ONE, GL_ONE);
2213 GL_Color(c[0], c[1], c[2], 1);
2214 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2215 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2216 GL_BlendEquationSubtract(false);
2221 if (v_color_enable.integer)
2223 c[0] = v_color_white_r.value;
2224 c[1] = v_color_white_g.value;
2225 c[2] = v_color_white_b.value;
2228 c[0] = c[1] = c[2] = v_contrast.value;
2229 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2231 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2232 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2235 cc[0] = bound(0, c[0] - 1, 1);
2236 cc[1] = bound(0, c[1] - 1, 1);
2237 cc[2] = bound(0, c[2] - 1, 1);
2238 GL_Color(cc[0], cc[1], cc[2], 1);
2239 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2240 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2246 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2248 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2249 GL_Color(c[0], c[1], c[2], 1);
2250 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2251 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2254 // apply post-brightness (additive brightness, for where contrast was <= 1)
2255 if (v_color_enable.integer)
2257 c[0] = v_color_black_r.value;
2258 c[1] = v_color_black_g.value;
2259 c[2] = v_color_black_b.value;
2262 c[0] = c[1] = c[2] = v_brightness.value;
2263 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2265 GL_BlendFunc(GL_ONE, GL_ONE);
2266 GL_Color(c[0], c[1], c[2], 1);
2267 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2268 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);